升级版GPUInstancing

本文探讨了使用Graphics.DrawMeshInstancedIndirect解决DrawMeshInstancing的痛点,包括超过1023个实例时的drawcall问题和CPU-GPU数据频繁传递的效率问题。通过在shader中利用StructuredBuffer直接获取GPU内存中的实例信息,实现了CPU只需在初始化时传一次数据,后续由GPU托管和更新。文章还介绍了如何在CPU端设置ComputeBuffer,以及如何使用ComputeShader更新物体坐标,实现GPU驱动的动态实例化渲染。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 痛点

昨天在CS的博客笔记里我琢磨了下DrawMeshInstancing,有提到他一次draw最多支持1023个,在多就要加drawcall,此外如果是动态数据还要每帧向GPU索要数据并在CPU端更新网格坐标,因此有如下两个痛点:

  1. 多于1023个坐标就要切分数组,加drawcall,1023*1024个物体就是1024个drawcall
  2. GPU和CPU数据每帧传递浪费时间

下面就有一个新的方法来解决以上痛点,u1s1,百度出来的资料真的是不行,还是要科学冲浪。
api:Graphics.DrawMeshInstancedIndirect
他的原理是将物体的坐标等信息逐mesh的打包成数组传给GPU显存,依据instanceID为数组下标,在shader中直接获取某个mesh的信息(浅见)。
也就是在shader中加一个StructuredBuffer<>并以instanceID为下标来区分不同的mesh draw,属于是CS和shader的缝合怪。
这一下就解放双手了

Shader "Custom/UnlitInstanceIndirectShader"
{
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_instancing nolightmap nodirlightmap nodynlightmap novertexlight
            
            #include "UnityCG.cginc"

            struct MeshProperties 
            {
                float4x4 mat; // world matrix
                float4 color;
            };
            StructuredBuffer<MeshProperties> _Properties;

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;   
                float4 color : COLOR;
            };

            v2f vert(appdata v, uint instanceID : SV_InstanceID)
            {
                v2f o;
                o.vertex = mul(_Properties[instanceID].mat, v.vertex);
                o.vertex = mul(UNITY_MATRIX_VP, o.vertex);
                o.color = _Properties[instanceID].color;
                return o;
            }

            fixed4 frag(v2f o) : SV_Target
            {
                return o.color;
            }
            ENDCG
        }
    }
}

这样,CPU只需要在初始化的时候给GPU传一次数据,然后把全部的工作交给GPU就可以了,由GPU托管数据,由CS来更新数据,由shader渲染数据,完美。

2. 如何使用

CS,shader和C#都要安排,尝试写一个能用一个坐标推开一百万个物体的demo
shader上面已经贴了,下面写C#

2.1 CPU端

Graphics.DrawMeshInstancedIndirect 参数描述
mesh 要绘制的mesh
submeshIndex mesh的子集
material 使用的材质
bounds 包围盒,如果这个包围盒超出视锥,unity会自动取消这次drawcall(不是剔除画出来的某单独的东西),这一堆渲染物体内部的剔除还是要自己做
bufferWithArgs 一个包含5个参数的ComputeBuffer,里面存着一些八股参数
argsOffset 后面都暂时用不着
properties 这个没用了,因为最多就存1023个数据
castShadows
receiveShadows
layer
camera
lightProbeUsage

这个bufferWithArgs一开始我没搞懂是干啥的,后来发现没啥用,跟着官方demo抄就行,参数个个顾名思义,好像没什么修改意义。

public class TestComputeShader : MonoBehaviour
{
   
   
    const int Range = 1024;
    const int Population = Range * Range;

    public Mesh mesh;  // 手拖unity内置mesh
    public Material mat;  // 手拖

    private ComputeBuffer meshPropertiesBuffer;
    private ComputeBuffer argsBuffer;

    public Transform pusher
### Unity 中 GPU Instancing 使用教程 #### 启用材质上的 GPU Instancing 支持 为了使材质支持 GPU Instancing,需在 Shader 文件中添加特定的宏定义。这可以通过编辑现有的着色器来完成: ```csharp #pragma surface surf Standard addshadow fullforwardshadows #pragma multi_compile_instancing ``` 这段代码中的 `#pragma multi_compile_instancing` 声明告诉编译器该材质应该支持实例化渲染[^4]。 #### 设置模型和材质 对于要应用 GPU Instancing 的 GameObjects,确保它们使用的是启用了此功能的材质。此外,这些对象应当拥有相同的 Mesh 和 Material,以便能够被有效地批量处理。如果需要为不同的实例提供独特的外观,则可通过设置每个实例的颜色或其他可变参数来实现这一点[^3]。 #### 编写脚本来管理实例数据 下面是一个简单的 C# 脚本例子,展示了如何在一个场景内创建多个带有不同变换属性的游戏对象,并将它们作为单一批次传递给 GPU 进行渲染: ```csharp using UnityEngine; public class GPUIstancingExample : MonoBehaviour { public Transform prefab; // 需要实例化的预制件 private Matrix4x4[] matrices = new Matrix4x4[10]; void Start() { for (int i = 0; i < matrices.Length; ++i) { Vector3 position = Random.insideUnitSphere * 5f; Quaternion rotation = Random.rotationUniform; matrices[i] = Matrix4x4.TRS(position, rotation, Vector3.one); } Graphics.DrawMeshInstanced(prefab.GetComponent<MeshFilter>().sharedMesh, matrices, prefab.GetComponent<Renderer>().material, 0); } } ``` 上述代码片段实现了如下操作:首先初始化了一个大小固定的数组用于存储各个实例的世界空间转换矩阵;接着循环填充这个数组,其中每一项代表一个新生成游戏对象的位置、方向以及尺度信息;最后调用 `Graphics.DrawMeshInstanced()` 方法一次性提交所有实例的数据至图形管线执行渲染命令[^1]。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值