Unity Shader学习日记 part5 CG基础
在了解完Shader的基本结构之后,我们再来看看编写着色器的语言。
Shader编写语言有CG,HLSL两种,我们主要学习CG的写法。
数据类型
CG的基础变量类型
uint a=12;//无符号32位整形
int b=12;//32位整形
float f=1.2f;//32位浮点型
half h=1.2h;//16位浮点型
fixed fix=1.2;//12位浮点型
bool b=true;
string str="123";
纹理对象句柄(理解为一个纹理就行)
sampler sam;
sampler1D 用于一维纹理,通常用于对一维纹理进行采用,如从左到右的渐变色
sampler2D 用于二维纹理,最常见的纹理类型之一,用于处理二维图像纹理,比如贴图
sampler3D 用于三维纹理,通常用于体积纹理,如体积渲染
samplerCUBE 用于立方体纹理,通常用于处理环境映射登需要立方体贴图的情况
samplerRECT 用于处理矩形纹理,通常用于一些非标准的纹理映射需求
他们都是用来处理纹理(Texture)数据的数据类型
注意区别在意纹理的类型和维度
可以看到,基本上和C#差不多,除此之外多了纹理对象句柄的类型。
CG的复合变量类型
复合类型也和C#差不多,数组,结构体
基础复合数据类型
数组:和C#差不多
int c[4]={1,2,3,4};
// a.length;
int d[2][3]={
{1,2,3},{4,5,6}};
//d.length 行
//d[0].length 列
//结构体
//没有访问修饰
//申明结束加分号
//一般在函数外部申明
注意结构体的写法
CG的特殊变量类型
学了这么多知识点,其中矩阵和向量占了很大一部分,同样,CG中也为我们提供了矩阵的变量以及向量相关的变量。
这些变量的类型都是基于基本数据类型的,无非就是在基本数据类型的后面加一些数字之类的
//向量:CG语言内置的数据类型
//内置的向量类型是基于基础数据类型申明的,最大不超过4维,可以是任意数值类型
//数据类型n=数据类型(a1,...,an);
fixed2 f2=fixed2(1.3,1.5);
fixed3 f3=fixed3(2,3,4);
//矩阵(行列不大于4不小于1)
//数据类型 'n'x'm'={n1m1,n1m2...}
int2x3 mytest={1,2,3,
3,4,6};
int4x4 test1={1,2,3,4,
5,6,7,8,
4,5,6,7,
1,2,2,3};
其他类型的向量和矩阵我就不举例了,类似一下,大差不差。
了解了这些之后,我们再来看看一些类型的特殊用法
Bool的特殊用法
之前我们提到特殊变量类型是基于基本类型的,所以,bool类型同样能够用在特殊类型中
//bool类型同样可以用于向量的申明
//它可以用于储存一些逻辑判断结果
//比如
//float3 fa=float3(0.5,0.0,1.0);
//float3 fb=float3(0.6,-0.1,0.9;
//bool3 bc=a<b;
//结果为 boool3(true,false,false)
学习了数据类型之后,我们思考,矩阵和向量使用如此的频繁,那我我们怎么去获取这些变量中的分量值呢?这就需要Swizzle操作符
Swizzle操作符
Swizzle使用点号(.)来表示,使用就和我们去使用类的成员一样。
但是,Swizzle操作符的作用远不止这一点,我们来看看
//如:向量.xyzw,向量.rgba来获取其中的值
//使用:1。用来提取分量,2.用来重新排列分量 3.创建新的分量
1.用来提取分量
fixed4 f4=fixed4(1,2,3,4);
fixed f=f4.w;
f=f4.r;
2.用来重新排列分量
f4=f4.wyzx;
f4=f4.gbar;
3.创建新的分量
//fixed3 f3=f4.xyz;
//fixed2 f2=f4.rg;
fixed4 f4_1=fixed4(f2,1,2);//低维创建高维,补充元素即可
我们发现,矩阵中的数据其实就是基本数据类型,那我们能不能用向量来表示呢?
向量和矩阵的特殊用法
其实向量和矩阵是可以相互使用的,就像这样
//向量和矩阵的更多用法
//1.利用向量声明矩阵
fixed4x4 f44={fixed4(1,2,3,4),
fixed4(2,3,4,5),
fixed4(4,5,6,7),
fixed4(5,6,7,8)};
//2.获取矩阵中的元素
f=f44[0][0];//一行一列
//3.利用向量获取矩阵的某一行
f4_1=f44[0];
//4.高维转低维
fixed3x3 f33=f44;//自动取值
fixed3 f5=f;
注意的是,如果使用低维转高维,需要将欠缺的维度补齐
那么,我们再来了解了解运算表达式吧
语法相关
基本运算和流程控制
比较运算符(和C#一样的)
条件运算符(三元运算符,用法和C#一样)
逻辑运算符(和C#一样,不过CG中不存在“短路”)
短路:在比较的过程中,如果一边满足则不会继续比较
属性运算符(和C#一样,不过%取余只能整数取余)
流程控制(和C#一样)
条件分支(if,switch)
循环(for,while,do while)
注意:虽然用法和C#一样,但是在使用的时候更多考虑性能的消耗
1.尽量少的使用循环,必要情况可以减少次数和复杂度
2.可以利用GPU并行性来代替循环
3.尽量避免复杂的条件分支
这一部分和C#没什么区别,唯一需要注意的是逻辑运算符短路的问题。
函数相关
这一部分也和C#一样,我就不多说了
函数相关
函数的申明和用法几乎和C#中一模一样
无返回值
结构
void name(in 参数类型 参数名,out 参数类型 参数名)
{
函数体
}
name:函数名,CG中一般小写
in:表示输入参数,外部传递过来的值,内部不会做更改,只会用来计算,允许有多个
out:表示输出参数,由内部向外传递,函数内部必须进行初始化或者修改,允许有多个
in,out可以不写,这样就可以避免in,out的特性,不过为了可读性和可维护性,建议写上
void test_01(in fixed inf,out fixed outf)
{
outf=inf+10;
}
至此,CG的基本内容就结束了,接下来我们看看CG的编写
CG编写
在此之前我们先来看看CG的结构
可以看到顶点片元着色器的结构和表面光照着色器的结构是不同的,不过他们都是使用CG代码段的方式来编写,所以我们在编写CG代码时同样要将代码放在CGPROGRAM和ENDCG之间。
在CG中最重要的就是顶点回调函数以及片元回调函数了,这也是渲染代码存放的位置
顶点/片元函数
//顶点着色器回调函数
//编译指令
#pragma vertex myVert
//处理顶点相关
#pragma fragment myFra
//处理颜色相关
//POSITION,SV_POSITION,语义,用来表面参数的含义
//POSITION:把模型的顶点坐标填充到输入的参数v中
//SV_POSITION:输出的内容是裁剪空间中的顶点坐标
float4 myVert(float4 v:POSITION):SV_POSITION
{
//mul是CG提供的内部函数,矩阵和向量的乘法
//UNITY_MATRIX_MVP,代表一个变换矩阵,是UNITY的内置模型,观察,投影矩阵的集合
//return mul(UNITY_MATRIX_MVP,v);
return UnityObjectToClipPos(v*2);//和上一个等价,新的写法
}
//片源着色器回调函数
//SV_Target:告诉着色器,把输出的颜色存储到一个渲染目标中,这里输出到默认的帧缓存中
fixed4 myFra():SV_Target
{
return fixed4(1,0,0,1);
}
可以看到最主要的部分就是申明编译指令
这个步骤决定了函数的名字以及调用。除此之外,我们可以看到,在函数的后面或者参数中还有一些代码,这一部分叫语义,它定义了参数的含义,就像POSITION:把模型的顶点坐标填充到输入的参数v中,这部分后面再说。
那么如何在函数中使用我们定义的变量呢?
非常简单,只需要申明一个同名变量即可,不过需要注意类型的对应
函数中使用自定义的变量
ShaderLab的属性和CG变量类型的对应
ShaderLab CG类型
Color,Vector float4,half4,fixed4
Range,Float,Int float,half,fixed
2D sampler2D
Cube samplerCube
3D sampler3D
2DArray sampler2DArray
使用是在CG中声明对应类型的同名变量即可
语义
在上面我们基本了解了语义的基本用法。我们再来看看。
语义:修饰输入和输出参数,让shader知道从哪里读取数据,并且把数据输出到哪里
语义是什么不重要,重要的是我们需要了解的语义有哪些
应用阶段-->顶点着色器
常用语义
应用阶段-->顶点着色器
一般在顶点着色器回调函数的传入中使用
POSITION:
模型空间的顶点位置,通常为float4类型
NORMAL:
顶点法线,通常为float3类型
TANGENT:
顶点切线,通常为float4类型
TEXCOORDn:
例如:TEXCOOORD0,TEXCOORD1....
该顶点的纹理坐标,通常为float2或者float4类型
n 表示第n+1组纹理坐标,TEXCOOORD0 第一组纹理坐标
纹理坐标:UV坐标,表示这个顶点在对于纹理图像上的位置
COLOR:
顶点颜色,通常为fixed4或者float4类型
顶点着色器-->片元着色器
顶点着色器-->片元着色器
一般在顶点着色器的返回值中使用
SV_POSITION:(必须)
裁剪空间中的顶点坐标
COLOR0:
通常用来输出第一组的顶点颜色
COLOR1
通常用来输出第二组颜色
TEXCOORD0--TEXCOORD7
通常用于输出纹理坐标
片元着色器输出
片元着色器输出
一般在片元着色器的返回值中使用
SV_Target:
输出的值会存档渲染目标中
如果顶点着色器和片元着色器需要更多的参数,可以通过定义结构体的方式,对参数进行封装传递
东西非常多,可以再使用中慢慢熟悉
我们想一下,这么多东西,那么再我们使用的时候必然会相当的麻烦,所以CG也内置了很多的函数,我们来看看
CG内置函数
数学相关
//1.三角函数相关
sincos(floatx,out s,out c) 同时计算x的sin和cos值并返回
sin(x) 正弦函数
cos(x) 余弦函数
tan(x) 正切函数
sinh(x) 双曲正弦函数
cosh(x) 双曲余弦函数
tanh(x) 双曲正切函数
asin(x) 反正弦函数,输入参数[-1,1],返回[-pai/2,pai/2]区间的角度值
acos(x) 反余弦函数,输入参数[-1,1],返回[0,pai]区间的角度值
atan(x) 反正切函数,输入参数[-1,1],返回[-pai/2,pai/2]区间的角度值
atan2(x) 计算y/x的反正切值,和atan功能一样,只是输入参数不同 atan(x)=atan2(x,1)
//2.向量,矩阵相关
cross(A,B) 叉乘(传入必须为三维向量)
dot(A,B) 点乘(三维向量)
mul(M,N) 计算两个矩阵相乘
mul(M,V) 计算矩阵和向量的相乘
mul(v,m) 计算向量和矩阵的相乘
transpose(M) M为矩阵,计算M的转置矩阵
determinant(m) 计算矩阵的行列式因子
//3.数值相关
abs(x)
ceil(x) 向上取整
floor(x) 向下取整
clamp(x,a,b)
等等
//4.其他
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必须归一化的三维向量
refract(I,N,eta) 计算折射向量,I为入射,N为顶点法向量,eta为折射系数。I是指向顶点的,I和N必须归一化的三维向量
纹理相关
//纹理相关
返回值均为fixed4类型的颜色值
1.二维纹理
tex2D(sampler2D tex,flota2 s) 二维纹理查询
等等
2.立方体纹理
3.其他纹理
CG内置文件
CG内置文件
unity中的常用内置
1.UnityCG.cginc
2.Lighting.cginc
3.UnityShaderVariables.cginc
4.HLSLSupport.cginc
等等
这些内置文件为我们提供了很多的方法,需要使用时通过#include 的方式引用即可。
来看看这些文件中常用的东西吧
//常用内容
方法(UnityCG.cginc)
1.float3 WorldSpaceViewDir(float4 v)
输入模型空间的顶点位置,返回世界空间中从该点到摄像机的观察方向
2.float3 ObjSpaceViewDir(float4 v)
输入模型空间的顶点位置,返回模型空间中该点到摄像机的观察方向
3.float3 WorldSpaceLightDir(float4 v)
仅用于向前渲染,输入一个模型空间的顶点位置,返回世界空间中从该店到光源的光照方向(没有归一化)
4.float3 ObjSpaceLightDir(float4 v)
仅用于向前渲染,输入一个模型空间的顶点位置,返回模型空间中从该店到光源的光照方向(没有归一化)
5.float3 UnityObjectToWorldNormal(float3 norm)
把法线方向从模型空间转到世界空间中
6.float3 UnityObjectToWorldDir(float3 dir)
把方向矢量从模型空间转到世界空间中
7.float3 UnityWorldToObjectDir(float3 dir)
把方向矢量从世界空间转到模型空间中
结构体(UnityCG.cginc)
1.appdata_base(顶点着色器输入)
顶点位置,顶点法线,第一组纹理坐标
2.appdata_tan(顶点着色器输入)
顶点位置,顶点法线,顶点切线,第一组纹理坐标
3.appdata_img(顶点着色器输入)
顶点位置,第一组纹理坐标
4.appdata_full(顶点着色器输入)
顶点位置,顶点法线,顶点切线,四组(或者更多)纹理坐标
5.appdata_img(顶点着色器输输出)
裁剪空间中的位置,纹理坐标
等等
变换宏矩阵
等等
东西有很多,我只举例部分,感兴趣的可以到HSLS的官网查看,这部分CG与HSLS差不多。
内部函数 - Win32 apps | Microsoft Learn
具体的用法会在之后的编写中用到,这里留个印象即可。
那么到这里,CG的基础就完结啦,后面部分我们会学习到基本的光照模型,到时候我们在通过使用的方式来认识他们。