Unity Geometry Shader零基础指南:从Realistic-Real-Time-Grass-Rendering-With-Unity拆解点扩展为12顶点草叶的完整流程
【免费下载链接】Realistic-Real-Time-Grass-Rendering-With-UnityRealistic Real Time Grass Rendering With Unity. Using the Geometry Shader to generate millions of grass on GPU. ref:Edward Lee项目地址: https://gitcode.com/gh_mirrors/re/Realistic-Real-Time-Grass-Rendering-With-Unity
这是一份面向新手的 Unity Geometry Shader 零基础教程。我们将基于开源项目Realistic-Real-Time-Grass-Rendering-With-Unity,完整拆解如何用一张「点云贴图」+ 一段几何着色器,在 GPU 上把 1 个顶点实时扩展成 12 个顶点的草叶,最终渲染出数百万根随风摇曳的草——CPU 端几乎零开销。
为什么 Geometry Shader 能渲染百万根草?
先建立一个直觉:传统渲染中,每一片草叶都是独立的网格(顶点、法线、UV、三角形索引),100 万根草意味着天文数字的顶点数据和海量 Draw Call。
而这个项目的思路完全不同 🌱:
| 阶段 | 谁负责 | 做什么 |
|---|---|---|
| CPU | C# 脚本 | 只生成「草地种子点」,每根草 =1 个点 |
| GPU 顶点着色器 | HLSL | 几乎原样转发点的位置、法线、UV |
| GPU 几何着色器 | HLSL | 核心:把 1 个点扩展成 12 个顶点的细长四边形条,并施加风力动画 |
| GPU 片元着色器 | HLSL | 采样草叶贴图、Alpha 裁剪、光照计算 |
一句话总结:草的「身体」不存,只存「根部坐标」,几何体在 GPU 里现算。这就是它能轻松撑到数百万根的原因。
项目要求 Unity 5.6+,本地搭建可用以下命令获取仓库:
git clone https://gitcode.com/gh_mirrors/re/Realistic-Real-Time-Grass-Rendering-With-Unity
项目结构速览:3 个文件撑起整套草渲染
整个系统非常精简,核心就三块,建议对照项目目录逐个看:
Assets/Script/GrassSpanner.cs— CPU 端:生成高度场地形 + 撒下草种子点Assets/Shader/GrassGeometryShader.shader— GPU 端:Geometry Shader 点扩 12 顶点 + 风力 + 光照Assets/Mat/GrassMat.mat— 材质球:绑定草叶贴图、高度、宽度参数
辅助资源:Assets/Texture/Heightmap.png(地形高度图)、Assets/Texture/grassBlade.png(草叶贴图)、Assets/Texture/grassBladeAlpha.png(Alpha 遮罩)。
第 1 步:CPU 撒「种子点」——高度场地形与草点生成
打开 GrassSpanner.cs,Start()里只做两件事:GenerateTerrain()和GenerateField(30, 50)。
高度图即地形。Heightmap.png是一张 1024×1024 的灰度图,灰度值直接代表海拔。地形网格逐像素取样灰度 ×terrainHeight得到高度,再随机撒点。
三个新手要点:
- 草点贴地:每个草点的高度同样查高度图(
heightMap.GetPixel(...).grayscale),所以草会自然长在山坡上; - 分块撒草:
GenerateField(30, 50)把 250×250 的地面划成 30×30 个 30×30 的小块,每块撒 50 根,密度均匀又不呆板; - 按 65000 个点分 Mesh:
SetIndices(..., MeshTopology.Points, 0)把拓扑设为「点」,每 65000 个点拆一个grassLayer物体(Unity 2017.3 之前索引缓冲是 16 位,65535 是上限,留了余量)。
CPU 的工作到此结束——场景里没有任何草的形状数据。
第 2 步:Geometry Shader 把 1 个点扩展成 12 顶点草叶
打开 GrassGeometryShader.shader,先注意两个「入场券」:
#pragma target 4.0— Geometry Shader 需要 Shader Model 4.0(DirectX 11 / OpenGL 3.2+);[maxvertexcount(30)]— 向驱动声明「我一次最多吐出 30 个顶点」(实际用 12 个,留了余量)。
几何入口的签名值得逐字看懂:
void geom(point v2g points[1], inout TriangleStream<g2f> triStream)point表示输入图元是点,points[1]就是那 1 个根点;triStream是输出流,往里 Append 顶点就会自动组成三角形。
Unity草渲染项目中草叶与落叶纹理材质效果预览
12 个顶点是这样摆出来的(草叶 = 6 段竖直四边形):
先取根点
root,用它的世界坐标算一个伪随机数random(基于frac(root.x)、frac(root.z)的 sin 值)——无需存储,坐标即种子,让每根草的高度和宽度都不一样:_Width = _Width + random / 50_Height = _Height + random / 5
进入 12 次循环,偶数下标贴左侧、奇数下标贴右侧:
- 偶数:
pos.x = root.x - _Width,UV 为(0, currentV) - 奇数:
pos.x = root.x + _Width,UV 为(1, currentV),并把currentV += 1/5、currentVertexHeight = currentV * _Height
也就是说每走一对,草叶就「向上长一段」,6 对走完正好是一整片 0→1 的草叶贴图;
- 偶数:
最后用 10 个三角形把 12 个顶点连成四边形条:
triStream.Append(v[p], v[p+2], v[p+1]),循环p = 0 … 9。
整个草叶的「几何」就诞生于此——不占显存、不占顶点缓冲,纯计算。
第 3 步:风力动画——为什么草叶上端摆得更厉害
草叶要「活」,就得随风摆动。项目没有做复杂的布娃娃模拟,而是用了一段非常聪明的近似 🌬️:
- 基础风:
wind由sin(_Time.x * ...)叠加多频率正弦波构成,再按根点坐标偏移相位,避免全场草同步摆动; - 随机扰动:
randomAngle/randomMagnitude给每根草一个固定的个体化风偏,草群更自然; - 关键一行:
windCoEff初始为 0,每到一个奇数顶点就windCoEff += offsetV(即 0、0.2、0.4…1.0)。
位移量 =wind * windCoEff,根部系数为 0(纹丝不动),叶尖系数为 1(摆幅最大)——越往上摆得越大,一行参数就模拟出了草叶的物理感。
第 4 步:片元着色器——AlphaTest 裁剪与 Blinn-Phong 光照
frag阶段做的是标准光照流水线:
- 采样
_MainTex(草叶颜色)+_AlphaTex(Alpha 通道),配合 Pass 里的AlphaToMask On和材质队列AlphaTest,把透明部分裁掉,得到镂空草叶; ShadeSH9取环境光 +saturate(dot(N, L))漫反射 + Blinn-Phong 高光,三者和color.rgb相乘输出。
材质 GrassMat.mat 中两个核心参数开箱即调:
| 参数 | 默认值 | 作用 |
|---|---|---|
_Height | 3 | 草叶基础长度(顶点着色器中还会叠加随机量) |
_Width | 0.05 | 草叶基础宽度(range 0~0.1) |
想调「高瘦草」或「矮胖草」,改这两个数即可,无需碰代码。
新手常见坑:Geometry Shader 草渲染的 4 个注意点
- ⚠️平台限制:Geometry Shader 需要 SM 4.0,部分老手机/低端 WebGL 环境会走 fallback 不显示草,真机测试时留意;
- ⚠️65000 点的分块是 16 位索引缓冲的历史限制,Unity 2017.3+ 支持 32 位索引后可以放宽;
- ⚠️
Cull OFF别删:草叶是双面薄片,关闭双面裁剪,否则背对镜头的草叶会消失; - ⚠️
[maxvertexcount]别小于实际输出顶点数,否则驱动会直接丢弃图元,屏幕上一片空白且很难排查。
零基础总结:这条管线你学会了吗?
把整条流程串起来就是一句话:CPU 撒点 → 顶点着色器转发 → 几何着色器「点变草」→ 片元着色器上色。你只需要记住这个项目的精髓——把「形状生成」从 CPU 的模型数据挪到 GPU 的几何着色器里,用坐标哈希出随机性,用一行windCoEff换来整片草海的风声。掌握这套「点扩展」思路后,替换贴图与参数,你就能快速做出芦苇、头发、雪花等一切细长物体的海量实时渲染。
【免费下载链接】Realistic-Real-Time-Grass-Rendering-With-UnityRealistic Real Time Grass Rendering With Unity. Using the Geometry Shader to generate millions of grass on GPU. ref:Edward Lee项目地址: https://gitcode.com/gh_mirrors/re/Realistic-Real-Time-Grass-Rendering-With-Unity
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考