US2S3L13——CG内置文件

CG内置文件

CG中的内置文件和内置函数一样,是用于帮助我们进行Shader开发的
利用其中的函数、宏、全局变量等内容,可以提升我们Shader的开发效率

如果想要了解更多的内置内容可以参阅Unity官网的资料:

CG内置文件的位置和作用

我们可以在Unity的安装目录中找到CG内置文件,在:编辑器安装目录/Data/CGIncludes​ 中

  • 后缀为.cginc​的文件为CG语言内置文件
  • 后缀为.glslinc​的文件为GLSL语言内置文件

image

他们是预定义的Shader文件,里面包含了一些已经写好的Shader相关逻辑
作用和CG内置函数一样,可以提升我们的Shader开发效率,可以直接使用其中的方法等内容来进行逻辑开发

Unity中常用的内置文件有

  1. UnityCG.cginc​:包含最常用的帮助函数、宏和结构体等

    其中:

    • 常用的帮助函数有:函数(UnityCG.cginc中)
    • 常用的结构体有:结构体(UnityCG.cginc中)
  2. Lighting.cginc​:包含各种内置光照模型。如果编写的是Surface Shader(标准表面着色器),会自动包含进来

  3. UnityShaderVariables.cginc​:编译UnityShader时,会自动包含进来。包含许多内置的全局变量

  4. HLSLSupport.cginc​:编译UnityShader时,会自动包含进来。声明了很多用于跨平台编译的宏和定义

等等

如何使用CG内置文件

在CG语句块中进行引用,通过编译指令 #include "内置文件名.cginc"​ 的形式进行引用
我们便可以在CG语言中使用其中的内容

注意:一些常用的函数、宏、变量,可以不用引用,Unity会在编译时自动识别,但是为了避免报错,建议都引用

"宏"通常指的是一种预处理指令或代码片段,用于在代码中进行文本替换。
宏允许程序员定义一个标识符(通常以大写字母表示)来代表一个代码片段,
然后在编译时将这个标识符替换为相应的代码,这种替换过程称为宏展开。

说人话:就是为一些代码片段,取一个别名,方便使用。在真正编译时在把这个别名翻译成对应的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex myVert
#pragma fragment myFrag

#include "UnityCG.cginc"
//...
ENDCG
}
}

常用内容

  • 函数(UnityCG.cginc中)

    下列的函数都是将一个点或者方向或者矢量转换到另一个坐标空间中

    1. float3 WorldSpaceViewDir(float4 v)

      输入一个模型空间中的顶点位置,返回世界空间中从该点到摄像机的观察方向

    2. float3 ObjSpaceViewDir(float4 v)

      输入一个模型空间中的顶点位置,返回模型空间中从该点到摄像机的观察方向

      它可用于切线空间下计算法线纹理,通过顶点,计算模型空间下摄像机观察方向
      用例可见:US3S2L6——切线空间下计算法线纹理贴图

    3. float3 WorldSpaceLightDir(float4 v)

      仅用于向前渲染中。输入一个模型空间中的顶点位置,返回世界空间中从该点到光源的光照方向。(返回值没有被归一化)

    4. float3 ObjSpaceLightDir(float4 v)

      仅用于向前渲染中。输入一个模型空间中的顶点位置,返回模型空间中从该点到光源的光照方向。(返回值没有被归一化)

      它可用于切线空间下计算法线纹理,通过顶点,计算模型空间下光的方向
      用例可见:US3S2L6——切线空间下计算法线纹理贴图

    5. float3 UnityObjectToWorldNormal(float3 norm)

      把法线方向从模型空间转换到世界空间中

    6. float3 UnityObjectToWorldDir(in float3 dir)

      把方向矢量从模型空间转换到世界空间中

    7. float3 UnityWorldToObjectDir(float3 dir)

      把方向矢量从世界空间转换到模型空间中

    等等

    还有另外的一些常用函数,详见:US3S1L9——Unity内置光照计算相关函数

  • 结构体(UnityCG.cginc中)

    1. appdata_base​:用于顶点着色器输入:顶点位置、顶点法线、第一组纹理坐标

      其成员为:

      1
      2
      3
      4
      5
      6
      struct appdata_base {
      float4 vertex : POSITION; //模型空间中的顶点位置
      float3 normal : NORMAL; //顶点法线
      float4 texcoord : TEXCOORD0; //第一组纹理坐标
      UNITY_VERTEX_INPUT_INSTANCE_ID
      };
    2. appdata_tan​:用于顶点着色器输入:顶点位置、顶点法线、顶点切线、第一组纹理坐标

    3. appdata_full​:用于顶点着色器输入:顶点位置、顶点法线、顶点切线、四组(或更多)纹理坐标

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      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; //顶点颜色
      UNITY_VERTEX_INPUT_INSTANCE_ID
      };
    4. appdata_img​:用于顶点着色器输入:顶点位置、第一组纹理坐标

    5. v2f_img​:用于顶点着色器输出:裁剪空间中的位置,纹理坐标

      1
      2
      3
      4
      5
      6
      7
      struct v2f_img
      {
      float4 pos : SV_POSITION; //裁剪空间中顶点位置
      half2 uv : TEXCOORD0; //纹理的UV坐标
      UNITY_VERTEX_INPUT_INSTANCE_ID
      UNITY_VERTEX_OUTPUT_STEREO
      };

    等等

    使用内置结构体进行参数传递示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    CGPROGRAM
    #pragma vertex myVert
    #pragma fragment myFrag

    #include "UnityCG.cginc"

    sampler2D _My2D; //使用外部关联的2D贴图

    v2f_img myVert(appdata_base data)
    {
    v2f_img v2fData; //输出到片元着色器的结构体变量
    v2fData.pos = UnityObjectToClipPos(data.vertex); //转换模型空间的顶点坐标到裁剪空间
    v2fData.uv = data.texcoord; //将纹理坐标赋值到结构体成员上

    return v2fData;
    }

    fixed4 myFrag(v2f_img data) : SV_Target
    {
    fixed4 color = tex2D(_My2D, data.uv); //使用纹理查询获取贴图上的某个坐标的颜色
    return color;
    }
    ENDCG
  • 变换矩阵宏(UnityShaderVariables.cginc中)

    编译UnityShader时,该文件内的宏会自动包含进来,因此无需去 #include

    坐标空间变换顺序:模型空间 -> 世界空间 -> 观察空间 -> 裁剪空间 -> 屏幕空间

    在片元着色器中,我们常常需要完成:模型空间 -> 世界空间 -> 观察空间 -> 裁剪空间的变换
    而下面的宏就是可以直接用于进行矩阵计算的矩阵宏,它们就可以用来进行坐标空间的转换

    1. UNITY_MATRIX_MVP​:当前的 模型 ×\times 观察 ×\times 投影 矩阵,用于将顶点/方向向量**从模型空间变换到裁剪空间中**

      1
      2
      3
      4
      5
      6
      7
      8
      9
      v2f_img myVert(appdata_base data)
      {
      v2f_img v2fData; //需要传递给片元着色器的数据
      v2fData.pos = UnityObjectToClipPos(data.vertex); //将模型空间的顶点转换到裁剪空间
      //上一句等效于
      v2fData.pos = mul(UNITY_MATRIX_MVP​, data.vertex); //将模型空间的顶点转换到裁剪空间

      return v2fData;
      }
    2. UNITY_MATRIX_MV​:当前的 模型 ×\times 观察 矩阵,用于将顶点/方向向量**从模型空间变换到观察空间中**

    3. UNITY_MATRIX_M​:当前的模型矩阵,用于将将顶点/方向向量**从模型空间变换到世界空间中**

      它有一个等价的转换矩阵为 unity_ObjectToWorld

    4. UNITY_MATRIX_V​:当前的观察矩阵,用于将顶点/方向向量**从世界空间变换到观察空间中**

    5. UNITY_MATRIX_P​:当前的投影矩阵,用于将顶点/方向向量**从观察空间变换到裁剪空间中**

    6. UNITY_MATRIX_VP​:当前的 观察 ×\times 投影 矩阵,用于将顶点/方向向量**从世界空间变换到裁剪空间中**

    7. UNITY_MATRIX_T_MV​:UNITY_MATRIX_MV​的转置矩阵

    8. UNITY_MATRIX_IT_MV​:UNITY_MATRIX_MV​的逆转置矩阵,用于**将法线从模型空间变换到观察空间**,也可用于得到UNITY_MATRIX_MV​的逆矩阵

    9. _Object2World​:当前的模型矩阵,用于将顶点/方向矢量**从模型空间变换到世界空间**

      1
      2
      3
      4
      5
      6
      7
      8
      v2f_img myVert(appdata_base data)
      {
      v2f_img v2fData; //需要传递给片元着色器的数据

      float4 worldPos = mul(_Object2World, data,vertex​) //得到世界空间下的顶点坐标

      return v2fData;
      }
    10. _World2Object​:_Object2World​ 的逆矩阵,用于将顶点/方向矢量**从世界空间变换到模型空间**

    等等

  • 变量

    1. _Time​ (不用引用,直接使用即可)

      自关卡加载以来的时间(t/20, t, t*2, t*3)​用于对着色器内的事物进行动画处理

    2. _LightColor0​ (向前渲染时,引用UnityLightingCommon.cginc​;延迟渲染时,引用UnityDeferredLibrary.cginc​)

      光的颜色

    等等