1/3
2/3
3/3
[笔记]TA百人计划-图形3.3 几何着色器
艺术菌毯
编辑于 2022年01月27日 12:22
收录于文集
共26篇
  • 几何着色器的基本概念

        几何着色器的作用就是修改网格形成新的形状,然后根据新的形状渲染出不同的效果。

        曲面细分着色器是逐顶点操作,而几何着色器的输入是图元,输出也是图元。

        它的关键部分代码如下:

代码块
JavaScript
自动换行
复制代码
#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类型的输出

        我们可以看到,它将输出的片元组装成了线,然后再输出颜色。

        完整代码如下:

代码块
JavaScript
自动换行
复制代码
Shader "Unlit/GsLineStream"
{
	Properties
	{
		_MainTex("MainTex", 2D) = "white" {}
	}

	SubShader
	{
		Pass
		{
			Tags{ "RenderType" = "Opaque" }
			LOD 200

			CGPROGRAM
			#pragma target 4.0
			#pragma vertex VS_Main
			#pragma geometry GS_Main
			#pragma fragment FS_Main
			#include "UnityCG.cginc"

			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<g2f> lStream)
			{
				for (int i = 0; i < 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类型的输出

        我们可以看到三角形输出和普通的片元着色器没有任何区别,这是因为

        完整代码如下:

代码块
JavaScript
自动换行
复制代码
Shader "Unlit/GsTriStream"
{
	Properties
	{
		_MainTex("MainTex", 2D) = "white" {}
		_Size("Size",Range(0,0.2))=0.01
	}

	SubShader
	{
		Cull Off

		Pass
		{
			Tags{ "RenderType" = "Opaque" }
			LOD 200

			CGPROGRAM
			#pragma target 4.0
			#pragma vertex VS_Main
			#pragma geometry GS_Main
			#pragma fragment FS_Main
			#include "UnityCG.cginc"

			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<g2f> tStream)
			{
				for (int i = 0; i < 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
		}
	}
}
复制成功

        现在对于几何着色器的基本内容我们了解了,我们再来看一个稍微复杂一点的应用。

- 几何变换后输出

        完整代码如下:

代码块
JavaScript
自动换行
复制代码
Shader "Unlit/GsTriStream"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_Length("Length",float) = 1.0
		_Color("Color",Color)=(1,1,1,1)
	}
	SubShader
	{
		Tags { "RenderType" = "Opaque" }
		LOD 100

		Pass
		{
			CGPROGRAM

			#pragma vertex vert
			#pragma fragment frag
			//几何着色器
			#pragma geometry geom
			#include "UnityCG.cginc"

			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<g2f> 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 < 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
		}
	}
}
复制成功

        好,关于几何着色器的解析就到这里了,接下来完成综合作业