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

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. HTTP POST 请求的两种编码格式:application/x-www-form-urlencoded 和 multipart/form-data

    在常见业务开发中,POST 请求常常在这些地方使用:前端表单提交时.调用接口代码时和使用 Postman 测试接口时.我们下面来一一了解: 一.前端表单提交时 application/x-www-fo ...

  2. GitLab 系列文章

    GitLab 系列文章 记录 GitLab 的相关文章 列表 Docker 搭建 GitLab GitLab CI/CD 配置 GitLab 配置模板 访问 GitLab 数据库 GitLab 转让所 ...

  3. Java自学-图形界面 事件监听

    Swing 如何进行事件监听 示例 1 : 按钮监听 创建一个匿名类实现ActionListener接口,当按钮被点击时,actionPerformed方法就会被调用 package gui; imp ...

  4. 解决CocoaPods could not find compatible versions for pod "React/Core"

    react-native框架中,在ios文件夹下执行pod install命令时出现的问题. 下面时完整的异常信息: [!] CocoaPods could not find compatible v ...

  5. 【Mysql】SpringBoot阿里Druid数据源连接池配置

    一.pom.xml添加 <!-- 配置数据库连接池 --> <dependency> <groupId>com.alibaba</groupId> &l ...

  6. C#LeetCode刷题-图

    图篇 # 题名 刷题 通过率 难度 133 克隆图   18.7% 中等 207 课程表   40.0% 中等 210 课程表 II   40.0% 中等 310 最小高度树   29.5% 中等 3 ...

  7. Flutter 容器Container类和布局Layout类

    1.布局和容器 [布局]是把[容器]按照不同的方式排列起来. Scaffold包含的主要部门:appBar,body,bottomNavigator 其中body可以是一个布局组件,也可以是一个容器组 ...

  8. Linux命令--vi/vim复制一行,粘贴

    在光标的位置按“yy”,复制当前行: 然后再光标的行按“p”,粘贴到下一行,原来的往下顺移

  9. 编译原理根据项目集规范族构造LR(0)分析表

    转载于https://blog.csdn.net/Johan_Joe_King/article/details/79058597?utm_medium=distribute.pc_relevant.n ...

  10. Javascript模块化编程(二):AMD规范 (转)

    原文作者: 阮一峰 日期: 2012年10月30日 这个系列的第一部分介绍了Javascript模块的基本写法,今天介绍如何规范地使用模块. (接上文) 七.模块的规范 先想一想,为什么模块很重要? ...