1.1.结构体(结构体构成及结构体名称和变量名称的简写含义)
a.结构体语法
结构体允许储存多个不同类型的变量,并将多个变量包装成为一个整体进行输入或者输出。
结构体如下:
struct Type { //变量 _1; //变量 _2; //变量 _3; //变量 _4; }
struct:定义结构体的关键词。
Type:给当前结构体定义一种类型,着色器函数定义输入和输出数据类型时会用到,结构体内包含的变量仍然需要定义数据类型和名称,然后填充对应的语义。最后通过[结构体名称].[变量名称]的语法访问,
例如:v.vertex,表示访问名称 v 的结构体内的vertex变量。
b.结构体应用
大概了解了结构体的使用方法,接下来就把Pass第三篇中的1.3.Shader改写为以结构体作为输入和输出的Shader,代码如下:
Shader "Unlit/结构体应用" { Properties { _MainTex("MainTex", 2D) = "white" {} _MainColor("MainColor",Color) = (1,1,1,1) } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag//这三行已经很熟悉了吧 //定义顶点着色器的输入结构体 struct appdata //appdata 是 ApplicationData的缩写,意思是获取数据。 { float4 vertex : POSITION; float2 uv : TEXCOORD0;//这里把顶点着色器的 输入参数 都写在了结构体内。 }; //定义顶点着色器的输出结构体 struct v2f//v2f中 v代表vertex、2代表 to、f代表fragment。表示从顶点着色器传递到片段着色器的数据。 { float4 position : SV_POSITION; float2 texcoord : TEXCOORD0;//这里把顶点着色器的 输出参数 写在结构体内。 };//这些语义词,可以看前面Pass第二篇的内容。 float4 _MainColor; //在CG中以float4类型再次声明 sampler2D _MainTex; float4 _MainColor_ST; //声明纹理属性变量以及ST变量 void vert(in appdata v,out v2f o)//顶点着色器 输入appdata类型的结构体,定义名称为v(vertex的首字母) { //顶点着色器 输出v2f类型的结构体,定义名称为o(out的首字母) o.position = UnityObjectToClipPos(vertex); //使用公式计算纹理坐标 o.texcoord = uv * _MainColor_ST.xy + _MainColor_ST.zw; } //使用结构体传入传出参数 void frag(in v2f i,out float4 color : SV_TARGET) //顶点着色器经过计算,将v2f结构体传递给片段着色器,于是v2f结构体成了片段着色器的输入结构体 //并且在片段函数中重新定义名称为i(in的首字母)。 { Color = tex2D(_MainTex,i.texcoord) * _MainColor; //经过片段着色器的计算之后最终输出fixed4类型的color。 } ENDCG } } }
1.2.返回结构体的函数
在Pass第一篇里介绍了有返回值的函数,返回函数也可以做到返回结构体,那么话不多说,下面就把无返回值的Shader改写成返回结构体的Shader:
Shader "Unlit/返回结构体的函数" { Properties { _MainTex("MainTex", 2D) = "white" {} _MainColor("MainColor",Color) = (1,1,1,1) } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float4 position : SV_POSITION; float2 texcoord : TEXCOORD0; }; float4 _MainColor; sampler2D _MainTex; float4 _MainColor_ST; v2f vert(appdata v)//顶点着色器定义了返回值的类型v2f的结构体,然后输入appdata类型的结构体,名称为v,这里省略了输入关键词in { v2f o,//在函数内声明v2f的名称为o o.position = UnityObjectToClipPos(v.vertex); o.texcoord = v.uv * _MainColor_ST.xy + _MainColor_ST.zw; return o;//经过顶点着色器的计算之后返回o } fixed4 frag(v2f i) : SV_TARGET // 片段着色器定义了返回类型fixed4,输入类型为v2f的结构体,名称为i { return tex2D(_MainTex,i.texcoord) * _MainColor; } ENDCG } } }
这样写整洁很多,也就是使用结构体储存变量形式更简洁点。
Unity Shader 中Pass相关介绍到第四篇了,自己会看了一遍又一遍,发现还有很多没有涉及到的内容,比如:顶点和向量变换函数、视角向量函数,其他很多辅助函数,Unity中一些常用的包含文件,还有渲染平台的差异性,渲染流水线都没有细致的捋一遍。一时之间可能做不全,后续会跟着一个一个的案例来补齐不足。
接下来需要完整的补上渲染流水线和Shader概念(是Shader不是Unity Shader哈,这两个词是两个东西。),这样看会更清晰点,之所以放在这些代码块介绍之后,是方便理解GPU渲染管线对应的代码块所在的位置,这个前后学习不受影响。当然在讲述的过程中也有带着解释渲染流程的概念。