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渲染管线对应的代码块所在的位置,这个前后学习不受影响。当然在讲述的过程中也有带着解释渲染流程的概念。