Unity3D学习笔记8——GPU实例化(3)
1. 概述
在前两篇文章《Unity3D学习笔记6——GPU实例化(1)》《Unity3D学习笔记6——GPU实例化(2)》分别介绍了通过简单的顶点着色器+片元着色器,以及通过表面着色器实现GPU实例化的过程。而在Unity的官方文档Creating shaders that support GPU instancing里,也提供了一个GPU实例化的案例,这里就详细论述一下。
2. 详论
2.1. 自动实例化
一个有意思的地方在于,Unity提供的标准材质支持自动实例化,而不用像《Unity3D学习笔记6——GPU实例化(1)》《Unity3D学习笔记6——GPU实例化(2)》那样额外编写脚本和Shader。并且,会自动将transform,也就是模型矩阵作为每个实例的属性。
照例,还是编写一个脚本挂到一个空的GameObject对象上:
using UnityEngine;
public class Note8Main : MonoBehaviour
{
public Mesh mesh;
public Material material;
public int instanceCount = 5000;
// Start is called before the first frame update
void Start()
{
MaterialPropertyBlock props = new MaterialPropertyBlock();
for (int i = 0; i < instanceCount; i++)
{
GameObject go = new GameObject();
go.name = i.ToString();
MeshFilter mf = go.AddComponent<MeshFilter>();
mf.mesh = mesh;
MeshRenderer mr = go.AddComponent<MeshRenderer>();
mr.material = material;
go.transform.position = Random.insideUnitSphere * 5;
go.transform.eulerAngles = new Vector3(Random.Range(0.0f, 90.0f), Random.Range(0.0f, 90.0f), Random.Range(0.0f, 90.0f));
float s = Random.value;
go.transform.localScale = new Vector3(s, s, s);
go.transform.parent = gameObject.transform;
}
}
// Update is called once per frame
void Update()
{
}
}
这个脚本的意思是,给挂接的GameObject下新建很多GameObject,它们使用我们传入的Mesh和Material,但是位置、姿态和大小是随机的。传入的Mesh使用Unity自带的胶囊体,Material使用Unity的标准材质。运行结果如下:
这个时候Unity还没有自动实例化,打开Frame Debug就可以看到:
这个时候我们可以在使用的材质上勾选打开实例化的选项:
再次运行,就会在Frame Debug看到Unity实现了自动实例化,绘制的批次明显减少,并且性能会有所提升:
可以看到确实是自动进行实例化绘制了,但是这种方式却似乎存在实例化个数的上限,所有的实例化数据还是分成了好几个批次进行绘制。与《Unity3D学习笔记6——GPU实例化(1)》《Unity3D学习笔记6——GPU实例化(2)》提到的通过底层接口Graphic进行实例化绘制相比,效率还是要低一些。
2.2. MaterialPropertyBlock
自动实例化只能将transform,也就是模型矩阵作为每个实例的属性。如果需要增加自己的实例属性,就需要使用MaterialPropertyBlock,也就是材质属性块。
修改上面的脚本:
using UnityEngine;
public class Note8Main : MonoBehaviour
{
public Mesh mesh;
public Material material;
public int instanceCount = 5000;
// Start is called before the first frame update
void Start()
{
MaterialPropertyBlock props = new MaterialPropertyBlock();
for (int i = 0; i < instanceCount; i++)
{
GameObject go = new GameObject();
go.name = i.ToString();
MeshFilter mf = go.AddComponent<MeshFilter>();
mf.mesh = mesh;
MeshRenderer mr = go.AddComponent<MeshRenderer>();
mr.material = material;
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));
mr.SetPropertyBlock(props);
go.transform.position = Random.insideUnitSphere * 5;
go.transform.eulerAngles = new Vector3(Random.Range(0.0f, 90.0f), Random.Range(0.0f, 90.0f), Random.Range(0.0f, 90.0f));
float s = Random.value;
go.transform.localScale = new Vector3(s, s, s);
go.transform.parent = gameObject.transform;
}
}
// Update is called once per frame
void Update()
{
}
}
脚本使用的材质,其使用的Shader如下,可以直接在Standard Surface Shader的基础上改:
Shader "Custom/HiddenSurfaceIntanceShader"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
struct Input
{
float2 uv_MainTex;
};
half _Glossiness;
half _Metallic;
//fixed4 _Color;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_DEFINE_INSTANCED_PROP(fixed4, _Color)
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandard o)
{
// Albedo comes from a texture tinted by color
//fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * UNITY_ACCESS_INSTANCED_PROP(Props, _Color);
o.Albedo = c.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
关键的代码在于Unity内置宏UNITY_INSTANCING_BUFFER_START和UNITY_INSTANCING_BUFFER_END、UNITY_DEFINE_INSTANCED_PROP定义了实例化属性,在着色器中,通过内置宏UNITY_ACCESS_INSTANCED_PROP来获取这个属性值。这个实例化属性也就是脚本代码中MaterialPropertyBlock传入的颜色值。
查看Unity Shader源代码,这四个用于实例化的宏封装的是一个cbuffer数组,cbuffer就是hlsl的常量缓冲区:
#define UNITY_INSTANCING_CBUFFER_SCOPE_BEGIN(name) cbuffer name {
#define UNITY_INSTANCING_CBUFFER_SCOPE_END }
#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;
#define UNITY_ACCESS_INSTANCED_PROP(arr, var) arr##Array[unity_InstanceID].var
运行的结果如下:
可以看到除了纹理,每一个胶囊体还获取了随机赋予给材质的颜色,也就是我们设置的颜色成为了实例化属性数据。MaterialPropertyBlock主要由Graphics.DrawMesh和Renderer.SetPropertyBlock使用,在希望绘制具有相同材质,但属性略有不同的多个对象时可使用它。
个人认为使用MaterialPropertyBlock自动实例化性能比不上使用Graphics.DrawMeshInstancedIndirect(),但是它有个优点是实例化的要求没那么高,Graphics.DrawMeshInstancedIndirect()要求使用同一mesh,同一贴图;但是MaterialPropertyBlock没这个要求,只要是同一材质,任何属性不一样都可以用,在减少绘制批次的同时还能减少材质的个数。
3. 参考
- 《Unity3D学习笔记6——GPU实例化(1)》
- 《Unity3D学习笔记6——GPU实例化(2)》
- Creating shaders that support GPU instancing
- MaterialPropertyBlock
Unity3D学习笔记8——GPU实例化(3)的更多相关文章
- Unity3D学习笔记7——GPU实例化(2)
目录 1. 概述 2. 详论 2.1. 实现 2.2. 解析 3. 参考 1. 概述 在上一篇文章<Unity3D学习笔记6--GPU实例化(1)>详细介绍了Unity3d中GPU实例化的 ...
- Unity3D学习笔记6——GPU实例化(1)
目录 1. 概述 2. 详论 3. 参考 1. 概述 在之前的文章中说到,一种材质对应一次绘制调用的指令.即使是这种情况,两个三维物体使用同一种材质,但它们使用的材质参数不一样,那么最终仍然会造成两次 ...
- unity3d学习笔记(一) 第一人称视角实现和倒计时实现
unity3d学习笔记(一) 第一人称视角实现和倒计时实现 1. 第一人称视角 (1)让mainCamera和player(视角对象)同步在一起 因为我们的player是生成的,所以不能把mainCa ...
- Unity3D学习笔记2——绘制一个带纹理的面
目录 1. 概述 2. 详论 2.1. 网格(Mesh) 2.1.1. 顶点 2.1.2. 顶点索引 2.2. 材质(Material) 2.2.1. 创建材质 2.2.2. 使用材质 2.3. 光照 ...
- Unity3D学习笔记3——Unity Shader的初步使用
目录 1. 概述 2. 详论 2.1. 创建材质 2.2. 着色器 2.2.1. 名称 2.2.2. 属性 2.2.3. SubShader 2.2.3.1. 标签(Tags) 2.2.3.2. 渲染 ...
- Unity3D学习笔记4——创建Mesh高级接口
目录 1. 概述 2. 详论 3. 其他 4. 参考 1. 概述 在文章Unity3D学习笔记2--绘制一个带纹理的面中使用代码的方式创建了一个Mesh,不过这套接口在Unity中被称为简单接口.与其 ...
- Unity3D学习笔记12——渲染纹理
目录 1. 概述 2. 详论 3. 问题 1. 概述 在文章<Unity3D学习笔记11--后处理>中论述了后处理是帧缓存(Framebuffer)技术实现之一:而另外一个帧缓存技术实现就 ...
- 一步一步学习Unity3d学习笔记系1.3 英雄联盟服务器集群架构猜想
说到了网游那就涉及到服务器了,时下最火的属英雄联盟了,我也是它的粉丝,每周必撸一把,都说小撸怡情,大撸伤身,强撸灰飞烟灭,也告诫一下同仁们,注意身体,那么他的服务器架构是什么呢,给大家分享一下, 具体 ...
- Unity3D 学习笔记
不是什么技术文章,纯粹是我个人学习是遇到一些觉得需要注意的要点,当成笔记. 1.关于调试,在Android下无法断点,Debug也无法查看,查看日志方法可以启动adb的log功能,或者自己写个GUI控 ...
随机推荐
- 2022最新IntellJ IDEA的zheng开发部署文档
目录 前景提示 一.环境整合 构建工具(参考工具部署方式) 二.git 导入编译器 三.模块描述浅析 四.配置文档 1.总配置 2.数据库配置 3.密码设置 4.配置建议 五.在IDEA中执行MySQ ...
- VMWARE vcenter重置root密码
1\重启VCSA 2\在GNU GRUBc的时候,按住e键,在后面加上一句命令 3.rw init=/bin/bash 4. 按CTRL-X或者按住F10,启动系统 5. 使用passwd命令修改ro ...
- 漏洞复现:MS10-046漏洞
漏洞复现:MS10-046漏洞 实验工具1.VMware虚拟机2.Windows7系统虚拟机3.Kali 2021 系统虚拟机 1.在VMware中打开Windows7虚拟机和Kali 2021虚拟机 ...
- XCTF练习题---MISC---如来十三掌
XCTF练习题---MISC---如来十三掌 flag:flag{bdscjhbkzmnfrdhbvckijndskvbkjdsab} 解题步骤: 1.观察题目,下载附件进行查看 2.打开附件,压根看 ...
- 【前端干货】别再羡慕别人的Excel啦,教你点击按钮直接打开侧边栏!
负责技术支持的葡萄又来啦. 三日不见,我们的客户又为我们发来新的问题. 这次我们需要实现的场景是在前端表格环境中,像模板按钮那样,点击之后弹出一个侧边栏,然后通过点击不同的单元格显示不同的内容. 挤接 ...
- 常用的Linux 系统备份、恢复命令
公众号关注 「开源Linux」 回复「学习」,有我为您特别筛选的学习资料~ 删库跑路的事常常听说,不过,这只能是个调侃的话题,真正的工作中可不能这么干,否则,库是删了,路怕是跑不了了. 所以,备份很重 ...
- 一文搞懂CDN加速原理
开源Linux 长按二维码加关注~ 一.什么是 CDN CDN的全称是(Content Delivery Network),即内容分发网络.其目的是通过在现有的Internet中增加一层新的CACHE ...
- WSL与Windows环境共享
Reference 更多cmd.exe帮助参考 cmd_helps WSL备份及windows Docker安装 WSL安装维护 在使用wsl时,总是需要执行windows的cmd,但是windows ...
- css实现气泡提示框三角及css中drop-shadow的使用
css 做一个弹出气泡,样式怎么设计? 难点: 要实现白色三角型,可以在伪元素before和after上设置一个黑.一个白三角形,白三角形会挡住黑的,从而实现. &::before, & ...
- netty系列之:netty中常用的对象编码解码器
目录 简介 什么是序列化 重构序列化对象 序列化不是加密 使用真正的加密 使用代理 Serializable和Externalizable的区别 netty中对象的传输 ObjectEncoder O ...