在上一个Shader中我们只使用了最基本的参数,这次我们将从系统中获取更多的参数来进行说明.
Shader "Unlit/SimpleShader2"
{
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct a2v{
float4 vertexs : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f{
float4 pos : SV_POSITION;
fixed3 color : COLOR0;
};
v2f vert (a2v i)
{
v2f o;
o.pos = UnityObjectToClipPos(i.vertexs);
o.color = i.normal;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
return fixed4(i.color, 1.0);
}
ENDCG
}
}
}
这个shader与上次不同的是定义了两个结构.
struct a2v{
float4 vertexs : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
a2v的结构定义了哪些数据会传递给顶点着色器.这个结构必须定义好语义,Unity才会知道需要把哪些数据传递给顶点着色器.这个阶段的语义有
POSITION 模型空间中的顶点位置, float4
NORMAL 顶点法线,float3
TANGENT 顶点切线,float4
TEXCOORDn(TEXCOORD0, TEXCOORD1) 顶点坐标纹理,如uv1,uv2等.通常是float2或float4
COLOR 顶点颜色,通常是fixed4或float4
所以我们在例子中传入了顶点的位置POSITION,顶点的法线NORMAL和一套模型的uv.
struct v2f{
float4 pos : SV_POSITION;
fixed3 color : COLOR0;
};
v2f结构则定义了从顶点着色器传递到片元着色器的数据.在传递到片元着色器的数据中,SV_POSITION参数是必须的,否则渲染器将无法得到裁剪空间中的顶点坐标,也就无法把顶点渲染到屏幕上.
这个阶段的语义通常有:
SV_POSITION 裁剪空间中的顶点坐标,必须包含的语义
COLOR0 通常用于第一组颜色输出
COLOR1 通常用于第二组颜色输出
TEXCOORD0~TEXCOORD7 通常用于输出纹理坐标uv
所以本例中我们从顶点着色器传递到片元着色器的参数有裁剪空间下的顶点坐标和一组color数据.
需要注意的是顶点着色器是逐顶点调用的,而片元着色器是逐片元调用的.片元着色器的输入是顶点着色器的输出经过插值的结果.
v2f vert (a2v i)
{
v2f o;
o.pos = UnityObjectToClipPos(i.vertexs);
o.color = i.normal;
return o;
}
在这个例子中,顶点着色器的输出是我们自定义的结构数据v2f,输入是我们定义的结构数据a2v.
在函数中我们首先定义了需要输出的结构v2f o.接着我们给o.pos赋值,把模型空间中的坐标转换到裁剪空间中.然后我们把顶点的法线传给了o.color.然后把填充了数据的v2f传递给片元着色器.
fixed4 frag (v2f i) : SV_Target
{
return fixed4(i.color, 1.0);
}
在片元着色器中,我们直接把传来的经过插值的color直接输出,即直接把顶点的法线数据作为颜色进行输出.
下面是在unity中的效果:
在cube中,在同一个面上的顶点法线都是同一个方向,所以cube的每一个面都是一个颜色.模型的forward的法线是(0,0,1),所以他的前面输出了蓝色,对应的右面(1,0,0)红色,上面(0,1,0)绿色.
而sphere中,每个面的法线都朝着不同的方向,而顶点着色器的输出经过插值传递给片元着色器,所以球体的颜色被均匀的插值输出,出现了连续的颜色效果.