Unity Shader - Geometry Shader入门

Geometry Shader

过程

  1. vertex阶段输出顶点数据 => geometry阶段
  2. geometry阶段输入顶点数据
  3. 生成一系列顶点数据
  4. 按顺序输出 => fragment阶段

用处

  • 可以用来制作粒子系统
  • 利用点云生成Instance,集群渲染
  • Mesh置换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[maxvertexcount(30)]
void geom(point v2g points[1], inout TriangleStream<g2f> triStream)
{
const int vertexCount = 12;
const float4 root = points[0].pos;
g2f v[vertexCount] = {...};

UNITY_UNROLL
for (int i = 0; i < vertexCount; i++)
{
float4 pos = root + float4(x, y, z, 1);

// 在此阶段进行坐标变换
v[i].pos = UnityObjectToClipPos(pos);
}

UNITY_UNROLL
for (int p = 0; p < (vertexCount - 2); p++)
{
// 定义顶点顺序
triStream.Append(v[p]);
triStream.Append(v[p + 2]);
triStream.Append(v[p + 1]);
}
}

完整代码

GeometryShader.shader
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
Shader "Grass/Grass_GeometryShader"
{
Properties
{
_Height("Grass Height", range(0, 10)) = 3
_Width("Grass Width", range(0, 5)) = 0.1
_Color("Color",Color) = (0,1,0,1)
_NormalSmooth("Normal Smooth", range(0, 1)) = 0.5
}
SubShader
{
Cull off
Tags
{
"Queue" = "AlphaTest" "RenderType" = "TransparentCutout" "IgnoreProjector" = "True"
}

Pass
{
Cull OFF
Tags
{
"LightMode" = "ForwardBase"
}
AlphaToMask On


CGPROGRAM
#include "UnityCG.cginc"
#pragma vertex vert
#pragma fragment frag
#pragma geometry geom
#include "UnityLightingCommon.cginc"

#pragma target 4.0

struct v2g
{
float4 pos : SV_POSITION;
half3 norm : NORMAL;
half2 uv : TEXCOORD0;
};

struct g2f
{
float4 pos : SV_POSITION;
half3 norm : NORMAL;
half2 uv : TEXCOORD0;
};

v2g vert(appdata_full v)
{
v2g o;
o.pos = v.vertex;
o.norm = v.normal;
o.uv = v.texcoord;
return o;
}

float _Height;
float _Width;
half4 _Color;
half _NormalSmooth;

float rand(float2 uv)
{
return frac(sin(dot(uv, float2(12.9898, 78.233))) * 43758.5453);
}

float remap(float x, float oldMin, float oldMax, float newMin, float newMax)
{
return lerp(newMin, newMax, x / (oldMax - oldMin));
}

float remap01(float x, float min, float max)
{
return lerp(min, max, x);
}
[maxvertexcount(30)]
void geom(point v2g points[1], inout TriangleStream<g2f> triStream)
{
const float4 root = points[0].pos;
const int vertexCount = 12;


half random = rand(root.xz);

float angle = random * UNITY_PI * 2;
float faceNormalAngle = angle + UNITY_PI / 2;

half2 direction = half2(cos(angle), sin(angle));
half3 faceNormal = half3(cos(faceNormalAngle), 0, sin(faceNormalAngle));
faceNormal = normalize(lerp(faceNormal, half3(0, 1, 0), _NormalSmooth));

g2f v[vertexCount];
UNITY_UNROLL
for (int i = 0; i < vertexCount; i++)
{
const uint h = (uint)i / 2;

const float length = (1 - h / ((vertexCount - 2) / 2.0)) * _Width / 2;
half2 dir = (fmod(i, 2) == 0) ? direction : -direction;
dir *= length;

const float3 pos = root + float3(dir.x, h * _Height, dir.y) * remap01(random, 0.3, 1.2);

g2f gPoint;
gPoint.pos = UnityObjectToClipPos(pos);
gPoint.norm = faceNormal;
gPoint.uv = half2(i, 0);
v[i] = gPoint;
}

UNITY_UNROLL
for (int p = 0; p < (vertexCount - 2); p++)
{
triStream.Append(v[p]);
triStream.Append(v[p + 2]);
triStream.Append(v[p + 1]);
}
}

half4 frag(g2f IN,fixed facing : VFACE) : COLOR
{
half3 worldNormal = UnityObjectToWorldNormal(IN.norm);
//ads
fixed3 light;
//ambient
fixed3 ambient = ShadeSH9(half4(worldNormal, 1));
//diffuse
fixed3 diffuseLight = saturate(dot(worldNormal, UnityWorldSpaceLightDir(IN.pos))) * _LightColor0;
//specular Blinn-Phong
fixed3 halfVector = normalize(UnityWorldSpaceLightDir(IN.pos) + WorldSpaceViewDir(IN.pos));
fixed3 specularLight = pow(saturate(dot(worldNormal, halfVector)), 15) * _LightColor0;

light = ambient + diffuseLight + specularLight;
return half4(light, 1) * _Color;
// return facing > 0 ? 1 : 0;
// return half4(IN.norm / 2 + 0.5, 1);
}
ENDCG
}
}
}
PointRender.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
[ExecuteInEditMode]
[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
public class PointRender : MonoBehaviour
{
public Material geoMaterial;
private Mesh m_pointMesh;
[Range(0, 5)] public float radius = 1;

void Start()
{
m_pointMesh = CreatePointMesh();

GetComponent<MeshFilter>().mesh = m_pointMesh;
GetComponent<MeshRenderer>().sharedMaterial = geoMaterial;
}

Mesh CreatePointMesh(int num = 100)
{
m_pointMesh = new Mesh();

var verts = new List<Vector3>(num);
var indices = new int[num];

for (int i = 0; i < num; i++)
{
var pos = Random.insideUnitCircle;
verts.Add(new Vector3(pos.x * radius, 0, pos.y * radius));
indices[i] = i;
}

m_pointMesh.vertices = verts.ToArray();
m_pointMesh.SetIndices(indices, MeshTopology.Points, 0);

return m_pointMesh;
}
}

参考


Unity Shader - Geometry Shader入门
https://automask.github.io/wild/2022/06/26/lab/S_Shader_Geometry/
作者
Kyle Zhou
发布于
2022年6月26日
许可协议