案例
在mesh网格中立方体的范围从-0.5到+0.5,当传入POSITION数据时才用了float2类型, 实际上只传入了xy两个分量,输出的pos经过float4重置组合,将w分量设为1,为了保证矩阵相乘时结果不出错, 把这样的pos传给片元着色器时,片元程序默认pos已经经过矩阵变换了, 所以就将其映射到设备空间,在设备空间当中,最左边到最右边的x值与y值,是从-1到+1, 所以随着屏幕的缩小与放大,这个图形总是等比例适配于屏幕空间 这类的Shader不是大多数使用的,如果要做关于UI的系统,可以在用这类Shader的原理 (在实际UI系统当中,还要考虑到让图形显示在所有场景的最上面,也就是最靠近摄像机的位置,当然还考虑到资源架构的组织问题)
void vert(in float2 objPos:POSITION,out float4 pos:POSITION,out fixed4 col:COLOR)
{
pos=float4(objPos,0,1);
col=pos;
}
void frag(inout fixed4 col:COLOR)
{
float arr[]={0.5,0.5};
col.y=Func2(arr);
}
输入与输出
作为一个顶点程序,其实可以有返回值的
(修改前)
void vert(in float2 objPos:POSITION,out float4 pos:POSITION,out fixed4 col:COLOR)
{
pos=float4(objPos,0,1);
col=pos;
}
(修改后)
float4 vert(in float2 objPos:POSITION,out float4 pos:POSITION): COLOR
{
pos=float4(objPos,0,1);
return pos;
}
💡 将col作为顶点程序的返回值返回,类型为float4,顶点程序后面,要加上语义,以表示返回值的语义,例如上方在vert顶点程序中加上了: COLOR,说明返回值返回的是一个float4类型的COLOR颜色 如果没有写上语义,将无法编译此着色器,导致报错
其实函数可以带上语义的
frag片元函数也一样
fixed4 frag(in fixed4 col:COLOR):COLOR
{
float arr[]={0.5,0.5};
col.y=Func2(arr);
return col;
}
语义重复
在下方程序当中,objPos,定义为inout,既是输入又是输入,输出的POSITION语义与pos输出的语义重复了,片段程序不知道哪一个才是vert输出的POSITION,所以导致该着色器编译失败,出现报错
float4 vert(inout float2 objPos:POSITION,out float4 pos:POSITION): COLOR
{
pos=float4(objPos,0,1);
return pos;
}
Unity当中语义本身不能重复,可以有多个输出,但一个语义只能有一个输出
float4 vert(in float2 objPos:POSITION,out float4 pos:POSITION,out float2 opos:POSITION): COLOR
{
pos=float4(objPos,0,1);
return pos;
}
fixed4 frag(in float2 opos:POSITION,in fixed4 col:COLOR):COLOR
{
float arr[]={0.5,0.5};
col.y=Func2(arr);
return col;
}
在下方的的vert函数中out输出的opos的语义COLOR与vert函数返回值输出的语义COLOR也发生的重复,所以导致了着色器编译不通过报错
float4 vert(in float2 objPos:POSITION,out float4 pos:POSITION,out float2 opos:COLOR): COLOR
{
pos=float4(objPos,0,1);
opos=float2(1,1);
return pos;
}
将vert中的模型坐标传给frag
在下方的Shader程序当中将传入的POSITION也就是objPos赋值给opos,由于都是float2类型,所以可以互相赋值 💡 语义只是强调参数的环境与具体用途,语义不同,只要类型相同就可以赋值
float4 vert(in float2 objPos:POSITION,out float4 pos:POSITION,out float2 opos:TEXCOORD0): COLOR
{
pos=float4(objPos,0,1);
opos=objPos;
return pos;
}
fixed4 frag(in float2 opos:TEXCOORD0,in fixed4 col:COLOR):COLOR
{
float arr[]={0.5,0.5};
col.y=Func2(arr);
return col;
}
结构体
在实际编写过程中,着色器通常需要输入输出多个参数。结构体的作用在于可以同时将顶点坐标、法线向量和第一套UV等传入到顶点着色器,然后再同时输出裁切空间的顶点坐标、世界空间顶点坐标、世界空间法线向量和纹理坐标到片段着色器。 由于函数有多个输入输出,结构体可以让代码编写更加方便,且美观。
结构体的语法
结构体允许存储多个不同类型的变量,并且将多个变量包装成为一个整体进行输入或者输出。 结构体语法如下:
struct Type
{
//变量
};
例如:
struct v2f{
float4 pos:POSITION;
float2 objPos:TEXCOORD0;
};
在vert与frag函数中传入此结构体
struct v2f{
float4 pos:POSITION;
float2 objPos:TEXCOORD0;
};
v2f vert(in float2 objPos:POSITION)
{
v2f v;
v.pos=float4(objPos,0,1);
v.objPos=objPos;
return v;
}
fixed4 frag(in float4 pos: POSITION,in float2 opos:TEXCOORD0):COLOR
{
return float4(opos,0,1);
}
💡 要注意,结构体必须要声明在使用的函数前面,否则将出现如下报错
别名
在声明struct的时候,也可以通过别名的方式,进行声明 给结构体起一个别名,如:
typedef

ABC{
...
}S;
这就为结构体ABC定义了一个别名S。以后写S x;就等价于写struct ABC x 。 例如:
typedef struct {
float4 pos:POSITION;
float2 objPos:TEXCOORD0;
}v2f;
💡 但要注意CG语言当中不像C语言,是不支持指针,所以在别名时,也不能使用*p指针
完整代码
Shader "Learning/CGPROGRAM/vf3"
{
SubShader
{
pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
float Func2(float arr[2])
{
float sum=0;
for (int i=0;i💡 这样的组织方式,可以保证以后要编写的着色器都可以引用cginc文件,不用重复编写结构体
Shader “Learning/CGPROGRAM/vf3” { SubShader { pass{ CGPROGRAM #pragma vertex vert #pragma fragment frag //引入sbin.cginc #include “sbin.cginc” typedef struct { float4 pos:POSITION; float2 objPos:TEXCOORD0; fixed4 col:COLOR; }v2f; v2f vert(appdata_base v) { v2f o; o.pos=float4(v.pos,0,1); o.objPos=float2(1,0); o.col=v.col; return o; } fixed4 frag(v2f IN):COLOR { return IN.col; } ENDCG } } }
## UnityShaderLab中Build in的结构体
包含在UnityCG.cginc当中
struct appdata_base { float4 vertex : POSITION;//顶点,(采用4维向量) float3 normal : NORMAL;//法向量 float4 texcoord : TEXCOORD0;//纹理坐标 UNITY_VERTEX_INPUT_INSTANCE_ID }; //正切, struct appdata_tan { float4 vertex : POSITION; float4 tangent : TANGENT;//切线数据 float3 normal : NORMAL; float4 texcoord : TEXCOORD0; UNITY_VERTEX_INPUT_INSTANCE_ID }; //能够为顶点着色器,提供的所有数据 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 }; ``` 💡 在着色器当中,顶点程序,需要从应用程序中接受它要使用的参数, 其主要内容,就是顶点当中应当包括的信息,为了能够结构话组织数据的输入, 可以利用结构体对数据进行包装,经过结构体对包装之后,就可以在结构体的成员当中使用提供给顶点程序的数据 💡 由于顶点程序向片元程序输出的数据,由于需求目的不同,所以无法在每一个着色器当中去确定哪些是需要用到的数据,于是无法做一个统一的方式写在cginc当中 当然如果在某些特殊的需求下,固定传入的数据的情况下,可以写在自定义的cginc当中


...