GPUInstancing的实现原理2

383 阅读2分钟

GPUInstancing的实现原理2

「这是我参与2022首次更文挑战的第5天,活动详情查看:2022首次更文挑战」。

作为一名优秀的开发者,对于技术的探求应该是永无止步的,如何让自己更进步,那就只有不停的学习,不停的充电.而这些都都是说起来容易做起来难,坚持才是难的地方 --蛙哈哈 书接上文 GPUInstancing的实现原理.

在拿到了InstanceID 之后,就要对不同的实例进行个性化的处理.

后面是unity对着三句宏定义的解释

UNITY_INSTANCING_BUFFER_START(Props) // 必须在特殊命名的常量缓冲区中定义每个实例的属性。使用这对宏来包装对每个实例唯一的属性。
    UNITY_DEFINE_INSTANCED_PROP(float4, _Color) //用于根据类型和名称定义每个实例着色器的属性。在此示例中,_Color 属性是唯一的。
UNITY_INSTANCING_BUFFER_END(Props) // 必须在特殊命名的常量缓冲区中定义每个实例的属性。使用这对宏来包装对每个实例唯一的属性。

这三个宏定义的内容是:

 #define UNITY_INSTANCING_BUFFER_START(buf)      UNITY_INSTANCING_CBUFFER_SCOPE_BEGIN(UnityInstancing_##buf) struct {
 #define UNITY_DEFINE_INSTANCED_PROP(type, var)  type var; 
 #define UNITY_INSTANCING_BUFFER_END(arr)        } arr##Array[UNITY_INSTANCED_ARRAY_SIZE]; UNITY_INSTANCING_CBUFFER_SCOPE_END 

image-20220123221813669.png

那么这三句宏定义在比如Direct3D 11 平台上的展开代码为:

cbuffer UnityInstancingProps{
	float4 _Color[500];
}

上面代码使用UNITY_INSTANCING_BUFFER_START宏宣告要使用GPU多例化技术的变量,使用UNITY_DEFINE_INSTANCED_PROP宏声明_Color变量使用技术,使用UNITY_INSTANCING_BUFFER_END宏结束使用技术的宣告。

它定义了一个着色器的常量缓冲区,并定义了要使用的一些变量, 比如_Color, 每个实例都可以设置不同的颜色.

在这个实例真正渲染的时候,着色器根据当前的unity_InstanceID, 去获取到之前存在缓冲区里面的这个id的数据.

fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * UNITY_ACCESS_INSTANCED_PROP(Props, _Color);

如何给每一个实例都设置上不同的颜色呢?

private void Start()
        {
            MaterialPropertyBlock props = new MaterialPropertyBlock();
            MeshRenderer renderer;
            for (int i = 0; i < 10000; i++)
            {
                var go = Instantiate(Go);
                float r = Random.Range(0.0f, 1.0f);
                float g = Random.Range(0.0f, 1.0f);
                float b = Random.Range(0.0f, 1.0f);
                props.SetColor("_Color", new Color(r, g, b));
                renderer = go.GetComponent<MeshRenderer>();
                renderer.SetPropertyBlock(props);
                
                int x = Random.Range(1, 100);
                int y = Random.Range(1, 100);
                int z = Random.Range(1, 100);
                go.transform.position = new Vector3(x, y, z);
            }
            // for (int i = 0; i < 10000; i++)
            // {
            //     var go = Instantiate(Go);
            //     int x = Random.Range(1, 100);
            //     int y = Random.Range(1, 100);
            //     int z = Random.Range(1, 100);
            //     go.transform.position = new Vector3(x, y, z);
            //     float r = Random.Range(0.0f, 1.0f);
            //     float g = Random.Range(0.0f, 1.0f);
            //     float b = Random.Range(0.0f, 1.0f);
            //     var component = go.GetComponent<MeshRenderer>();
            //     component.material.color = new Color(r, g, b);
            // }
        }

直接去修改这个实例上的render的material的color 和去设置renderer的MaterialPropertyBlock是不一样的,后面会再出一节,来分析两者的区别.

image-20220123233143334.png

最终效果 1万个球, batches只有213.