Shader山下(十)表面着色器的顶点函数

时间:2021-10-25 04:37:49

对于每个顶点,GPU都会执行一次顶点函数,顶点函数的任务就是从3D局部空间或者transform得到的顶点转换到2D屏幕。我们可以使用顶点函数修改顶点的一些参数,例如顶点位置、顶点颜色和UV坐标。修改过这些参数之后,会将它们传递给surf函数。

首先我们需要创建一份最简单的漫反射shader:

Shader "Custom/Vertex/Base" {
Properties {
_MainTex ("Albedo (RGB)", 2D) = "white" {}
}
SubShader {
Tags { "RenderType"="Opaque"}
LOD 200

CGPROGRAM
#pragma surface surf Lambert

sampler2D _MainTex;

struct Input {
float2 uv_MainTex;
};

void surf (Input IN, inout SurfaceOutput o) {
fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
}

ENDCG
}
FallBack "Diffuse"
}

修改pragma这一行:

#pragma surface surf Lambert vertex:vert

表明我们要使用名称为vert的顶点方法。

Input中增加:

float4 vertColor;

实现vert方法:

		void vert(inout appdata_full v, out Input o)
{
UNITY_INITIALIZE_OUTPUT(Input, o);
o.vertColor = v.color;
}

我们注意到这个方法的输出o与surf方法的输入IN类型相同,其实就是在顶点为surf方法赋值了输入数据。

如果注释掉UNITY_INITIALIZE_OUTPUT这一行,会报错:

'vert': output parameter 'o' not completely initialized at line 25 (on glcore)

因为Input里面的uv_MainTex还没有初始化。

我们可以看一看appdata_full结构,在UnityCG.cginc里面:

struct appdata_full {
float4 vertex : POSITION;
float4 tangent : TANGENT;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
float4 texcoord1 : TEXCOORD1;
float4 texcoord2 : TEXCOORD2;
float4 texcoord3 : TEXCOORD3;
#if defined(SHADER_API_XBOX360)
half4 texcoord4 : TEXCOORD4;
half4 texcoord5 : TEXCOORD5;
#endif
fixed4 color : COLOR;
};

包含了坐标、切线、法线、uv、uv1、uv2、uv3、(uv4、uv5、)顶点颜色。

所以我们也可以把vert方法这样写:

		void vert(inout appdata_full v, out Input o)
{
o.uv_MainTex = v.texcoord.xy;
o.vertColor = v.color;
}

我们不仅可以修改o,还可以修改作为输入输出值的v。例如我们在Properties里添加:

_NormalYScale ("Normal Y Scale", Range(-2, 2)) = 1

SubShader里添加:

fixed _NormalYScale;
并在vert方法里添加:

v.normal = half3(v.normal.x, v.normal.y * _NormalYScale, v.normal.z);

原效果(Normal Y Scale = 1)

Shader山下(十)表面着色器的顶点函数

Normal Y Scale = 2:

Shader山下(十)表面着色器的顶点函数


Normal Y Scale = 0:

Shader山下(十)表面着色器的顶点函数

Normal Y Scale = -1:

Shader山下(十)表面着色器的顶点函数

Normal Y Scale = -2:

Shader山下(十)表面着色器的顶点函数