Unity Geometry Shader零基础指南:从Realistic-Real-Time-Grass-Rendering-With-Unity拆解点扩展为12顶点草叶的完整流程
2026/8/27 15:32:17 网站建设 项目流程

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。

而这个项目的思路完全不同 🌱:

阶段谁负责做什么
CPUC# 脚本只生成「草地种子点」,每根草 =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得到高度,再随机撒点。

三个新手要点:

  1. 草点贴地:每个草点的高度同样查高度图(heightMap.GetPixel(...).grayscale),所以草会自然长在山坡上;
  2. 分块撒草GenerateField(30, 50)把 250×250 的地面划成 30×30 个 30×30 的小块,每块撒 50 根,密度均匀又不呆板;
  3. 按 65000 个点分 MeshSetIndices(..., 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 段竖直四边形):

  1. 先取根点root,用它的世界坐标算一个伪随机数random(基于frac(root.x)frac(root.z)的 sin 值)——无需存储,坐标即种子,让每根草的高度和宽度都不一样:

    • _Width = _Width + random / 50
    • _Height = _Height + random / 5
  2. 进入 12 次循环,偶数下标贴左侧、奇数下标贴右侧

    • 偶数:pos.x = root.x - _Width,UV 为(0, currentV)
    • 奇数:pos.x = root.x + _Width,UV 为(1, currentV),并把currentV += 1/5currentVertexHeight = currentV * _Height

    也就是说每走一对,草叶就「向上长一段」,6 对走完正好是一整片 0→1 的草叶贴图;

  3. 最后用 10 个三角形把 12 个顶点连成四边形条:triStream.Append(v[p], v[p+2], v[p+1]),循环p = 0 … 9

整个草叶的「几何」就诞生于此——不占显存、不占顶点缓冲,纯计算

第 3 步:风力动画——为什么草叶上端摆得更厉害

草叶要「活」,就得随风摆动。项目没有做复杂的布娃娃模拟,而是用了一段非常聪明的近似 🌬️:

  • 基础风windsin(_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 中两个核心参数开箱即调:

参数默认值作用
_Height3草叶基础长度(顶点着色器中还会叠加随机量)
_Width0.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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询