zoukankan      html  css  js  c++  java
  • URP下GPU Instance以及IndirectDraw探究

    今天一个渲染群群友提出了在URP下无法IndirectDraw的问题,直接使用的官方文档的代码。我们知道DrawMeshInstancedIndirect方法在官方文档上有两个支持的shader,一个是表面着色器,一个是顶点片元着色器。而URP下不支持表面着色器,所以群友把顶点片元着色器拿到了URP下使用,结果发现DrawIndirect并不能正确执行。问题出在了哪里呢?

    对于想快速得到答案的小伙伴们,这里先给出结论:

    首先必须要加这一句预编译指令:

    #pragma instancing_options procedural:setup

    setup方法里面是用来处理InstanceID计算出来后去怎么处理数据,如果用不到可以不填充这个方法,但是必须实现。

    void setup()
                {
    
                }

    如上,空着就可以,这个预编译指令主要是用来告诉unity我们需要drawIndirect。

    然后不能直接从SV_InstanceID中拿instanceID,必须先定义在顶点的输入和输出结构中。

    之前错误的代码是这样的:

     Varyings vert(Attributes v,uint instanceID: SV_InstanceID)
                {
                    xxxxx
                }

    而正确的做法是这样的:

    struct Attributes
                {
                    float3 positionOS : POSITION;
                    float2 uv :TEXCOORD0;
            UNITY_VERTEX_INPUT_INSTANCE_ID
                };
    
                // 顶点着色器的输出
                struct Varyings
                {
                    float4 positionCS : SV_POSITION;
                    float2 uv :TEXCOORD0;
                UNITY_VERTEX_INPUT_INSTANCE_ID
                };
    Varyings vert(Attributes v)
                {
                    Varyings o = (Varyings)0;
                    UNITY_SETUP_INSTANCE_ID(v);
                    UNITY_TRANSFER_INSTANCE_ID(v, o);
                    xxxxxxxxxxxxxxxxxxxxx
                    return o;
                }

    然后要获取instanceID可以通过unity_InstanceID拿到。

    结论讲完说一下原因:

    首先第一个预编译指令存在的意义是如果我们只有:

    #pragma multi_compile_instancing

    当IndirectDraw的时候你会发现UNITY_INSTANCING_ENABLED这个keyword是关闭的。而正常情况下用Renderer渲染时是打开的。为什么会这样呢?因为Unity把这两种渲染方式做了区分。

    使用Renderer渲染时当开启GPU Instancing,会激活UNITY_INSTANCING_ENABLED,但是当DrawIndirect的时候,Unity希望我们激活的是UNITY_PROCEDURAL_INSTANCING_ENABLED这个keyword,所以会把UNITY_INSTANCING_ENABLED关闭掉。那么如果我们没有定义上面instancing_options procedural:setup的预编译指令,连UNITY_PROCEDURAL_INSTANCING_ENABLED也不会被激活,所以就无法Instance了。这是为什么必须要这个预编译指令。

    那么这个预编译指令后面的setup方法是用来干什么呢?他提供了一个当InstanceID计算完成后处理数据的阶段。比如如何通过InstanceID去取数据之类的。

    第二个点是为什么不能在方法参数里面直接使用SV_InstanceID的语义,而必须定义在结构里面呢?

    这个就是内置管线和URP的一点区别,目前具体的原因还不太清楚,但是一般我们让自己的shader支持GPU Instance都是通过手动SETUP进行的。

    最后就是关于UNITY_PROCEDURAL_INSTANCING_ENABLED这个宏:

    这个就得看UnityInstancing.hlsl这个文件里面对于UNITY_GET_INSTANCE_ID这个宏的定义:

     #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
            #ifndef UNITY_INSTANCING_PROCEDURAL_FUNC
                #error "UNITY_INSTANCING_PROCEDURAL_FUNC must be defined."
            #else
                void UNITY_INSTANCING_PROCEDURAL_FUNC(); // forward declaration of the procedural function
                #define DEFAULT_UNITY_SETUP_INSTANCE_ID(input)      { UnitySetupInstanceID(UNITY_GET_INSTANCE_ID(input)); UNITY_INSTANCING_PROCEDURAL_FUNC();}
            #endif
        #else
            #define DEFAULT_UNITY_SETUP_INSTANCE_ID(input)          { UnitySetupInstanceID(UNITY_GET_INSTANCE_ID(input));}
        #endif

    可以看到这个宏只能在setup方法里使用,不能在其他方法比如顶点片元使用,那么当我们想判断是否是Instance的情况时,可以使用这个宏来判断:

    UNITY_ANY_INSTANCING_ENABLED

    这个宏是可以用在顶点片元着色器的,只要两种Instance有一个激活了,这个宏就会返回1.

  • 相关阅读:
    [BZOJ5020] [THUWC 2017]在美妙的数学王国中畅游
    [BZOJ3876] [AHOI2014&JSOI2014]支线剧情
    [BZOJ5120] [2017国家集训队测试]无限之环
    [BZOJ2959] 长跑
    [BZOJ2502] 清理雪道
    SSM-MyBatis-08:Mybatis中SqlSession的commit方法为什么会造成事物的提交
    SSM-MyBatis-07:Mybatis中SqlSession的insert和delete底层到底做了什么
    SSM-MyBatis-06:Mybatis中openSession到底做了什么
    SSM-MyBatis-05:Mybatis中别名,sql片段和模糊查询加getMapper
    SSM-MyBatis-04:Mybatis中使用properties整合jdbc.properties
  • 原文地址:https://www.cnblogs.com/shenyibo/p/14295047.html
Copyright © 2011-2022 走看看