文章目录

  • 1. 概述
  • 2. 详论
    • 2.1. 自动实例化
    • 2.2. MaterialPropertyBlock
  • 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 updatevoid 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 framevoid 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 updatevoid 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 framevoid 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 200CGPROGRAM// 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.0sampler2D _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 assumeuniformscalingUNITY_INSTANCING_BUFFER_START(Props)// put more per-instance properties hereUNITY_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 variableso.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. 参考

  1. 《Unity3D学习笔记6——GPU实例化(1)》
  2. 《Unity3D学习笔记6——GPU实例化(2)》
  3. Creating shaders that support GPU instancing
  4. MaterialPropertyBlock

具体实现代码

Unity3D学习笔记8——GPU实例化(3)相关推荐

  1. Unity3D学习笔记6——GPU实例化(1)

    文章目录 1. 概述 2. 详论 3. 参考 1. 概述 在之前的文章中说到,一种材质对应一次绘制调用的指令.即使是这种情况,两个三维物体使用同一种材质,但它们使用的材质参数不一样,那么最终仍然会造成 ...

  2. Unity3D 学习笔记3——了解U3D引擎的操作面板和各种工具

    Unity3D 学习笔记3--了解U3D引擎的操作面板和各种工具 在完成了Unity的安装破解之后,我们接下来要做的当前是启动这个引擎,然后学会如何去使用它为我们提供的各种工具,这也是上手任何一个软件 ...

  3. Unity3D 学习笔记4 —— UGUI+uLua游戏框架

    Unity3D 学习笔记4 -- UGUI+uLua游戏框架 使用到的资料下载地址以及基础知识 框架讲解 拓展热更过程 在这里我们使用的是uLua/cstolua技术空间所以提供的UGUI+uLua的 ...

  4. unity3d 学习笔记四 skybox(天空盒) light(光源) halo(光晕)

    Unity3D学习笔记(四)天空.光晕和迷雾 六年前第一次接触<魔兽世界>的时候,被其绚丽的画面所折服,一个叫做贫瘠之地的地方,深深印在我的脑海里.当时在艾泽拉斯大陆还不能使用飞行坐骑,试 ...

  5. Unity3D学习笔记:粒子特效参数

    Unity3D学习笔记:粒子特效参数含义 转载 https://blog.csdn.net/asd237241291/article/details/8433534 粒子特效 粒子系统检视面板 初始化 ...

  6. Unity3D学习笔记12——渲染纹理

    文章目录 1. 概述 2. 详论 3. 问题 1. 概述 在文章<Unity3D学习笔记11--后处理>中论述了后处理是帧缓存(Framebuffer)技术实现之一:而另外一个帧缓存技术实 ...

  7. Unity3D学习笔记(二、小球滚动吃金币)

    源码:键盘方向键操作小球滚动吃金币Unity3D源码 下篇:Unity3D学习笔记(三.小球跑酷) 一.颜色材质球创建  二.Plane平板创建 三.围墙 同理二,新建Cube,并调整属性,设立围墙 ...

  8. 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. ...

  9. Unity3D学习笔记之九为场景添加细节(二)

                 上节为场景中添加了第一块带有碰撞器的石头,本节我们来利用Prefab,将场景细节都添加进去,并且做的更完善.        这一系列教程以及素材均参考自人人素材翻译组出品的翻 ...

最新文章

  1. 【转】WebApi 身份认证解决方案:Basic基础认证
  2. python中random库_python标准库之random模块
  3. android卫星菜单中间,Android卫星菜单:android-satellite-menu
  4. SMT32H743+CubeMX-配置MPU后,在Keil上的程序卡死
  5. 数据类型oracle,oracle的数据类型
  6. 淄博计算机文化基础试卷,我校顺利举办省高校计算机文化基础考试并获佳绩
  7. 日志的log中如何输出变量_如何在kubernetes中优雅的输出日志
  8. /dev/null脚本中作用
  9. Android将库导入到build.gradle
  10. 洛谷P4012 深海机器人问题(费用流)
  11. php数组中去掉空格,php数组如何去除空格
  12. 下载蓝盒插件_chrome迅雷下载插件-迅雷下载支持插件下载 v3.1官方版--pc6下载站...
  13. 使用daemontools监控zookeeper,storm等进程
  14. 【通俗理解】显著性检验,T-test,P-value
  15. 电驱系列:直流无刷马达(不用芯片,独立元器件搭建)
  16. 【MySql】mysql之用户管理
  17. 不改一行代码,将微信小程序转成商业App?
  18. 春风桃李花开夜,秋雨梧桐叶落时——python面向对象三大特征(封装、继承、多态)
  19. 【OpenCV+Python实现医学影像拼接(一)】
  20. Duang! 工商银行也要做电商?

热门文章

  1. rhce考试试题以及答案_RHCE考试题
  2. Max-Min 带宽公平分配算法
  3. 数据库基本知识、操作
  4. 【Cesium】根据两点坐标获取Heading(朝向),Pitch(俯仰角)和模型矩阵
  5. linux 服务器加装硬盘流程及sda sdb加载顺序
  6. 徐文长传奇李舟楫的博客新浪博客
  7. SCSI 中定义的provision
  8. win 32学习笔记(三) 消息队列
  9. 携程java面经 一二HR面面经
  10. Python中亲和度分析时defaultdict的一种典型算法