GPUInstancing的实现原理
「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战」。
作为一名优秀的开发者,对于技术的探求应该是永无止步的,如何让自己更进步,那就只有不停的学习,不停的充电.而这些都都是说起来容易做起来难,坚持才是难的地方 --蛙哈哈
GPU Instancing 其实是 Unity 对 GPU 渲染命令的上层封装,将它变得简单好用.
在OpenGL中的名字叫做:多实例渲染接口
比如 OpenGL 提供了一些接口:
void glDrawArraysInanced(GLenum mode, GLint first, GLsizei count, Glsizei primCount);//无索引的顶点网格集多实例渲染
void glDrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei primCount);//索引网格的多实例渲染
void glDrawElementsInstancedBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instanceCount, GLuint baseVertex);//索引基于偏移的网格多实例渲染
GPU Instancing 有两个特性:
- 多实例渲染
- 每个实例可以有自己的特色,比如不同的缩放/颜色/位置等等
如何做到多实例的渲染呢?
先看 OpenGL 的接口, 以第一个接口为例:
通过mode、first和count所构成的几何体图元集,绘制 primCount 个实例,置变量 gl_InstanceID 都会依次递增,新的数值会被传递到顶点着色器,以区分不同实例的顶点属性.
这个函数是 glDrawArrays() 的多实例版本.
void glDrawArrays( GLenum mode, GLint first, GLsizei count);
每次 OpenGL调用 glDrawArraysInanced 时,其实调用的是 glDrawArrays 的 primCount 次拷贝, 每次的 mode、first 和 count 参数都是直接传入的, 模型网格数据都是同一个,只要让GPU进行多次绘制就可以了,没有CPU啥事,所以就提高的渲染的效率.
如何做到个性化呢?
再看Untiy的Shader文件, 这个文件是Unity文档中的简单的一个 多实例渲染的Shader,下面会进行解释.
Shader "SimplestInstancedShader"
{
Properties
{
_Color ("Color", Color) = (1, 1, 1, 1)
}
SubShader
{
...
Pass
{
CGPROGRAM
...
#pragma multi_compile_instancing // 开启多实例的变量编译
...
struct appdata
{
float4 vertex : POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID //顶点着色器的 InstancingID定义
};
struct v2f
{
float4 vertex : SV_POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID //片元着色器的 InstancingID定义
};
UNITY_INSTANCING_BUFFER_START(Props)
UNITY_DEFINE_INSTANCED_PROP(float4, _Color)
UNITY_INSTANCING_BUFFER_END(Props)
v2f vert(appdata v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v); //装配 InstancingID
UNITY_TRANSFER_INSTANCE_ID(v, o); //输入到结构中传给片元着色器
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(i); //装配 InstancingID
return UNITY_ACCESS_INSTANCED_PROP(Props, _Color); //提取多实例中的当前实例的Color属性变量值
}
ENDCG
}
}
}
#pragma multi_compile_instancing
告诉着色器生成Instancing变体
UNITY_VERTEX_INPUT_INSTANCE_ID //顶点着色器的 InstancingID定义
之所以能进行个性化绘制,就是着色器每次都知道现在绘制的是哪一个实例, InstancingID 就是实例的编号,是与实例对应的数组的索引.
在UnityInstancing.cginc文件中,可以看到 UNITY_VERTEX_INPUT_INSTANCE_ID 的定义
uint instanceID : SV_InstanceID;
SV_InstanceID的语义就是: 由运行时自动生成的每个实例标识符.
因为篇幅的问题,关于如何使用使用这个InstanceID,可以看下一篇.