UnityShader_再探几何着色器

142 阅读4分钟

本文已参与[新人创作礼]活动,一起开启掘金创作之路
继续上一篇UnityShader_初探几何着色器接着往下

不同输出流对应的图像分别是如何的

上一篇文末我们讲到,几何着色器的输出流有三种,分别是:TriangleStream、LineStream、PointStream。 他们对应的成像分别是怎么样的呢? TriangleStream:

[maxvertexcount(3)]
            void geom(triangle v2g input[3],inout TriangleStream<g2f> outStream) {
                for (int i = 0; i < 3; i++) {
                    g2f o = (g2f)0;
                    o.vertex = input[i].vertex;
                    o.uv = input[i].uv;
                    //将一个顶点添加到输出流列表 
                    outStream.Append(o);
                }
                //RestartStrip可以模拟一个primitives list 
                outStream.RestartStrip();
            }

在这里插入图片描述

LineStream:

[maxvertexcount(3)]
            void geom(triangle v2g input[3],inout LineStream<g2f> outStream) {
                for (int i = 0; i < 3; i++) {
                    g2f o = (g2f)0;
                    o.vertex = input[i].vertex;
                    o.uv = input[i].uv;
                    //将一个顶点添加到输出流列表 
                    outStream.Append(o);
                }
                //RestartStrip可以模拟一个primitives list 
                outStream.RestartStrip();
            }

在这里插入图片描述

PointStream:

[maxvertexcount(3)]
            void geom(triangle v2g input[3],inout PointStream<g2f> outStream) {
                for (int i = 0; i < 3; i++) {
                    g2f o = (g2f)0;
                    o.vertex = input[i].vertex;
                    o.uv = input[i].uv;
                    //将一个顶点添加到输出流列表 
                    outStream.Append(o);
                }
                //RestartStrip可以模拟一个primitives list 
                outStream.RestartStrip();
            }

在这里插入图片描述 PS.本来想继续使用Cube的,但是Cube的顶点数太少,在使用PointStream作为输出流的时候,效果不明显,下面还是贴上图,中间实际上是有8个顶点的 在这里插入图片描述

不同的输入流对图像有什么影响呢?

下面以直线作为输出流,分别以triangle、line、point作为输入流看看会有什么不同的结果

//triangle作为输入
void geom(triangle v2g input[3],inout LineStream<g2f> outStream) 
{
                for (int i = 0; i < 3; i++) {
                    g2f o = (g2f)0;
                    o.vertex = input[i].vertex;
                    o.uv = input[i].uv;
                    outStream.Append(o);
                }
                outStream.RestartStrip();
}
//line作为输入
void geom(line v2g input[2],inout LineStream<g2f> outStream) 
{
        for (int i = 0; i < 2; i++) {
            g2f o = (g2f)0;
            o.vertex = input[i].vertex;
            o.uv = input[i].uv;
            outStream.Append(o);
        }
}
//point作为输入
void geom(point v2g input[1],inout LineStream<g2f> outStream) 
{
         g2f o = (g2f)0;
         o.vertex = input[0].vertex;
         o.uv = input[0].uv;
         outStream.Append(o);
}

在这里插入图片描述 从上面可以看出,triangle、line作为输入流都有图像输出,但是网格形式不一样,但是point作为输入流没有图像呈现, 我们再近看 triangle、line 作为输入流网格有什么不同,下图分别是triangle、line作为输入流的网格成像 在这里插入图片描述 在这里插入图片描述 可以明显看出,triangle是以三角形作为基本图形,而line是以线段作为基本图形,至于point为什么不能产生图形,我猜测最大的原因应该是1个点作为输入不足以构成需要2个点才能生成的线, 当用PointStream作为输出流的时候,三种输入流都有图像生成,并且点的排布都不一样。

划重点!我查阅了DX的相关书籍后,上面是这么说的:如果我们选择以GL_TRIANGLES绘制顶点,我们要把输入修饰符设置为triangles,也就是说要依照上一篇我们见到的这张表 在这里插入图片描述

几何着色器的简单应用(简易爆炸效果)

Shader "Geometry/Explode"
{
	Properties
	{
		_MainTex("Texture", 2D) = "white" {}
		_Emission1("Emission1", Color) = (1,1,1,1)
		_Emission2("Emission2", Color) = (1,1,1,1)
		_Height("Height", Range(-1.5, 3)) = 0
		_TotalHeight("Total Height", Float) = 1
		_Strength("Explosion Strenth", Range(0, 10)) = 2
		_Scale("Scale", Range(0, 5)) = 1
	}
		SubShader
	{
		Tags { "RenderType" = "Opaque"}
		Pass
		{
			Tags{"LightMode" = "ForwardBase"}
			Cull Off
			CGPROGRAM
			#pragma vertex vert
			#pragma geometry geom
			#pragma fragment frag
			#include "UnityCG.cginc" 

			struct a2v
			{
				float4 vertex : POSITION;
				float3 normal : NORMAL;
				float2 uv : TEXCOORD0;
			};

			struct v2g
			{
				float4 vertex : POSITION;
				float3 normal : NORMAL;
				float2 uv : TEXCOORD0;
			};

			struct g2f
			{
				float4 pos : SV_POSITION;
				float3 normal : TEXCOORD0;
				float EmissionParam : TEXCOORD1;
				float2 uv : TEXCOORD2;
			};

			sampler2D _MainTex;
			fixed4 _Emission1;
			fixed4 _Emission2;
			float _Height;
			float _TotalHeight;
			float _Strength;
			float _Scale;

            //三向旋转
			float3 randto3D(float3 seed)
			{
				float3 f = sin(float3(dot(seed, float3(127.1, 337.1, 256.2)), dot(seed, float3(129.8, 782.3, 535.3))
				, dot(seed, float3(269.5, 183.3, 337.1))));
				f = -1 + 2 * frac(f * 43785.5453123);
				return f;
			}

            //单向旋转
			float rand(float3 seed)
			{
				float f = sin(dot(seed, float3(127.1, 337.1, 256.2)));
				f = -1 + 2 * frac(f * 43785.5453123);
				return f;
			}

			float3x3 AngleAxis3x3(float angle, float3 axis)
			{
				float s, c;
				sincos(angle, s, c);
				float x = axis.x;
				float y = axis.y;
				float z = axis.z;
				return float3x3(
					x * x + (y * y + z * z) * c, x * y * (1 - c) - z * s, x * z * (1 - c) - y * s,
					x * y * (1 - c) + z * s, y * y + (x * x + z * z) * c, y * z * (1 - c) - x * s,
					x * z * (1 - c) - y * s, y * z * (1 - c) + x * s, z * z + (x * x + y * y) * c
				);
			}

			float3x3 rotation3x3(float3 angle)
			{
				return mul(AngleAxis3x3(angle.x, float3(0, 0, 1)), mul(AngleAxis3x3(angle.y, float3(1, 0, 0)), AngleAxis3x3(angle.z, float3(0, 1, 0))));
			}

			v2g vert(a2v v)
			{
				v2g o;
				o.vertex = v.vertex;
				o.normal = v.normal;
				o.uv = v.uv;
				return o;
			}

			g2f VertexOutput(float3 pos, float3 normal, float2 uv,float param)
			{
				g2f o;
				o.pos = UnityObjectToClipPos(float4(pos, 1));
				o.normal = UnityObjectToWorldNormal(normal);
				o.uv = uv;
				o.EmissionParam = param;
				return o;
			}

			[maxvertexcount(3)]
			void geom(triangle v2g input[3], inout TriangleStream<g2f> triStream)
			{
				float3 p0 = input[0].vertex.xyz;
				float3 p1 = input[1].vertex.xyz;
				float3 p2 = input[2].vertex.xyz;

				float3 n0 = input[0].normal;
				float3 n1 = input[1].normal;
				float3 n2 = input[2].normal;


				float2 uv0 = input[0].uv;
				float2 uv1 = input[1].uv;
				float2 uv2 = input[2].uv;

				//三角面的中心(到三个顶点的距离都相等)
				float3 center = (p0 + p1 + p2) / 3;
				//根据三角面的高度和_Height的高度差
				float offset = (center.y - _Height) * _TotalHeight;

				// 高度差<=0的话 模型不发生碎片化,保持原样
				if (offset < 0)
				{
					triStream.Append(VertexOutput(p0, n0, uv0, -1));
					triStream.Append(VertexOutput(p1, n1, uv1, -1));
					triStream.Append(VertexOutput(p2, n2, uv2, -1));
					triStream.RestartStrip();
					return;
				}

				// 高度差>0的话 模型直接不进行渲染,消失了
				else if (offset > 1)
					return;

				//下面是实现爆炸 扩散效果

				//取一个平滑的偏移量 <=> clamp((offset - 0) / (1 - 0), 0.0, 1.0);
				float ss_offset = smoothstep(0, 1, offset);

				//计算位置偏移 当前三角面的法线方向 * ss_offset * 强度
				float3 translation = (n0 + n1 + n2) / 3 * ss_offset * _Strength;
				//旋转矩阵 下面的rand可以换成randto3D
				float3x3 rotationMatrix = rotation3x3(rand(center.zyx));
				//三角面的缩放尺寸
				float scale = _Scale - ss_offset;

				//计算经过变化后的三个顶点的位置
				float3 t_p0 = mul(rotationMatrix, p0 - center) * scale + center + translation;
				float3 t_p1 = mul(rotationMatrix, p1 - center) * scale + center + translation;
				float3 t_p2 = mul(rotationMatrix, p2 - center) * scale + center + translation;
				//利用两条法向量叉乘可以得到垂直于该面向量的方式
				float3 normal = normalize(cross(t_p1 - t_p0, t_p2 - t_p0));

				//将计算后的三个点加入到输出流当中
				triStream.Append(VertexOutput(t_p0, normal, uv0, ss_offset));
				triStream.Append(VertexOutput(t_p1, normal, uv1, ss_offset));
				triStream.Append(VertexOutput(t_p2, normal, uv2, ss_offset));
				triStream.RestartStrip();
			}

			fixed4 frag(g2f i) : SV_Target
			{
				fixed4 color = tex2D(_MainTex, i.uv);
				//下面3行注释掉就是显示原来的贴图
				//fixed4 color = step(0, i.EmissionParam) * _Emission1 + step(i.EmissionParam, 0) * color;
				//if (i.EmissionParam > 0)
				//color = lerp(color, _Emission2, i.EmissionParam);
				return color;
			}
			ENDCG
		}
	}
		FallBack "Diffuse"
}

在这里插入图片描述 在这里插入图片描述

DX相关文档 有梦想,动力不止!