


几何着色器的基本概念
几何着色器的作用就是修改网格形成新的形状,然后根据新的形状渲染出不同的效果。
曲面细分着色器是逐顶点操作,而几何着色器的输入是图元,输出也是图元。
它的关键部分代码如下:
#pragma target 4.0
#pragma geometry GS_Main
[maxvertexcount(3)]
void GS_Main(point VS_OUTPUT IN[1], inout TriangleStream<GS_OUTPUT> triStream)
{
/*shader body*/
}
上面这部分代码的解析如下:
1. 着色器目标级别:着色器编译目标级别为4.0以上才能支持几何着色器
2. 着色器名称:几何着色器的名称为GS_Main
3. 顶点上限:[maxvertexcount(num)]表示输出顶点的上限(不是说输出是图元么),如果输出超过20个会导致明显的性能下降
4. 输入类型:void GS_Main ( point VS_OUTPUT IN[1], inout TriangleStream<GS_OUTPUT> triStream ) 中point为输入类型,它的具体类型有:point, line, triangle, lineadj(带有相邻信息的直接,4个点,3条线), triangleadj(带有相邻信息三角形,6个顶点构成)

5. 输出类型:void GS_Main ( point VS_OUTPUT IN[1], inout TriangleStream<GS_OUTPUT> triStream )中TriangleStream为输出类型。输出类型有:

每输出一个点,都需附加到输出流中,比如triStream.Append(o);
对于TriangleStream ,如果需要改变输出图元,需要每输出点足够对应相应的图元后都要RestartStrip()一下再继续构成下一图元,如:triStream.RestartStrip();
现在对于基本概念都了解了,我们来看几个示范Shader。
范例Shader
- LineStream类型的输出
我们可以看到,它将输出的片元组装成了线,然后再输出颜色。

完整代码如下:
Shader &quot;Unlit/GsLineStream&quot;
{
Properties
{
_MainTex(&quot;MainTex&quot;, 2D) = &quot;white&quot; {}
}
SubShader
{
Pass
{
Tags{ &quot;RenderType&quot; = &quot;Opaque&quot; }
LOD 200
CGPROGRAM
#pragma target 4.0
#pragma vertex VS_Main
#pragma geometry GS_Main
#pragma fragment FS_Main
#include &quot;UnityCG.cginc&quot;
uniform sampler2D _MainTex;
struct v2g
{
float4 pos : POSITION;
float3 normal : NORMAL;
float2 uv : TEXCOORD0;
};
struct g2f
{
float4 pos : POSITION;
float3 normal : NORMAL;
float2 uv : TEXCOORD0;
};
// Vertex Shader
v2g VS_Main(appdata_base v)
{
v2g o = (v2g)0;
o.pos = UnityObjectToClipPos(v.vertex);
o.normal = v.normal;
o.uv = v.texcoord;
return o;
}
// Geometry Shader
[maxvertexcount(3)]
void GS_Main(triangle v2g p[3], inout LineStream&lt;g2f&gt; lStream)
{
for (int i = 0; i &lt; 3; i++)
{
g2f o = (g2f)0;
o.pos = p[i].pos;
o.normal = p[i].normal;
o.uv = p[i].uv ;
//将每个顶点添加到LineStream流里
lStream.Append(o);
}
}
// Fragment Shader
float4 FS_Main(g2f i) : COLOR
{
return fixed4(1,1,0,1);
}
ENDCG
}
}
}
- TriangleStream类型的输出
我们可以看到三角形输出和普通的片元着色器没有任何区别,这是因为

完整代码如下:
Shader &quot;Unlit/GsTriStream&quot;
{
Properties
{
_MainTex(&quot;MainTex&quot;, 2D) = &quot;white&quot; {}
_Size(&quot;Size&quot;,Range(0,0.2))=0.01
}
SubShader
{
Cull Off
Pass
{
Tags{ &quot;RenderType&quot; = &quot;Opaque&quot; }
LOD 200
CGPROGRAM
#pragma target 4.0
#pragma vertex VS_Main
#pragma geometry GS_Main
#pragma fragment FS_Main
#include &quot;UnityCG.cginc&quot;
uniform sampler2D _MainTex;
struct v2g
{
float4 pos : POSITION;
float3 normal : NORMAL;
float2 uv : TEXCOORD0;
};
struct g2f
{
float4 pos : POSITION;
float3 normal : NORMAL;
float2 uv : TEXCOORD0;
};
// Vertex Shader
v2g VS_Main(appdata_base v)
{
v2g o = (v2g)0;
o.pos = UnityObjectToClipPos(v.vertex);
o.normal = v.normal;
o.uv = v.texcoord;
return o;
}
float _Size;
// Geometry Shader
[maxvertexcount(3)]
void GS_Main(triangle v2g p[3], inout TriangleStream&lt;g2f&gt; tStream)
{
for (int i = 0; i &lt; 3; i++)
{
g2f o = (g2f)0;
o.pos = p[i].pos;
o.normal = p[i].normal;
o.uv = p[i].uv ;
//将每个顶点添加到TriangleStream流里
tStream.Append(o);
}
//添加三角面
//每输出点足够对应相应的图元后
//都要RestartStrip()一下再继续构成下一图元
tStream.RestartStrip();
}
// Fragment Shader
float4 FS_Main(g2f i) : COLOR
{
half3 lDirWs = _WorldSpaceLightPos0.xyz;
half3 nDirWs = normalize(UnityObjectToWorldNormal(i.normal));
half nDotL = max(0,dot(lDirWs,nDirWs));
return nDotL;
}
ENDCG
}
}
} 现在对于几何着色器的基本内容我们了解了,我们再来看一个稍微复杂一点的应用。
- 几何变换后输出

完整代码如下:
Shader &quot;Unlit/GsTriStream&quot;
{
Properties
{
_MainTex (&quot;Texture&quot;, 2D) = &quot;white&quot; {}
_Length(&quot;Length&quot;,float) = 1.0
_Color(&quot;Color&quot;,Color)=(1,1,1,1)
}
SubShader
{
Tags { &quot;RenderType&quot; = &quot;Opaque&quot; }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//几何着色器
#pragma geometry geom
#include &quot;UnityCG.cginc&quot;
float _Length;
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _Color;
struct a2v {
float4 vertex:POSITION;
float2 uv:TEXCOORD0;
};
struct v2g {
float4 vertex:POSITION;
float2 uv:TEXCOORD0;
};
struct g2f {
float4 vertex:SV_POSITION;
float2 uv:TEXCOORD0;
float4 col:COLOR;
};
v2g vert(a2v v) {
v2g o;
o.vertex = v.vertex;
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
//一个三角面生成的最多顶点数
[maxvertexcount(9)]
void geom(triangle v2g IN[3], inout TriangleStream&lt;g2f&gt; tristream) {
g2f o;
//用两条边算出法线方向
float3 edgeA = IN[1].vertex - IN[0].vertex;
float3 edgeB = IN[2].vertex - IN[0].vertex;
float3 normalFace = normalize(cross(edgeA, edgeB));
//三角面中心点
float3 centerPos = (IN[0].vertex + IN[1].vertex + IN[2].vertex) / 3;
//中心点uv位置
float2 centerTex = (IN[0].uv + IN[1].uv + IN[2].uv) / 3;
//外拓的顶点距离
centerPos += float4(normalFace, 0)*_Length;
// i=3是因为要1个三角面变成了3个三角面
for (uint i = 0; i &lt; 3; i++)
{
//三角面顶点1
o.vertex = UnityObjectToClipPos(IN[i].vertex);
o.uv = IN[i].uv;
//三角形底部颜色为黑色
o.col = fixed4(0, 0, 0, 1);
//添加顶点
tristream.Append(o);
uint index = (i + 1) % 3;
// 三角面顶点2
o.vertex = UnityObjectToClipPos(IN[index].vertex);
o.uv = IN[index].uv;
//三角形底部颜色为黑色
o.col = fixed4(0, 0, 0, 1);
tristream.Append(o);
// 三角面顶点3
o.vertex = UnityObjectToClipPos(float4(centerPos, 1));
o.uv = centerTex;
//三角形顶点颜色为白色
o.col = fixed4(1.0, 1.0, 1.0, 1.);
tristream.Append(o);
//添加三角面
tristream.RestartStrip();
}
}
float4 frag(g2f i) :SV_Target
{
float4 col = tex2D(_MainTex,i.uv)*i.col*_Color;
return col;
}
ENDCG
}
}
} 好,关于几何着色器的解析就到这里了,接下来完成综合作业