Unity的GPU Instancing
Unity的GPU Instancing
GPU Instancing可以用来批量绘制大量相同几何结构相同材质的物体,以降低绘制所需的batches。要想在Unity中使用,首先需要至少在shader的某个pass中加上#pragma multi_compile_instancing
。由于instancing的每个物体所需要的绘制数据可能各不相同,因此还需要在shader中传递一个instanceId:
struct VertexData {UNITY_VERTEX_INPUT_INSTANCE_IDfloat4 vertex : POSITION;…
};
UNITY_VERTEX_INPUT_INSTANCE_ID
宏定义如下:
// - UNITY_VERTEX_INPUT_INSTANCE_ID Declare instance ID field in vertex shader input / output struct.
# define UNITY_VERTEX_INPUT_INSTANCE_ID DEFAULT_UNITY_VERTEX_INPUT_INSTANCE_ID#if defined(UNITY_INSTANCING_ENABLED) || defined(UNITY_PROCEDURAL_INSTANCING_ENABLED) || defined(UNITY_STEREO_INSTANCING_ENABLED)#ifdef SHADER_API_PSSL#define DEFAULT_UNITY_VERTEX_INPUT_INSTANCE_ID uint instanceID;#else#define DEFAULT_UNITY_VERTEX_INPUT_INSTANCE_ID uint instanceID : SV_InstanceID;#endif#else#define DEFAULT_UNITY_VERTEX_INPUT_INSTANCE_ID
#endif
其实就是在启用gpu instancing时定义一个instanceID。
除此之外,我们需要在shader的开头部分使用UNITY_SETUP_INSTANCE_ID
宏进行设置:
InterpolatorsVertex MyVertexProgram (VertexData v) {InterpolatorsVertex i;UNITY_INITIALIZE_OUTPUT(Interpolators, i);UNITY_SETUP_INSTANCE_ID(v);i.pos = UnityObjectToClipPos(v.vertex);…
}
UNITY_SETUP_INSTANCE_ID
宏展开如下:
// - UNITY_SETUP_INSTANCE_ID Should be used at the very beginning of the vertex shader / fragment shader,
// so that succeeding code can have access to the global unity_InstanceID.
// Also procedural function is called to setup instance data.
# define UNITY_SETUP_INSTANCE_ID(input) DEFAULT_UNITY_SETUP_INSTANCE_ID(input)#define DEFAULT_UNITY_SETUP_INSTANCE_ID(input) { UnitySetupInstanceID(UNITY_GET_INSTANCE_ID(input)); UnitySetupCompoundMatrices(); }
这个宏主要做了两件事,第一是设置全局的unity_InstanceID
变量,该变量用于索引shader用到的各类内置矩阵(例如object to world)的数组:
void UnitySetupInstanceID(uint inputInstanceID){#ifdef UNITY_STEREO_INSTANCING_ENABLED#if defined(SHADER_API_GLES3)// We must calculate the stereo eye index differently for GLES3// because otherwise, the unity shader compiler will emit a bitfieldInsert function.// bitfieldInsert requires support for glsl version 400 or later. Therefore the// generated glsl code will fail to compile on lower end devices. By changing the// way we calculate the stereo eye index, we can help the shader compiler to avoid// emitting the bitfieldInsert function and thereby increase the number of devices we// can run stereo instancing on.unity_StereoEyeIndex = round(fmod(inputInstanceID, 2.0));unity_InstanceID = unity_BaseInstanceID + (inputInstanceID >> 1);#else// stereo eye index is automatically figured out from the instance IDunity_StereoEyeIndex = inputInstanceID & 0x01;unity_InstanceID = unity_BaseInstanceID + (inputInstanceID >> 1);#endif#elseunity_InstanceID = inputInstanceID + unity_BaseInstanceID;#endif}
第二就是重新定义常用的矩阵:
void UnitySetupCompoundMatrices(){unity_MatrixMVP_Instanced = mul(unity_MatrixVP, unity_ObjectToWorld);unity_MatrixMV_Instanced = mul(unity_MatrixV, unity_ObjectToWorld);unity_MatrixTMV_Instanced = transpose(unity_MatrixMV_Instanced);unity_MatrixITMV_Instanced = transpose(mul(unity_WorldToObject, unity_MatrixInvV));}
注意这里的unity_ObjectToWorld
和unity_WorldToObject
也已经被重新定义过了:
#define unity_ObjectToWorld UNITY_ACCESS_INSTANCED_PROP(unity_Builtins0, unity_ObjectToWorldArray)#define MERGE_UNITY_BUILTINS_INDEX(X) unity_Builtins##X#define unity_WorldToObject UNITY_ACCESS_INSTANCED_PROP(MERGE_UNITY_BUILTINS_INDEX(UNITY_WORLDTOOBJECTARRAY_CB), unity_WorldToObjectArray)inline float4 UnityObjectToClipPosInstanced(in float3 pos){return mul(UNITY_MATRIX_VP, mul(unity_ObjectToWorld, float4(pos, 1.0)));}inline float4 UnityObjectToClipPosInstanced(float4 pos){return UnityObjectToClipPosInstanced(pos.xyz);}#define UnityObjectToClipPos UnityObjectToClipPosInstanced
开启gpu instancing时,这里实际上就是用instanceId去对应的矩阵数组中进行索引。
正是因为每次batch都需要传递给gpu的是矩阵数组而不是矩阵本身,batch的大小需要进行限制,即最多一次只会将有限数量的几何体合并到一个batch进行gpu instancing。unity定义了一个UNITY_INSTANCED_ARRAY_SIZE
宏来表示最大数量的限制。
gpu instancing同样支持阴影和多光源的情况。对于阴影,只需要在shadow caster的pass中加上对应的instancing声明即可:
#pragma multi_compile_shadowcaster
#pragma multi_compile_instancingstruct VertexData {UNITY_VERTEX_INPUT_INSTANCE_ID
};InterpolatorsVertex MyShadowVertexProgram (VertexData v) {InterpolatorsVertex i;UNITY_SETUP_INSTANCE_ID(v);
}
对于多光源的情况,则需要使用延迟渲染路径:
然而,默认的gpu instancing只能支持相同材质,这在使用时会很不方便,有时候可能仅仅想要修改材质的某个属性,例如这里修改不同球体的颜色,会导致instancing失效:
我们可以使用MaterialPropertyBlock
来避免修改颜色时创建出新的材质:
MaterialPropertyBlock properties = new MaterialPropertyBlock();properties.SetColor("_Color", new Color(Random.value, Random.value, Random.value));t.GetComponent<MeshRenderer>().SetPropertyBlock(properties);
为了在shader代码中使用到此属性,需要在instancing buffer中对其定义:
UNITY_INSTANCING_BUFFER_START(InstanceProperties)UNITY_DEFINE_INSTANCED_PROP(float4, _Color)
#define _Color_arr InstanceProperties
UNITY_INSTANCING_BUFFER_END(InstanceProperties)
对宏进行展开,可以发现就是定义了一个包含struct数组的cbuffer,其中struct中定义了我们新增的属性:
#define UNITY_INSTANCING_BUFFER_START(buf) UNITY_INSTANCING_CBUFFER_SCOPE_BEGIN(UnityInstancing_##buf) struct {#define UNITY_INSTANCING_BUFFER_END(arr) } arr##Array[UNITY_INSTANCED_ARRAY_SIZE]; UNITY_INSTANCING_CBUFFER_SCOPE_END#define UNITY_DEFINE_INSTANCED_PROP(type, var) type var;
如果要把vertex shader中使用的instanceId传递到fragment shader,可以使用unity提供的UNITY_TRANSFER_INSTANCE_ID
:
InterpolatorsVertex MyVertexProgram (VertexData v) {InterpolatorsVertex i;UNITY_INITIALIZE_OUTPUT(Interpolators, i);UNITY_SETUP_INSTANCE_ID(v);UNITY_TRANSFER_INSTANCE_ID(v, i);…
}
这个宏定义很简单:
#define UNITY_TRANSFER_INSTANCE_ID(input, output) output.instanceID = UNITY_GET_INSTANCE_ID(input)
那么最终要如何正确读取这个cbuffer的属性呢?这里Unity也提供了配套的宏:
float3 GetAlbedo (Interpolators i) {float3 albedo =tex2D(_MainTex, i.uv.xy).rgb * UNITY_ACCESS_INSTANCED_PROP(_Color_arr, _Color).rgb;...
}
这个宏定义也很简单,就是从之前定义的struct数组中,根据instanceId进行索引,再取出对应的变量:
#define UNITY_ACCESS_INSTANCED_PROP(arr, var) arr##Array[unity_InstanceID].var
经过修改之后,再次运行,可以发现batch降低了,instancing生效了:
如果你觉得我的文章有帮助,欢迎关注我的微信公众号:Game_Develop_Forever
Reference
[1] GPU Instancing
[2] (四)unity自带的着色器源码剖析之——————Unity3D 多例化技术(GUI Instancing)
Unity的GPU Instancing相关推荐
- Unity GPU Instancing的使用尝试
似乎是在Unity5.4中开始支持GPU Instacing,但如果要比较好的使用推荐用unity5.6版本,因为这几个版本一直在改. 这里测试也是使用unity5.6.2进行测试 在5.6的版本里, ...
- unity gpu instancing
Unity gpu instancing unity可以自动合并相同的material对象渲染 将对应shader enable instancing的选项勾上 本文说明一下直接调用unity api ...
- 【Unity游戏开发】静态、动态合批与GPU Instancing
https://zhuanlan.zhihu.com/p/356211912 前言 动态合批与静态合批其本质是对将多次绘制请求,在允许的条件下进行合并处理,减少cpu对gpu绘制请求的次数,达到提高性 ...
- unity SRP Batcher与GPU instancing使用情况
SRP Batcher更合适大量不同的物体, 比如材质上用了不同的贴图.参数.等等,只要shader变种不变,即使不同材质也能合并: GPU instancing 必须同材质同参数,只是可以自定义ma ...
- 每天执行一次批处理_关于静态批处理/动态批处理/GPU Instancing /SRP Batcher的详细剖析...
静态批处理[1] 定义 标明为 Static 的静态物件,如果在使用相同材质球的条件下,在Build(项目打包)的时候Unity会自动地提取这些共享材质的静态模型的Vertex buffer和Inde ...
- 闲云野鹤:吃鸡(一)之场景制作:使用GPU instancing方式制作刷草插件
用GPU instancing方式制作刷草插件(unity版本8.2.2) 先上最终效果图(欢迎加我qq交流:358641634): 十种草 混刷生成比较自然的场景(带阴影.风力.草可见距离可调) 插 ...
- Unity3D GPU Instancing测试
GPU instancing 很早就支持手机了(Android只支持Opengl ES 3.0),最近在调研这个就对它测试了一下. 如果是不动的物体勾选static静态合并批次(40-50帧率) 自定 ...
- Unity Shader - URP Instancing
URP 中的内置 GPU Instancing 的使用,和 Built-in RP 之前的宏定义名字是一样的,而且功能也是一样的,所以:使用方法和 Built-in RP 中没任何却别 Shader ...
- GPU instancing介绍
从Unity5.4开始,多了一个叫做GPU instancing的功能.这个功能是做什么用的呢?下面做一个小例子来说明一下: 还是之前做 MaterialPropertyBlock的例子,在屏幕上面做 ...
最新文章
- 框架警察 fxcop 的规则莫名其妙
- python乘法口诀代码-浅析一句python代码成生九九乘法表
- 多个线程作用于同一个runnable对象
- 第三次学JAVA再学不好就吃翔(part9)--基础语法之键盘录入
- python 生成pdf页面大小_使用具有自定义大小页面和最佳图像分辨率的Reportlab生成PDF...
- 多重句柄怎么处理_golang异常处理详解
- 阶段1 语言基础+高级_1-3-Java语言高级_06-File类与IO流_02 递归_1_递归概念分类注意事项...
- 硬盘数据恢复方法 固态硬盘数据恢复方法
- Spring揭秘——什么是IOC和DI
- LaTeX(Overleaf)写作笔记
- 【学术相关】iccv、cvpr、eccv论文接收率及格式下载(附论文下载)
- RS485通讯协议的应用
- 使用PS把证件照背景变成白色
- 6种摆脱百度竞价恶意点击的技巧
- 原生JS写一个首字母排序的通讯录效果
- 什么是数据标准化?在Python中如何进行数据标准化?「必学」
- switchport mode access
- vite+ts+vue3 知识点(定义全局函数和变量)
- 图像相似度匹配——距离大全
- Maven编译失败: zip file is empty
热门文章
- 网络安全基础知识入门!网络安全学习教程
- x265编码H265
- python box_箱体图Boxplot及Python绘制方
- MySQL数据库修改表某一列数据(一整列)
- ABBYY FineReader OCR图文识别软件如何快速将纸质文档转为电子档教程
- 游戏厂商出海记:韩国内卷严重,其它地区占到什么地盘?
- 使用python对指定手机号获取各网站登录的验证码。
- 性能测试的实施及总结(二)
- 深耕技术,与实践赛跑:一文告诉你如何稳妥快速完善区块链技术并有序推动商用​?...
- python绘制花朵图案_Python实现平行坐标图的绘制(plotly)方式