栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 游戏开发 > 其他

GPUInstance的使用

其他 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

GPUInstance的使用

一.关于GPUInstance 1.用于渲染加速的硬件特性.

gpu硬件支持的一种特性,使用少量的渲染调用(DrawCall)渲染同一网格的多个副本.也就是说在渲染时,他只需要提交一个网格副本,一个材质球,然后在把这些模型对象中不同的属性(比如:位置,大小,旋转,颜色等)提取出来放到一个数组中.

2.底层逻辑,为什么能够加速渲染,提高性能?
  1. 针对同网格同材质的模型多个对象进行渲染时,有一下几种渲染方式:
    1. 原始模式,每个材质和模型都调用一次渲染,这样会导致的后果是,每次渲染一个对象CPU就会向GPU提交一次渲染相关的所有资源.很明显这里面有很多重复资源的提交造成了很大的浪费,也让渲染速度大大降低.
    2. 动态合批模式,这个很好,把最近相同的模型进行合并成一个模型,然后一次提交一次渲染,爽快利落,典型的空间换速度,也很划算,最大化的提高渲染效率.但是,当网格数量和网格的顶点数超过一定数量时,动态合批就不能在进行了因为这个时候就会拉低CPU的执行效率,以及内存空间的暴涨.所以动态合批有较多的限制.
    3. 静态合批模式,这个也很好,离线把模型全部给合并到一个网格(合并后的网格顶点数量有限制:65535),这样能够解决动态合批的问题,但是也有问题,会导致一下几个问题:
      1. 重复网格的副本会被复制很多份,导致资源包体和运行时内存都会有很大的增长.
      2. 一些不需要渲染的顶点也被提交给GPU,增大了GPU的顶点处理器的压力,.
      3. 在逻辑处理上,静态合批也有一个问题,就是合并完后的模型对象不能再被位移,旋转,缩放等处理了.
    4. 然后就是GPUInstance,他也是针对相同模型的网格和材质只提交一次,然后他组合这些模型对象的不同之处打成一个数组提交给GPU,这样既能够减少提交的次数,有不会让内存暴涨.CPU端也不会因为合并网格而各种计算,增加消耗.这样他的特点就出来了:
      1. 在CPU端进行模型裁剪,只合并看见的模型对象.这样提交给GPU渲染的实例会更少更有效.在某些场景下,这个相对于静态批处理来说优势很足.
      2. 只组织和提交模型对象之间的不同之处,内存的消耗也能够控制.这个相对于动静两种合批来说都是优势巨大.
      3. 不需要做网格的合并处理,这方面也CPU没有消耗.这个相对于动态合批来说优势巨大.
      4. 在CPU端的实例不同点的合并处理,需要一定的消耗,这个对于静态批处理来说没有优势.
      5. 在GPU端要增加一个索引和缓存的读取处理,这个也算是额外的消耗,这个对于静态批处理和动态批处理都没有优势.
      6. 因为一个批量渲染,只提交一个网格副本,所以对于网格的大小就没有了限制.适应范围更广泛,这个对于动态合批来说优势巨大.
  2. 综上几个渲染方式的对比之后,在针对这种大量的同网格,.同材质的模型时,数量越大性能提高程度就越明显.

二.适用场景         1.不能被动态合批

                模型顶点数据(位置,法线,切线,UV等)总和不能超过2700个float.

        2.静态合批不划算或者不能静态核批

                模型为一簇簇的出现或者散布广泛

                模型对象位置,大小,旋转等都在变化.

            比如: 

                        草丛,树木丛

                        建筑装饰物件

                        陨石群

三.在U3D里实现 Shader的支持

基础使用

1).使用U3D内置的Shader

  1. 使用Unity自带的能够开启GpuInstance 的Shader(几乎都有,但是也有例外,比如Partcles的材质好像都没有.),创建一个材质球.
  2. 在材质球的Inspector中把Enable GPU Instancing的选项给勾选上. 

                    

     3.把使用这个材质球的模型,复制N多个,然后运行你就会通过一下两个地方来判断是否进行GPUInstance合批.            

                 1).Profiler

                ​​​​​​​      

                 2). FrameDebug ,当出现DrawMesh(instanced),就说明模型被GpuInstance合并.右边窗口点击Preview按钮,切换到模型展示窗口,在这个窗口的右下方你能看到xxx Instances字样,就表示有中一个批次包含多少个网格副本.

 ​​​​​​​        ​​​​​​​        ​​​​    

        ​​​​​​​        ​​​​​​​     

2).自定义Shader使用

第一步:预编译中增加instancing的宏编译.

#pragma multi_compile_instancing

                    

只要加上这一条,那么在对应的材质Inspector上就能看到多一个Enable GPU Instancing的选项.

                   

但是只是这样还不行,还需要进行下面步骤的操作,

第二步:在顶点数据结构的最后,增加UNITY_VERTEX_INPUT_INSTANCE_ID定义

                  

这里需要说明的是,如果你使用Unity内置的顶点数据结构,比如:appdata_base,appdata_full等结构的时候,就忽略这一步吧,因为在内置的数据结构中,都默认添加了上这个宏.

第三步:在顶点程序的最前面增加UNITY_SETUP_INSTANCE_ID ()的宏方法.

                  

第四步:在材质球的Inspector中把EnableGpuInstance的选项给勾选上.

                  

到此,这个自定义的shader就是一个合格的支持GpuInstance的Shader了.材质球也开启了GpuInstance.然后替换上运行通过FrameDebug检验是不是合格.

看下图中左边的DrawMesh(Instancing)表示启用了GpuInstancing合批,右边面板中的Shader信息栏中展示的是,咱们自定义的Shader.这样就表明我们的Shader开启Instancing没有问题了.

                  

3).自定义属性使用

承接上面的,完成在自定义shader中增加GpuInstancing的支持之后.

在上面的材质球中,如果我想改变某个属性怎么办?比如想让更改材质球的Color属性.让每个模型对象都展示不一样的颜色?那么这就需要下面的步骤了.

        1.在顶点到片段的结构体中增加宏: UNITY_VERTEX_INPUT_INSTANCE_ID

                  

注意这个增加的宏,与顶点结构体中增加的宏名字是一样的.

        2.在顶点程序的最后,return 之前,增加一个宏方法: UNITY_TRANSFER_INSTANCE_ID (v,o) 的调用.

                  

        3.在片元程序中增加一个UNITY_SETUP_INSTANCE_ID (i);        

                  

定义InstancingBuffer的结构体,并声明一个数组.

  1. 结构体的位置最好放在v2f结构体的下面.所有程序代码的上面.
  2. 它有固定的定义格式:

UNITY_INSTANCING_BUFFER_START(bufTypeName)      

    UNITY_DEFINE_INSTANCED_PROP(type1,pname1)

    UNITY_DEFINE_INSTANCED_PROP(type2,pname2)

    UNITY_DEFINE_INSTANCED_PROP(type3,pname3)

UNITY_INSTANCING_BUFFER_END(arrName)

我们这里的定义示例如下图:

                  

注意:这里的定义都不需要最后的分号”;”.

           

在顶点或者片元程序中调用InstancingBuffer的内容.

  • 需要使用宏方法UNITY_ACCESS_INSTANCED_PROP(arrName,pname)
  •           

 

到此,自定义材质球属性这一块Shader端全部搞定,现在就要考虑如何为这个属性赋值呢?这就要走到下一步了,

C#脚本端, 针对Instancing的材质,修改InstancingBuffer中定义的属性值.

  • 需要定义一个MaterialPropertyBlock的对象.
  • ​​​​​​​              
  • 定义一个颜色数组,类型使用Vertor4.然后根据逻辑填充颜色.
  •               
  • 然后调用MaterialPropertyBlock的SetVertor4Array来复制.(因为MaterialPropertyBlock的SetXXXArray方法群中没有SetColorArray,我们用SetVertor4Array来代替).             
  •               
  • 然后获取模型对象的Renderer组件,执行组件的方法SetPropertyBlock
  •                                           

UnityInstancing.cginc中定义宏的简单分析

UNITY_VERTEX_INPUT_INSTANCE_ID

                    

                     根据代码分析,这个宏其实就是定义了一个整型变量instanceID.

UNITY_SETUP_INSTANCE_ID(input)

                    

                    根据代码分析,这个宏方法的作用,其实就是把input数据中的instanceID变量经过变换之后赋值给了变量:unity_InstanceID了.之后从InstancingBuffer中获取内容都是通过这个变量来索引读取的.

UNITY_TRANSFER_INSTANCE_ID(input,output)

                    

                我们看到,这个宏的作用其实就是把顶点数据中的instanceID赋值给片段输入数据结构的instanceID.

两对宏定义:

UNITY_INSTANCING_BUFFER_START 和UNITY_INSTANCING_BUFFER_END

以及

UNITY_DEFINE_INSTANCED_PROP和UNITY_ACCESS_INSTANCED_PROP

                    

                    

通过上面代码的分析,我们知道,在上一节中定义的结构,转换成GLES3平台下的结构:  

                    

                    

UNITY_INSTANCED_ARRAY_SIZE

这个宏使用来定义默认的合批最大数量在移动端是250. 其他端是500.

                    

unity预定义的InsancingBuffer的情况.

我们通过这个截图可以看到,有3个PerDraw, 其实Unity针对坐标转换矩阵,Lightmap,SH,等都做了预处理,并对一些Buffer的访问做了封装,.所以很多地方我们调用的才那么方便.   

                    

脚本端支持

默认情况

系统会自动对场景中符合规范的,被开启GpuInstance的对象进行Instance合批处理.

绘制实例化网格的API函数

DrawInstanceMesh:

  1. 一次调用Mesh的数量有限,最大为1023个模型
  2. 材质球必须开启GpuInstance,如果不开启就会出发异常
  3. 属性赋值使用MaterialBlockProperties的Array方法群..

DrawInstanceMeshIndirect:这个函数是放在这里就是看的,他的处理就比较复杂需要单独另开一章进行讲解.

  1. 一次调用没有限制副本数量,
  2. 材质球不需要开启Instance
  3. 执行效率更高

DrawMesh:

    没想到吧,使用这个API, 只要材质球使用同一个,并且材质球开启了GPUInstance也会被自动Instancing合批的.

意外情况
  1. 材质不同或者材质没有被开启GpuInstancing都不会被合批.
  2. UNITY_SETUP_INSTANCE_ID宏调用的位置(不是程序的最前面第一行).可能会导致显示不正确.
  3. 带有光照贴图的对象使用了没有处理光照贴图的材质,这个不会被合批.
  4. 对象以及所有父对象中,只要Scale的分量中包含负值 ,这个不会被合批.
  5. 对象位置深度顺序中包含了其他模型或者材质的对象,这个会产生多个合批.
  6. 带有骨骼蒙皮动画的对象.这个不会被合批.(如果这个使用GPU蒙皮的话,就会被合批处理,针对GPU蒙皮+Instance之后我们再聊.)
总结
  1. 由于Unity的封装,只要掌握前面的几个宏,就能够非常方面的使用.
  2. Unity只是针对视野之内的模型进行Instance合批处理,对于那些遍布场景的同模型同材质的对象优化效果更加友好.

四.软硬件支持 Unity的官方文档描述

1.文档地址:https://docs.unity3d.com/cn/2019.4/Manual/GPUInstancing.html

2.摘自文档:

平台的支持太乱了,如果自己去判断的话基本上会累死的.所以Unity中对于这些平台是否支持提供了一个api.

一句代码搞定: SystemInfo.supportsInstancing

1.让材质球开启GpuInstance

                    

2.使用材质球切换DrawInstanceMesh和DrawMesh

​​​​​​​                    

五.运行和调试环境

    Unity 2018.4.12f1

    Microsoft Visual Studio Professional 2019

六.代码地址

        链接:https://pan.baidu.com/s/1aE3AjUGwide-Mk-f3vihmA   提取码:ngvy

        代码运行截图如下:      

                    ​​​​​​​

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/905042.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号