Unity Shader 学习笔记

一些基础知识点介绍

  1. 数据类型

    • double(双精度类型),一般64bit
    • float(浮点数类型),一般是32bit,如float2, float3, float4
    • half(半精度类型),一般是16bit。用于几何位置或纹理坐标。
    • fixed(定点数类型),一般是11bit。精度更低。一般用于颜色。
    • vec(向量值类型), 如vec2, vec3, vec4
  2. 常见方法

    • tex2D(sampler2D, TEXCOORD0)
    • length(float2/float3/float4) 求向量模
    • atan -> arctan
    • asin -> arcsin
    • saturate(x)。如果x取值小于0,则返回值为0;如果x取值大于1,则返回值为1;若x在0到1之间,则直接返回x的值。
    • discard 丢弃片段。例如uv的x/y坐标满足某个条件时丢弃对应的片段,那么这些丢弃的片段都不绘制了。但是discard是一条消耗比较大的指令,因为只要有个一包含discard指令的着色器,就可能会导致某些重要的优化失效,渲染可能会执行的更差,
    • step(a, x)。Returns (x >= a) ? 1 : 0
  3. 常见结构体

    • SurfaceOutput,表面着色器输出结构体
      1
      2
      3
      4
      5
      6
      7
      8
      struct SurfaceOutput {
      half3 Albedo; //该像素的颜色值
      half3 Normal; //该像素的法向量
      half3 Emission; //该像素的辐射光,辐射光是最简单的一种光,它直接从物体发出并且不受任何光源影响
      half Specular; //该像素的镜面高光
      half Gloss; //该像素的光泽度
      half Alpha; //该像素的透明度
      };
    • Input,输入结构体。如果我们的shader很复杂并且需要知道像素的其他相关信息,我们就可以将以下变量包含在输入结构体中,以此来查询其他的相关变量。
      • float3 viewDir – 视图方向( view direction)值。为了计算视差效果(Parallax effects),边缘光照(rim lighting)等,需要包含视图方向(view direction)值。
      • float4 with COLOR semantic(比如float4 currentColor,即用户自定义和颜色相关的变量名称) – 每个顶点(per-vertex)颜色的插值。
      • float4 screenPos – 为了反射效果,需要包含屏幕坐标系中的位置信息时包含此参数。
      • float3 worldPos – 世界坐标系中的位置。
      • float3 worldRefl – 表示世界坐标系中的反射向量(reflect vector)。如果表面着色器(surface shader) 不为SurfaceOutput结构中的Normal赋值,也就是说Normal不会发生变化,也就不需要重新求取worldRefl值了,那么就可以直接通过Input结构体传递该参数。
      • float3 worldNormal – 表示世界坐标系中的法线向量(normal vector)。如果表面着色器(surface shader) 不为SurfaceOutput结构中的Normal赋值,也就是说Normal不会发生变化,也就不需要重新求取worldNormal值了,那么就可以直接通过Input结构体传递该参数。
  4. Properties常见语句

    • float 例句:_SomeValue("SomeValue", float) = 10
    • Color 例句:_Color("Background Color", Color) = (1,1,1,1)
    • Texture 例句:_MainTex("Albedo (RGB)", 2D) = "white" {}。其中{option},它只对2D,Rect或者Cube贴图有关,在写输入时我们最少要在贴图之后写一对什么都不含的空白的{},当我们需要打开特定选项时可以把其写在这对花括号内。如果需要同时打开多个选项,可以使用空白分隔。可能的选择有ObjectLinear, EyeLinear, SphereMap, CubeReflect, CubeNormal中的一个,这些都是OpenGL中TexGen的模式。使用[NoScaleOffset]标签可以在检视窗口中隐藏纹理的 Tiling 和 Offset 两个参数选项。如[NoScaleOffset]_MainTex("Albedo (RGB)", 2D) = "white" {}
    • Range 例句:_SomeRange("SomeRange", Range(0.25, 0.5)) = 0.25
    • Vector 例句:_SomeVector("SomeVector", Vector) = (1,1,1,1)
    • Rect 例句:
    • Cube 例句:

https://onevcat.com/2013/07/shader-tutorial-1

  1. Tags。

    Tags { "RenderType"="Opaque" "IgnoreProjector"="True" "ForceNoShadowCasting"="True" "Queue"="Transparent"}

    Tags的种类:

    • RenderType 渲染类型。分为两种:
      • Opaque 不透明
      • Transparent 透明
    • IgnoreProjector 忽略Projector的影响。只有"True""False"两种值。
    • ForceNoShadowCasting 强制关闭阴影投射。只有"True""False"两种值。
    • Queue 指定渲染队列。将透明和不透明物体进行混合,如果渲染队列设置不正确则会导致不透明物体无法呈现在透明物体之后。预定义的Queue有:
      • Background 最早被调用的渲染,用来渲染天空盒或者背景。
      • Geometry 这是默认值,用来渲染非透明物体(普通情况下,场景中的绝大多数物体应该是非透明的)。
      • AlphaTest 用来渲染经过Alpha Test的像素,单独为AlphaTest设定一个Queue是出于对效率的考虑。
      • Transparent 以从后往前的顺序渲染透明物体。
      • Overlay 用来渲染叠加的效果,是渲染的最后阶段(比如镜头光晕等特效)。

        这些预定义的值本质上是一组定义好的整数,Background = 1000, Geometry = 2000, AlphaTest = 2450, Transparent = 3000,最后Overlay = 4000。
        在我们实际设置Queue值时,不仅能使用上面的几个预定义值,我们也可以指定自己的Queue值,写成类似这样:”Queue”=”Transparent+100”,
        表示一个在Transparent之后100的Queue上进行调用。通过调整Queue值,我们可以确保某些物体一定在另一些物体之前或者之后渲染。

  2. LOD。它是 Level of Detail 的缩写,在这里例子里我们指定了其为200(其实这是 Unity 的内建 Diffuse 着色器的设定值)。这个数值决定了我们能用什么样的 Shader。在 Unity 的 Quality Settings中我们可以设定允许的最大 LOD,当设定的 LOD 小于 SubShader 所指定的 LOD 时,这个 SubShader 将不可用。Unity 内建 Shader 定义了一组 LOD 的数值,我们在实现自己的 Shader 的时候可以将其作为参考来设定自己的 LOD 数值,这样在之后调整根据设备图形性能来调整画质时可以进行比较精确的控制。

  • VertexLit 及其系列 = 100
  • Decal, Reflective VertexLit = 150
  • Diffuse = 200
  • Diffuse Detail, Reflective Bumped Unlit, Reflective Bumped VertexLit = 250
  • Bumped, Specular = 300
  • Bumped Specular = 400
  • Parallax = 500
  • Parallax Specular = 600

UnityCG

  1. Cg API文档:http://http.developer.nvidia.com/Cg/index_stdlib.html

  2. 常量

    • #define PI 3.141592653589
    • const float PI = 3.14159;//pi = 3.14159265358979323846264338327
  3. 顶点输入参数的语义(semantic):

    • POSITION 位置
    • NORMAL 法向量
    • TEXCOORD0 纹理坐标
    • TANGENT 切向量
    • COLOR 颜色
1
2
3
4
5
6
7
8
9
10
struct vertexInput {
float4 vertex : POSITION; // position (in object coordinates, i.e. local or model coordinates)
float4 tangent : TANGENT; // vector orthogonal to the surface normal
float3 normal : NORMAL; // surface normal vector (in object coordinates; usually normalized to unit length)
float4 texcoord : TEXCOORD0; // 0th set of texture coordinates (a.k.a. “UV”; between 0 and 1)
float4 texcoord1 : TEXCOORD1; // 1st set of tex. coors.
float4 texcoord2 : TEXCOORD2; // 2nd set of tex. coors.
float4 texcoord3 : TEXCOORD3; // 3rd set of tex. coors.
fixed4 color : COLOR; // color (usually constant)
};
  1. Unity 在 UnityCG.cginc 中预定义的输入结构体:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    struct appdata_base {
    float4 vertex : POSITION;
    float3 normal : NORMAL;
    float4 texcoord : TEXCOORD0;
    };
    struct appdata_tan {
    float4 vertex : POSITION;
    float4 tangent : TANGENT;
    float3 normal : NORMAL;
    float4 texcoord : TEXCOORD0;
    };
    struct appdata_full {
    float4 vertex : POSITION;
    float4 tangent : TANGENT;
    float3 normal : NORMAL;
    float4 texcoord : TEXCOORD0;
    float4 texcoord1 : TEXCOORD1;
    float4 texcoord2 : TEXCOORD2;
    float4 texcoord3 : TEXCOORD3;
    fixed4 color : COLOR;
    // and additional texture coordinates only on XBOX360
    };

    struct appdata_img {
    float4 vertex : POSITION;
    half2 texcoord : TEXCOORD0;
    };
  2. 引入预定义结构体。如果要使用Unity预定义的一些结构体,则需要在着色器中使用#include "UnityCG.cginc"语句引入UnityCG.cginc文件。示例代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    Shader "cginc" { 
    SubShader {
    Pass {
    CGPROGRAM

    #pragma vertex vert
    #pragma fragment frag
    #include "UnityCG.cginc"

    struct vertexOutput {
    float4 pos : SV_POSITION;
    float4 col : TEXCOORD0;
    };

    vertexOutput vert(appdata_full input)
    {
    vertexOutput output;

    output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
    output.col = input.texcoord;

    return output;
    }

    float4 frag(vertexOutput input) : COLOR
    {
    return input.col;
    }

    ENDCG
    }
    }
    }

数据和运算

  1. 数据类型的精度

    • float 精度最高,消耗最大
    • half 精度适中,消耗一般。可用于表示几何位置或纹理坐标
    • fixed 精度最低,消耗最小。常用于颜色
  2. Unity内置的uniform参数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    uniform float4 _Time, _SinTime, _CosTime; // time values
    uniform float4 _ProjectionParams; // x = 1 or -1 (-1 if projection is flipped) y = near plane; z = far plane; w = 1/far plane
    uniform float4 _ScreenParams; // x = width; y = height; z = 1 + 1/width; w = 1 + 1/height
    uniform float3 _WorldSpaceCameraPos;
    uniform float4x4 _Object2World; // model matrix
    uniform float4x4 _World2Object; // inverse model matrix
    uniform float4 _WorldSpaceLightPos0; // position or direction of light source for forward rendering

    uniform float4x4 UNITY_MATRIX_MVP; // model view projection matrix
    uniform float4x4 UNITY_MATRIX_MV; // model view matrix
    uniform float4x4 UNITY_MATRIX_V; // view matrix
    uniform float4x4 UNITY_MATRIX_P; // projection matrix
    uniform float4x4 UNITY_MATRIX_VP; // view projection matrix
    uniform float4x4 UNITY_MATRIX_T_MV; // transpose of model view matrix
    uniform float4x4 UNITY_MATRIX_IT_MV; // transpose of the inverse model view matrix
    uniform float4 UNITY_LIGHTMODEL_AMBIENT; // ambient color
  3. OpenGL着色语言中的变量类型限定符:

  • 统一变量(uniform)
    统一变量存储应用程序通过OpenGL ES 3.0 API(Unity中是通过C#)传入着色器的只读值,对于保存着色器所需要的所有数据类型(如变换矩阵、照明参数和颜色)都很有用。本质上,一个着色器的任何参数在所有顶点或者片段中都应该以统一变量的形式传入。在编译时已知值的变量应该是常量,而不是统一变量,这样可以提高效率。
    统一变量在全局作用域中声明,只需要统一限定符。还需要注意的是,统一变量的命名空间在顶点着色器和片段着色器中都是共享的。统一变量通常保存在硬件中,这个区域被称作“常量存储”,是硬件中为存储常量值而分配的特殊空间。

OpenGL ES2.0 的三种变量类型(uniform,attribute和varying):http://blog.csdn.net/jackers679/article/details/6848085

  1. POSITION vs SV_POSITION
    它们都是 CG/HLSL 中的语义(semantics)。POSITION 将告诉 Unity 把模型的顶点坐标填充到输入参数 v 中,SV_POSITION 将告诉 Unity 顶点着色器的输出是剪裁空间中的顶点坐标。SV_ 前缀的变量代表 system value。

  2. 在Shader中+、-、*、/都是两个操作数各对应分量的运算。矩阵和向量、矩阵和矩阵之间的乘法使用mul函数。向量的点乘和叉乘分别使用dotcross函数。

编辑器辅助标签

  • [Header(Color Ramp Sample)]在检视窗口中添加一个标签文本。
  • [KeywordEnum(None, Top_Bottom, Left_Right, Custom_UV)] Stereo ("Stereo Mode", Float) = 0在检视窗口中添加一个弹出列表控件
  • [Toggle(STEREO_DEBUG)] _StereoDebug ("Stereo Debug Tinting", Float) = 0在检视窗口中添加一个Toggle控件
  • 使用[NoScaleOffset]标签可以在检视窗口中隐藏纹理的 Tiling 和 Offset 两个参数选项。如[NoScaleOffset]_MainTex("Albedo (RGB)", 2D) = "white" {}

深度测试(ZTest)

ZTest 可取值为:Greater , GEqual , Less , LEqual , Equal , NotEqual , Always , Never , Off,默认是 LEqual,ZTest Off 等同于 ZTest Always。ZWrite 可取值为:On , Off,默认是 On。系统中存在一个颜色缓冲区和一个深度缓冲区,分别存储颜色值和深度值,来决定画面上应该显示什么颜色。深度值是物体在世界空间中距离摄像机的远近。距离越近,深度值越小;距离越远,深度值越大。

Tags 语句

Tags 语句是用来给子着色器或通道分配一些键值对,用于控制渲染顺序、渲染类型、投影器效果等参数。例如,Tags{ "PreviewType" = "Plane" }语句的含义如下:
- PreviewType 标签是用来指示材质检视面板预览应如何显示材质。34
- PreviewType = “Plane” 的意思是,材质在检视面板中会显示为一个平面,而不是默认的球形。这个标签一般用于 UI 着色器,比如透明、粒子、字体等。

混合

Unity 使用 Blend 命令来设置混合的渲染状态,Blend 命令的参数是源因子和目标因子,它们分别表示片段着色器的输出值和渲染目标的原有值的乘数1。例如:Blend SrcAlpha OneMinusSrcAlpha 表示开启Alpha混合(即传统的透明度混合),并指定混合因子。其他混合命令如下:

1
2
3
4
5
6
Blend SrcAlpha OneMinusSrcAlpha // Traditional transparency
Blend One OneMinusSrcAlpha // Premultiplied transparency
Blend One One // Additive
Blend OneMinusDstColor One // Soft Additive
Blend DstColor Zero // Multiplicative
Blend DstColor SrcColor // 2x Multiplicative

颜色与颜色之间可以进行加减乘除进行混合运算。加法可以起到颜色叠加的效果,但是由于颜色值的值域为0~1,相加很容易达到1,颜色会愈发明亮,因此叠加建议使用乘法;减法可以进行反色处理,但是同样是值域的原因,数值达到0,颜色就很暗淡,因此要做反色建议使用除法。关于混合的更多内容请参考官方文档:ShaderLab: Blending

其他

o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); 等价于 o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;。注意,在这两句代码的前面变量声明的部分要多声明一句float4 _MainTex_ST;TRANSFORM_TEX 是 Unity 内置的宏,在 UnityCG.cginc 中定义的:

1
2
// Transforms 2D UV by scale/bias property
#define TRANSFORM_TEX(tex, name)(tex.xy * name##_ST.xy + name##_ST.zw)

教程

  • 浅墨:https://github.com/QianMo/Awesome-Unity-Shader
  • 风宇冲:https://blog.sina.com.cn/s/articlelist_1192309394_4_1.html

参考链接

  • ShaderLab:向子着色器分配标签
  • Unity Shader 子着色器标签(SubShader Tags) - 知乎专栏
  • 【Unity Shader】浅析Unity shader中RenderType的作用及_CameraDepthNormalsTexture …
  • ShaderLab之SubShader - 简书