这个是因为自己被自己蠢哭了动笔的,里面大概记录自己所犯的错,和一些小知识。

1.有一个错误我经常犯:内部定义的字段没对应开放到编辑器的字段。这个是由于我有点依赖ide写代码的习惯导致,而shader的ide只提供了基本的关键字高亮。

  纠正方法:对一个字段,一直使用拷贝粘贴的方式进行书写。

  cg教程给了一个写cg代码的tip:

  

2.法线没归一化正确,导致效果奇奇怪怪,这个我是在写matcap那里犯的错:漏了这句:

o.twoUv.zw = o.twoUv.zw * 0.5 + 0.5;//(-1,1)->(0,1)

3.Tiling和Offset:

  Tiling:缩放模型UV纹理采样坐标,比如Tiling = 2,表示把模型UV坐标的U扩大2倍,U范围变成了0-2

    [此时模型U=[0,1]就采用了整个纹理,即模型的左半使用整个纹理;模型U = (1,2]的采样值使用纹理溢出填充值,看下面]

  Offset:偏移模型UV纹理采用坐标;比如Offset x=0.1表示把UV纹理坐标往左偏移,然后再采样,
  至于超出UV纹理的UV坐标采样的颜色值(纹理溢出填充值),则依赖UV纹理的WrapMode
  具体实现:
  float4 _MainTex_ST;//纹理缩放偏移向量(Unity默认此变量赋值,变量命名规则:纹理名_ST)
  vert()
  {
  ...
  //第一种方式:
  o.uv = v.vertex.xy * _MainTex_ST.xy + _MainTex_ST.zw;
  //第二种方式:使用内建宏,这只是对第一种的封装
  o.uv = TRANSFORM_TEX(v.vertex,_MainTex);
  }

4.shader的几个优化tips:

  1.gpu是并行运算,即运算一个float和运算float4是一样代价的,所以:

    float4 color;

    color.rgb = color.rgb*a;

    color.a = color.a*b;

    可以改成:float4 color; color = color*float4(a,a,a,b)

  2.少用if else,用step+乘法代替:

    

     step的定义图

     如:

    if (a >= b)
    {
    c = ;
    }
    else
    {  
    c = ;
    }
    //可以用下面的代替
  tmp = step(b,a)
  c = tmp + *(-tmp);

    这里讲一下步骤:

    第一步,参照step的定义图,我们需要先想方设法把if内的内容搞成 a >= b或者a <= b,然后分析一下if else里面所求内容,写成可以根据step得到的0或1进行计算的表达式,因为shader的判断逻辑一般比较简单,所以这一步不会特别难,然后就ok了,下面让我们来做一下:

    改写:

    if(a && b)

     {   c = 1;  }

    else{c = 2;}

    a&&b可以用乘法来代替,即if (a*b <=0)[这里也不严谨,如果a,b是负数就不对了,但很少会是负数],所以我们对比step的定义图可以得到:

     tmp = step(a*b,0);

     c = tmp + 2 * (1-tmp);

  3.如果能用fixed(-2,2)就用fixed,不然用half,最后才考虑用float,是几倍的性能之差。

  4.使用纹理来编码函数,即控制贴图,可以用很小的贴图然后通过插值获得大范围的数据,这些都是GPU硬件支持的,节省了GPU处理周期。

  5.只渲染必须着色的片段。比如可以预先打开深度测试,然后再对经过测试的片段进行fragment shader指令。

5.

  • float4 _Time : Time (t/20, t, t*2, t*3), use to animate things inside the shaders

    

6.记一次svn从主干merge .unity场景文件到分支的坑:

1.必须文件级别merge,不能文件夹级别merge,也就是说merget from的目录要详细到该文件

2.merge后必须在分支打开该.unity文件,看是否和主干一致,我发现修改一些obj的static属性没有同步过去

总结:.unity文件不知具有何特殊性,svn merge时需要谨慎对待,这次问题出乎我意料,严重影响了出包时间。

7.GrabPass捕捉屏幕纹理

Shader "Custom/GrabVF" {
Properties {
//_MainTex ("Base (RGB)", 2D) = "white" {}
}
SubShader {
// 在所有不透明对象之后绘制自己,更加靠近屏幕
Tags{"Queue"="Transparent"}
// 通道1:捕捉对象之后的屏幕内容放到_GrabTexture纹理中
GrabPass{}
// 通道2:设置材质
Pass{
Name "pass2"
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _GrabTexture;
float4 _GrabTexture_ST;
struct v2f
  {
float4 pos : POSITION; // 输入的模型空间中,顶点坐标信息
float4 uv : TEXCOORD0; // 材质信息也包含了xyzw,通常只用xy,但是这里由顶点生成
};
v2f vert (appdata_base v)
{
v2f o;
// 从模型坐标-世界坐标-视坐标-(视觉平截体乘以投影矩阵并进行透视除法)-剪裁坐标
o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
//o.uv = TRANSFORM_TEX(v.texcoord, _GrabTexture);// UV纹理坐标集信息来自屏幕样本对象,如果用这个uv采样,就把全屏的纹理显示到当前物体上了。
float4 screenUV = ComputeGrabScreenPos(o.pos);//计算该模型顶点在屏幕坐标的纹理信息,,_GrabTexture得到的是全屏纹理,要根据当前模型所在位置进行采样,显示的是物体背后的屏幕纹理而不是全屏,这个函数输入的是在[-w,w]立方体中的坐标,输出的是[0,w]立方体中的坐标,所以下面还要/w
o.uv = screenUV.xy/screenUV.w;
return o;
}
float4 frag (v2f i) : COLOR
{
// 对_GrabTexture纹理进行取样,进行2D纹理映射查找
half4 texCol = tex2D(_GrabTexture, i.uv);
// 颜色反相,便于观察效果
return - texCol;
}
ENDCG
}
}
FallBack "Diffuse"
}

  当然也可以把相机rtt到一个tex中,然后把tex设给物体的shader,这样也能获得屏幕纹理了。

8.语义可以在结构里修饰变量,也可以直接修饰变量

struct app_data
{
float4 pos: POSITION;
}
void vert(app_data v){}
void vert(float4 pos:POSITION){}

语义是一种黏合剂,它把流水线各个阶段的数据连接起来,它指明了数据对应的硬件资源,只有入口函数(顶点函数/片段函数)才使用语义,内部函数(库or自定义的)不能使用。

但同一个语义在不同阶段(输入、输出、顶点、片段)不是一样的,它只是连接“不同阶段”,比如应用程序的输出和顶点函数的输入,顶点函数的输出和片段函数的输入,使得后者可以去相应硬件取得前者的值并生产自己的值,比如片段函数的输入的POSITION其实是顶点函数输出的POSITION插值后的数值,并不一致。

9.swizzle重组操作符是一个圆点.:

float4 a;

a.z;a.wz;等,这个操作符效率很高,被硬件支持。

矩阵重组:

  float a; float3 b;

  float4x4 matrix;

  a = matrix._m32;b = matrix._m32_m00_m11;也可以数组提取b = matrix[0].xyz;

写入掩码:float4 a = (0,0,0,00;float2 b = (1,1); a.xz = b;这里不能写成a.zz,即写入掩码不能重复。

10.可以用使用“out”、“inout”、“in”等进行输入输出标识,无限制是默认是in, out可以用来输出更多变量,但感觉这个用处不大,毕竟使用结构体就能够输出多个。

11.,即较老的机器可能出乎意料。

12.基本的片段profile(即早期)只能用一个给定的纹理坐标集存取它对应的纹理,即一个纹理一次只能读取采样一次,下面是不支持的:

  float4 col1 = tex2D(_tex, uv1);

  float4 col2 = tex2D(_tex, uv2);

  需要搞成:tex2D(_tex, uv1);tex2D(_tex1, uv2);其中_tex和_tex1绑定同一纹理

13.diffuse = Kd * Max(dot(N, L), 0);

  specular = Ks * (Max(dot(N, H), 0)^shininess;其中H 是规范化V和L的半角向量:H = normalize(V+L);

14.顶点shader 访问的光照向量,视线向量,是物体空间吗?应该是了,毕竟Unity的光照函数里,直接用之和模型的法线进行计算了,而那个法线是物体空间的。

15.有人在知乎问如何评测一个shader的性能,我摘取了叶劲峰老师的答案片段:

  游戏过程有很多变数,例如渲染1个接近镜头的NPC和渲染10个远离镜头的NPC,其性能分别难以预测。瓶颈经常会改变。

  有一些shader是较容易评测的,例如,全屏后期处理的瓶颈在于pixel shader、纹理采样和带宽,其运行复杂度与屏幕分辨率成正比。

  对于其他shader,最简单的评测方式是,观察shader源代码编译成汇编之后的一般指令及纹理采样指令数目。这是一个非常粗糙的评测方式,但可以用于作一些简单的统计,找出那些可能有问题的shader。这适合像UE给美术随意创建shader的情况。

  但在游戏开发中,我看到一般的做法是,以执行游戏来作整体评测及优化,而不是单独评测各个部分。一方面是因为游戏有很多变数,另一方面是因为人力成本。所以通常会做一些自动评测整体性能的测试,例如让Bot在场景中行走,记录整体的帧率、CPU/GPU时间、draw-call等。如自动测试程序发现超出预期的数值,就发电邮通知团队。这种测试每天自动执行,可画出按天数的性能图表,知道开发及优化的整体情况。

  这种做法大概也可以推广到其他方面的自动化监控。

16.把对各个顶点数值不同的计算放shader里,把对各个顶点数值相同的变量放cpu里,然后传给shader

17.许多实现可以在顶程序点实现,也可以在片段程序实现,一般地,在后者实现能提高效果,但前者实现能提高性能,要斟酌。另外,如果该变量在顶点间是线性变化的,应该在顶点shader里计算,或者变化不快(如漫反射系数),可以在顶点shader里计算,如果变化很快(如高亮系数),应该在片段shader里计算,实践见真知。

写shader小细节——这个会不断更新的更多相关文章

  1. Oracle Sales Cloud:管理沙盒(定制化)小细节2——使用对象触发器更新数字字段

    在上一篇 "管理沙盒(定制化)小细节1" 的随笔中,我们使用公式法在 "业务机会" 对象(单头)上建立了 "利润合计" 字段,并将它等于 & ...

  2. [小细节,大BUG]记录一些小问题引起的大BUG(长期更新....)

    [小细节,大BUG] 6.问题描述:当从Plist文件加载数据,放入到tableView中展示时,有时有数据,有时又没有数据.这是为什么呢?相信很多大牛都想到了:我们一般将加载的数据,转换成模型,放入 ...

  3. 【小知识+小细节】不断更新ing...

    1.printf printf("%.0lf",k) 输出的不是floor(k) 而是k四舍五入 ..才发现.xlf 都是四舍五入取x位 2.cin char buff[300] ...

  4. Oracle Sales Cloud:管理沙盒(定制化)小细节1——利用公式创建字段并显示在前端页面

    Oracle Sales Cloud(Oracle 销售云)是一套基于Oracle云端的CRM管理系统.由于 Oracle 销售云是基于 Oracle 云环境的,它与传统的管理系统相比,显著特点之一便 ...

  5. Oracle Sales Cloud:报告和分析(BIEE)小细节1——创建双提示并建立关联(例如,部门和子部门提示)

    Oracle Sales Cloud(Oracle 销售云)是一套基于Oracle云端的客户商机管理系统,通过提供丰富的功能来帮助提高销售效率,更好地去了解客户,发现和追踪商机,为最终的销售成交 (d ...

  6. C++在使用Qt中SLOT宏须要注意的一个小细节

    大家都知道C++虚函数的机制,对于基类定义为虚函数的地方,子类假设覆写,在基类指针或者引用来指向子类的时候会实现动态绑定. 但假设指针去调用非虚函数,这个时候会调用C++的静态绑定,去推断当前的指针是 ...

  7. C++在使用Qt中SLOT宏需要注意的一个小细节

    大家都知道C++虚函数的机制,对于基类定义为虚函数的地方,子类如果覆写,在基类指针或者引用来指向子类的时候会实现动态绑定. 但如果指针去调用非虚函数,这个时候会调用C++的静态绑定,去判断当前的指针是 ...

  8. css小细节罗列

    有空时候把一些常见可能不是每个人都知道的css小细节总结了下,共勉. 1.line-height 众多周知,line-height是行高的意思,我们时常会使用类似line-height:24px;这样 ...

  9. 像VUE一样写微信小程序-深入研究wepy框架

    像VUE一样写微信小程序-深入研究wepy框架 微信小程序自发布到如今已经有半年多的时间了,凭借微信平台的强大影响力,越来越多企业加入小程序开发. 小程序于M页比相比,有以下优势: 1.小程序拥有更多 ...

随机推荐

  1. 2017面向对象程序设计(Java)第十三周助教工作总结

    时间飞逝,本学期即将接近尾声.不知不觉同学们已经学习java十三个星期了,想必同学们对Java课程体系及内容有了更加深入的理解,也应该摸索出了一套适用于自己的学习方法. 下面我对上周工作做以简单总结: ...

  2. 001_HyperLedger Fabric环境安装

    HyperLedger Fabric的环境,有解决三大问题 第一,是系统环境,这里我们选择的是centos7 第二,是开发环境,这里我们选择的是Go语言 第三,是运行环境,这里我们选择的是Docker ...

  3. Django 环境下常用的模型设计

    Django 环境下常用的模型设计 用户表 继承 django.contrib.auth.model import AbstractUser AbstractUser 默认已经包含了很多字段了 id ...

  4. 12、Java 正则表达式

    简介 用来描述或者匹配一系列符合某个语句规则的字符串 正则表达式定义了字符串的模式. 正则表达式可以用来搜索.编辑或处理文本. 正则表达式并不仅限于某一种语言,但是在每种语言中有细微的差别. 一.正则 ...

  5. 微信公众号里面怎么添加xls

    微信公众号里面添加xls的教程 我们都知道创建一个微信公众号,在公众号中发布一些文章是非常简单的,但公众号添加附件下载的功能却被限制,如今可以使用小程序“微附件”进行在公众号中添加附件. 以下是公众号 ...

  6. UIAutomator环境Android8.0 环境异常解决

    个人PC环境 ANDROID_HOME:F:\1Study\Andriod\51zxw_2018-0102\Sdk ANT_HOME:D:\ant\apache-ant-1.10.5\ CLASSPA ...

  7. Homekit_二路继电器

    介绍一款二路继电器,使用Homekit进行控制,有兴趣的可以去以下链接看看: https://item.taobao.com/item.htm?spm=a1z10.1-c.w4004-11265006 ...

  8. 地图绘制之basemap第一弹 basemap选择与安装

    作为一个测绘GIS专业的学生,会有很多绘制地图的需求,一般情况使用ArcGIS.QGIS就可以解决,但是在绘制如论文插图时需要使用更加专业可定制化程度更高的工具,专业传统一般使用GMT,几经比较,最终 ...

  9. javaWeb项目之图书管理系统(附视频讲解)

    视频播放地址:javaWeb图书系统 本系统为"Swing项目之图书管理系统"(此源码已共享)的Web版,网页框架用采用EasyUI 数据库为MysqL,写Web项目摒弃了火狐浏览 ...

  10. 5.oracle用户管理

    一.创建用户概述:在oracle中要创建一个新的用户使用create user语句,一般是具有dba(数据库管理员)的权限才能使用.create user 用户名 identified by 密码;  ...