Contents

CG语法

CG语句写在哪里?

对于顶点\片元着色器来说

CG语句需要写在Pass渲染通道语句块中

我们需要在Pass语句块中加入指令CGPROGRAM

指定着色器函数

我们需要先使用 #pragma 声明编译指令

用来指定 顶点、片元着色器使用的函数

#pragma vertex name(实现顶点着色器的函数名)
#pragma fragment name(实现片元着色器的函数名)

之后我们只需要在这两个函数中书写Shader逻辑即可


CG基础数据类型

类型说明
uint32位无符号整型
int32位整型
float32位浮点数 符号:f
half16位浮点数 符号:h
fixed12位浮点数
bool布尔
string字符串
smapler纹理对象句柄(处理纹理(Texture)数据的数据类型)
sampler1D用于一维纹理,通常用于对一维纹理进行采样,例如从左到右的渐变色
sampler2D用于二维纹理,最常见的纹理类型之一。它用于处理二维图像纹理,例如贴图
sampler3D用于三维纹理,通常用于体积纹理,例如体积渲染
samplerCUBE用于立方体纹理,通常用于处理环境映射等需要立方体贴图的情况
samplerRECT用于处理矩形纹理,通常用于一些非标准的纹理映射需求

声明

float a = 1.4;

基础复合数据类型

数组

int a[4] = {1,2,3,4};

二维数组

int b[2][3] = {{1,2,3},{4,5,6}};

结构体

struct v2f{
    int a[4] = {1,2,3,4};
    int b[2][3] = {{1,2,3},{4,5,6}};
}

特殊数据类型

向量

向量类型属于CG语言的内置数据类型

内置的向量类型是基于基础数据类型声明的

向量的最大维度不超过4维

数据类型可以是任意数值类型

int2 = int2(1,2);
float3 = float3(1.2,3.2,1);
half4 = half4(2,4,1,4);

矩阵

矩阵类型属于CG语言的内置数据类型

矩阵的最大行列不大于4,不小于1

数据类型可以是任意数值类型

int2x3 mInt2x3 = {1,2,3
                  4,5,6};

bool类型的特殊使用

bool类型同样可以用于如同向量一样声明

它可以用于存储一些逻辑判断结果

float3 a = (0.5,0.0,1.0);
float3 b = (0.6,-0.1,0.9);
bool3 c = a < b; //结果为bool3(true,true,false)

Swizzle操作符

Swizzle操作符通常以点号(.)的形式使用,后面跟着所需的分量顺序

对于四维向量来说我们可以通过向量.xyzw向量.rgba

两种分量的写法来表示向量中的四个值

其中xyzw和rgba分别代表向量中的四个元素

在此的意义就是向量一般可以用来表示坐标和颜色


如何使用Swizzle操作符

利用它来提取分量

fixed4 f1 = fixed4(1,2,3,4);
fixed f = f1.w;//xyzw 4
f = f1.a //rgba 4 

利用它来重新排列分量

fixed4 f2 = fixed4(1,2,3,4);
f4 = f2.yzxw; //fixed4(2,3,1,4)

利用它来创建新的向量

 fixed4 f3 = fixed4(1,2,3,4);
 fixed3 f4 = f3.xyz;
 fixed2 f5 = f3.rg;
 fixed4 f5_2 = fixed4(f5,f5);

向量和矩阵的更多用法

利用向量声明矩阵

fixed4x4 f4x4 = {fixed4(1,2,3,4),
                 fixed4(1,2,3,4),
                 fixed4(1,2,3,4),
                 fixed4(1,2,3,4)};

获取矩阵中的元素

fixed f = f4x4[0][0];

利用向量获取矩阵中的某一行

fixed4 f4 = f4x4[0]

高维转低维

向量和矩阵都可以用低维存高维,会自动舍去多余元素

fixed3x3 f3x3 = f4x4;

运算符

比较运算符

含义运算符
大于>
小于<
大于等于>=
小于等于<=
等于==
不等于!=

条件运算符

含义运算符
三目运算符condition ? value_if_true : value_if_false

逻辑运算符

逻辑运算符在CG中不存在C#中的"短路"操作

含义运算符
逻辑或运算符||
逻辑与运算符&&
逻辑非运算符

数学运算符

数学运算符在CG中取余符号只能向整数取余

含义运算符
加法+
减法-
乘法*
除法/
取余%
自增++
自减--

条件分支语句

CG语法中的流程控制语句和C#中的使用一模一样

需要注意的是

在使用它们时要更多的考虑性能消耗

  1. 尽量少的使用循环语句,如果一定要用要减少次数和复杂度
  2. 要利用GPU并行性这一特点来替代循环
  3. 尽量避免复杂的条件分支

函数

无返回值的函数

void name(in 参数类型 参数名, out 参数类型 参数名)
{
    //函数体
}

void:以void开头,表示没有返回值

name:函数的名称

in:表示是输入参数,表示由函数外部传递给函数内部,内部不会修改该参数,只会使用该参数进行计算,允许有多个

out:表示是输出参数,表示由函数内部传递给函数的调用者,在函数内部必须对该参数值进行初始化或修改,允许有多个

注意

in和out都可以省略,省略后就没有了in和out相关的限制

虽然可以省略,但是建议在编写Shader时不要省略in和out

因为他们可以明确参数的传递方式,提高代码的可读性和可维护性


有返回值的函数

type name(in 参数类型 参数名)
{
    //函数体
    return 返回值;
}

type:返回值类型

return:返回指定类型的数据

注意

虽然可以在有返回值的函数中使用out参数

但是这并不是常见做法,除非是一些自定义逻辑函数

对于顶点/片元着色器函数只会使用单返回值的方式进行处理


CG当中的函数声明和函数使用和C#中基本一致

区别主要是CG中的in和out关键字

  1. in表示输入参数,用于外部传给内部,内部只会用,不会改,可以有多个
  2. out表示输出参数,用于内部传给外部,内部必须初始化或修改,可以有多个

对于有返回值的函数,要不采用返回值形式,要不采用out


语义

G语言中提供了 语义 这种特殊关键字用于修饰函数中的传入参数和返回值

它的主要作用是让Shader知道从哪里读取数据,并把数据输出到哪里

让我们在Shader开发当中可以获取到想要的数据,并且可以把数据传递出去

Unity中只支持CG当中的部分语义

CGPROGRAM
#pragma vertex myVert
float4 myVert(float4 v:POSITION):SV_POSITION
{
    return UntiyObjectToClipes(v);
}
ENDCG

POSITIONSV_POSITION都是CG语言的语义

POSITION把模型的顶点坐标填充输入到参数v中

SV_POSITION顶点着色器输出的内容是裁剪空间中的顶点坐标


应用阶段—>顶点着色器

应用阶段传递模型数据给顶点着色器时Unity支持的语义

一般在顶点着色器回调函数的传入参数中应用

常用语义

语义含义
POSITION模型空间中的顶点位置,通常是float4类型
NORMAL顶点法线,通常是float3类型
TANGENT顶点切线,通常是float4类型
TEXCOORD0,1...该顶点的纹理坐标,通常是float2或者float4类型,TEXCOORD0表示第一组纹理坐标,依次类推(纹理坐标:也称UV坐标,表示该顶点对应纹理图像上的位置)
COLOR顶点颜色,通常是fixed4或float4类型

顶点着色器—>片元着色器

从顶点着色器传递数据给片元着色器时Unity支持的语义

一般在顶点着色器回调函数的返回值中应用

常用语义

语义含义
SV_POSITION裁剪空间中的顶点坐标(必备)
COLOR0通常用于输出第一组顶点颜色(不是必须的)
COLOR1通常用于输出第二组顶点颜色(不是必须的)
TEXCOORD0~TEXCOORD7通常用于输出纹理坐标(不是必须的)

片元着色器输出

片元着色器输出时Unity支持的常用语义,一般在片元着色器回调函数的返回值中应用。

常用语义:

语义含义
SV_Target输出值会存储到渲染目标中

更多语义

HLSL语义汇总:https://learn.microsoft.com/zh-cn/windows/win32/direct3dhlsl/dx-graphics-hlsl-semantics?redirectedfrom=MSDN


顶点/片元着色器传递更多参数

当我们在顶点/片元着色器当中想要获取更多模型相关信息时

可以使用结构体对数据进行封装

从而实现传递更多参数

声明用于传递给顶点着色器函数的结构体a2v

struct a2v
{
    //顶点坐标(基于模型空间)
    float4 vertex:POSITION;
    //顶点法线(基于模型空间)
    float3 normal:NORMAL;
    //纹理坐标(uv坐标)
    float2 uv:TEXCOORD0;
};

声明用于传递给片元着色器的结构体v2f

struct v2f
{
    //裁剪空间下的坐标
    float4 position:SV_POSITION;
    //顶点法线(基于模型空间)
    float3 normal:NORMAL;
    //纹理坐标(uv坐标)
    float2 uv:TEXCOORD0;
};

完整实例

CGPROGRAM
#pragma vertex myVert
#pragma fragment frag

//该结构体 是用于从应用阶段获取语义数据后 传递给顶点着色器回调函数的
struct a2v
{
    //顶点坐标(基于模型空间)
    float4 vertex:POSITION;
    //顶点法线(基于模型空间)
    float3 normal:NORMAL;
    //纹理坐标(uv坐标)
    float2 uv:TEXCOORD0;
};
//从顶点着色器传递给片元着色器的 结构体数据 
//同样这里面的成员也需要用语义去进行修饰
struct v2f
{
    //裁剪空间下的坐标
    float4 position:SV_POSITION;
    //顶点法线(基于模型空间)
    float3 normal:NORMAL;
    //纹理坐标(uv坐标)
    float2 uv:TEXCOORD0;
};
v2f vert(a2v data)
{
    //需要传递给片元着色器的数据
    v2f v2fData;
    v2fData.position = UnityObjectToClipPos(data.vertex);
    v2fData.normal = data.normal;
    v2fData.uv = data.uv;
    return v2fData;
}

fixed4 frag(v2f data):SV_Target
{
    return fixed4(0,1,0,1);
}

ENDCG

ShaderLab属性类型和变量类型的匹配关系

ShaderLab中声明的属性都是需要在Shader(着色器)逻辑中使用的

需要在CG中声明和属性对应类型的同名变量 才可以使用

Shader "ShaderName"{
    Properties{
        _Color("BackGroundColor", Color) = (1,1,1,1)
    }
    SubShader{
        Pass{
            CGPROGRAM
            //使用属性
            float4 _Color;
            ENDCG
        }
    }
    Fallback "VertexLit"
}

CG中变量类型的对应ShaderLab的属性类型

ShaderLab属性类型CG变量类型
Color,Vectorfloat4,half4,fixed4
Range,Float,Intfloat,half,fixed
2Dsampler2D
CubesamplerCube
3Dsampler3D
2DArraysampler2DArray

CG内置函数

Unity Shader中的CG语言提供了各种用于图形编程的函数

这些函数是CG为我们封装好的逻辑我们可以使用它们来编写Unity Shader


数学函数

三角函数相关

函数说明
sincos(float x, out s, out c)该函数同时计算x的sin值和cos值通过s和c进行返回(比分别运算快很多)
sin(x)正弦函数
cos(x)余弦函数
tan(x)正切函数
sinh(x)双曲正弦函数
cosh(x)双曲余弦函数
tanh(x)双曲正切函数
asin(x)反正弦函数,输入参数范围[-1,1],返回[-π/2,π/2]区间的角度值
acos(x)反余弦函数,输入参数范围[-1,1],返回[0,π]区间的角度值
atan(x)反正切函数,输入参数范围[-1,1],返回[-π/2,π/2]区间的角度值
atan2(y,x)计算y/x的反正切值。和atan功能一样,只是输入参数不同。atan(x)=atan2(x,1)

向量、矩阵相关

函数说明
cross(A,B)叉乘(注意:传入向量必须是三维向量)
dot(A,B)点乘(注意:传入向量必须是三维向量)
mul(M,N)计算两个矩阵相乘
mul(M,v)计算矩阵和向量相乘
mul(v,M)计算向量和矩阵相乘
transpose(M)M为矩阵,计算M的转置矩阵
determinant(m)计算矩阵的行列式因子

数值相关

函数说明
abs(x)返回输入参数的绝对值
ceil(x)对输入参数向上取整
floor(x)对输入参数向下取整
clamp(x,a,b)如果x小于a,则返回a;x大于b,则返回b;否则,返回x(“夹紧"函数)
radians(x)角度转弧度
degrees(x)弧度转角度
max(a,b)返回最大值
min(a,b)返回最小值
sqrt(x)求x的平方根,x必须大于0
exp(x)计算e的x次方的值,e=2.71828182845904523536
exp2(x)计算2的x次方的值
fmod(x,y)返回x/y的余数,y不为0
frac(x)返回标量或每个矢量分量的小数部分
frexp(x,out)将浮点数x分解为尾数和直属,即 x = m * 2的exp次方,返回m,将指数存储exp中
isfinite(x)判断标量或者向量中的每个数据是否是有限数,如果是返回true,否则返回false
isinf(x)判断标量或者向量中的每个数据是否是无限,如果是返回true,否则返回false
isnan(x)判断标量或者向量中的每个数据是否是非数据,如果是返回true,否则返回false
ldexp(x,n)计算x * 2的n次方 的值
log(x)计算ln(x)的值,x必须大于0
log2(x)计算log2(x次方)的值,x必须大于0
log10(x)计算log2(x次方)的值,x必须大于0
saturate(x)如果x小于0,返回0;如果x大于1,返回1;否则,返回x
sign(x)如果x大于0,返回1;如果x小于0,返回-1;否则,返回0
smoothstep(min,max,x)值x位于min、max区间内,如果x=min,返回0;如果x=max,返回1;如果在两者之间,返回-2* ((x-min)/(max - min))的三次方 + 3* ((x - min)/(max - min))的2次方
step(a,x)如果x<a,返回0;否则,返回1
all(x)输入参数均不为0,则返回true;否则返回False。相当于逻辑与&&
any(x)输入参数只要有其中一个不为0,则返回true。相当于逻辑或||

其他

函数说明
lit(NdotL,NdotH,m)N表示法向量;L表示入射光向量;H表示半角向量;m表示高光系数;该函数计算环境光、散射光、镜面光的贡献,返回4维向量;x位表示环境光贡献;y位表示散射光贡献;z位表示镜面光贡献;w始终为1
noise(x)噪声函数,返回值始终是0~1之间;对于相同的输入,始终返回相同值,不是真正意义的随机噪声

几何函数

函数说明
length(v)返回一个向量的模长
normalize(v)归一化向量
distance(p1,p2)计算两点之间的距离
reflect(I,N)计算反射光方向向量,I为入射光,N为顶点法向量,。I是指向顶点的,I和N必须被归一化,必须是3维向量
refract(I,N,eta)计算折射向量,I为入射光,N为顶点法向量,eta为折射系数。I是指向顶点的,I和N必须被归一化,必须是3维向量

纹理函数

注意:这些纹理采样函数返回值为 fixed4 类型的颜色值

二维纹理

函数说明
tex2D(sampler2D tex, float2 s)二维纹理查询
tex2D(sampler2D tex, float2 s, float2 dsdx, float2 dsdy)使用导数值查询二维纹理
tex2D(sampler2D tex, float3 sz)二维纹理查询,并进行深度值比较
tex2D(sampler2D tex, float3 sz, float2 dsdx, float2 dsdy)使用导数值查询二维纹理,并进行深度值比较
tex2Dproj(sampler2D tex, float3 sq)二维投影纹理查询
tex2Dproj(sampler2D tex, float4 szq)二维投影纹理查询,并进行深度值比较

立方体纹理

函数说明
texCUBE(samplerCUBE tex, float3 s)查询立方体纹理
texCUBE(samplerCUBE tex, float3 s, float3 dsdx, float3 dsdy)结合导数值查询立方体纹理
texCUBEDproj(samplerCUBE tex, float4 sq)查询立方体投影纹理,并进行深度值比较

其他纹理

函数名称描述
tex1D(sampler1D tex, float s)一维纹理查询
tex1D(sampler1D tex, float s, float dsdx, float dsdy)使用导数值查询一维纹理
tex1D(sampler1D tex, float2 sz)一维纹理查询,并进行深度值比较
tex1D(sampler1D tex, float2 sz, float dsdx, float dsdy)使用导数值查询一维纹理,并进行深度值比较
tex1Dproj(sampler1D tex, float2 sq)一维投影纹理查询
tex1Dproj(sampler1D tex, float3 szq)一维投影纹理查询,并进行深度值比较
texRECT(samplerRECT tex, float2 s)矩形纹理查询
texRECT(samplerRECT tex, float2 s, float2 dsdx, float2 dsdy)使用导数值查询矩形纹理
texRECT(samplerRECT tex, float3 sz)矩形纹理查询,并进行深度值比较
texRECT(samplerRECT tex, float3 sz, float2 dsdx, float2 dsdy)使用导数值查询矩形纹理,并进行深度值比较
texRECTproj(samplerRECT tex, float3 sq)矩形投影纹理查询
texRECTproj(samplerRECT tex, float3 szq)矩形投影纹理查询,并进行深度值比较
tex3D(sampler3D tex, float3 s)查询三维纹理
tex3D(sampler3D tex, float3 s, float3 dsdx, float3 dsdy)结合导数值查询三维纹理
tex3DDproj(sampler3D tex, float4 sq)查询三维投影纹理,并进行深度值比较

CG内置文件

我们可以在Unity的安装目录中找到CG内置文件

在Editor—>Data—>CGIncludes中

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

他们是预定义的Shader文件,里面包含了一些已经写好的Shader相关逻辑

作用和CG内置函数一样,可以提升我们的Shader开发效率

可以直接使用其中的方法等内容来进行逻辑开发

常用的内置文件

文件名描述
UnityCG.cginc包含最常用的帮助函数、宏和结构体等
Lighting.cginc包含各种内置光照模型(自动包含于Surface Shader)
UnityShaderVariables.cginc包含许多内置的全局变量,在编译时自动包含
HLSLSupport.cginc声明了很多用于跨平台编译的宏和定义

使用CG内置文件

在CG语句块中进行引用

通过编译指令

#include "内置文件名.cginc

我们便可以在CG语言中使用其中的内容

注意:一些常用的函数、宏、变量,可以不用引用,Unity会在编译时自动识别

但是为了避免报错,建议都引用

“宏"通常指的是一种预处理指令或代码片段,用于在代码中进行文本替换。

宏允许程序员定义一个标识符(通常以大写字母表示)来代表一个代码片段,

然后在编译时将这个标识符替换为相应的代码,这种替换过程称为宏展开。


常用内容

常用方法(来自 UnityCG.cginc

方法名描述
float3 WorldSpaceViewDir(float4 v)输入模型空间中的顶点位置,返回世界空间中从该点到摄像机的观察方向
float3 ObjSpaceViewDir(float4 v)输入模型空间中的顶点位置,返回模型空间中从该点到摄像机的观察方向
float3 WorldSpaceLightDir(float4 v)输入模型空间中的顶点位置,返回世界空间中从该点到光源的光照方向(未归一化)
float3 ObjSpaceLightDir(float4 v)输入模型空间中的顶点位置,返回模型空间中从该点到光源的光照方向(未归一化)
float3 UnityObjectToWorldNormal(float3 norm)将法线方向从模型空间转换到世界空间中
float3 UnityObjectToWorldDir(in float3 dir)将方向矢量从模型空间转换到世界空间中
float3 UnityWorldToObjectDir(float3 dir)将方向矢量从世界空间转换到模型空间中

常用结构体(来自 UnityCG.cginc

结构体名描述
appdata_base用于顶点着色器输入,包括顶点位置、顶点法线、第一组纹理坐标
appdata_tan用于顶点着色器输入,包括顶点位置、顶点法线、顶点切线、第一组纹理坐标
appdata_full用于顶点着色器输入,包括顶点位置、顶点法线、顶点切线、四组(或更多)纹理坐标
appdata_img用于顶点着色器输入,仅包含顶点位置和第一组纹理坐标
v2f_img用于顶点着色器输出,包含裁剪空间中的位置和纹理坐标

变换矩阵宏(来自 UnityShaderVariables.cginc

坐标空间变换顺序

模型空间 -> 世界空间 -> 观察空间 -> 裁剪空间 -> 屏幕空间

宏名描述
UNITY_MATRIX_MVP当前的模型观察投影矩阵,用于将顶点/方向向量从模型空间变换到裁剪空间
UNITY_MATRIX_MV当前的模型*观察矩阵,用于将顶点/方向向量从模型空间变换到观察空间
UNITY_MATRIX_V当前的观察矩阵,用于将顶点/方向向量从世界空间变换到观察空间
UNITY_MATRIX_P当前的投影矩阵,用于将顶点/方向向量从观察空间变换到裁剪空间
UNITY_MATRIX_VP当前的观察*投影矩阵,用于将顶点/方向向量从世界空间变换到裁剪空间
UNITY_MATRIX_T_MVUNITY_MATRIX_MV的转置矩阵
UNITY_MATRIX_IT_MVUNITY_MATRIX_MV的逆转置矩阵,用于将法线从模型空间变换到观察空间
_Object2World当前的模型矩阵,用于将顶点/方向矢量从模型空间变换到世界空间
_World2Object_Object2World的逆矩阵,用于将顶点/方向矢量从世界空间变换到模型空间

常用变量

变量名描述
_Time自关卡加载以来的时间,用于对着色器内的事物进行动画处理
_LightColor0光源的颜色(向前渲染时,UnityLightingCommon.cginc;延迟渲染,UnityDeferredLibrary.cginc)

CG中的内置文件和内置函数一样,都是用于帮助我们进行Shader开发的。
通过利用其中的函数、宏、全局变量等内容,我们可以提高Shader开发的效率。
建议将常用的内置文件、函数、宏、变量等内容记到笔记中,方便查阅。
更多内置文件和函数的资料可以参考Unity官网的相关文档: