文章目录

  • 1. 概述
  • 2. 详论
  • 3. 参考

1. 概述

在之前的文章中说到,一种材质对应一次绘制调用的指令。即使是这种情况,两个三维物体使用同一种材质,但它们使用的材质参数不一样,那么最终仍然会造成两次绘制指令。原因在于,图形工作都是一种状态机,状态发生了变化,就必须进行一次绘制调用指令。

GPU实例化用于解决这样的问题:对于像草地、树木这样的物体,它们往往是数据量很大,但同时又只存在微小的差别如位置、姿态、颜色等。如果像常规物体那样进行渲染,所使用的绘制指令必然很多,资源占用必然很大。一个合理的策略就是,我们指定一个需要绘制物体对象,以及大量该对象不同的参数,然后根据参数在一个绘制调用中绘制出来——这就是所谓的GPU实例化。

2. 详论

首先,我们创建一个空的GameObject对象,并且挂接如下脚本:

using UnityEngine;//实例化参数
public struct InstanceParam
{  public Color color;public Matrix4x4 instanceToObjectMatrix;        //实例化到物方矩阵
}[ExecuteInEditMode]
public class Note6Main : MonoBehaviour
{public Mesh mesh;public Material material;int instanceCount = 200;Bounds instanceBounds;ComputeBuffer bufferWithArgs = null;ComputeBuffer instanceParamBufferData = null;// Start is called before the first frame updatevoid Start(){instanceBounds = new Bounds(new Vector3(0, 0, 0), new Vector3(100, 100, 100));uint[] args = new uint[5] { 0, 0, 0, 0, 0 };bufferWithArgs = new ComputeBuffer(1, args.Length * sizeof(uint), ComputeBufferType.IndirectArguments);int subMeshIndex = 0;args[0] = mesh.GetIndexCount(subMeshIndex);args[1] = (uint)instanceCount;args[2] = mesh.GetIndexStart(subMeshIndex);args[3] = mesh.GetBaseVertex(subMeshIndex);bufferWithArgs.SetData(args);InstanceParam[] instanceParam = new InstanceParam[instanceCount];for (int i = 0; i < instanceCount; i++){   Vector3 position = Random.insideUnitSphere * 5;        Quaternion q =  Quaternion.Euler(Random.Range(0.0f, 90.0f), Random.Range(0.0f, 90.0f), Random.Range(0.0f, 90.0f));float s = Random.value;Vector3 scale = new Vector3(s, s, s);instanceParam[i].instanceToObjectMatrix = Matrix4x4.TRS(position, q, scale);instanceParam[i].color = Random.ColorHSV();}int stride = System.Runtime.InteropServices.Marshal.SizeOf(typeof(InstanceParam));instanceParamBufferData = new ComputeBuffer(instanceCount, stride);instanceParamBufferData.SetData(instanceParam);material.SetBuffer("dataBuffer", instanceParamBufferData);material.SetMatrix("ObjectToWorld", Matrix4x4.identity);}// Update is called once per framevoid Update(){        if(bufferWithArgs != null){         Graphics.DrawMeshInstancedIndirect(mesh, 0, material, instanceBounds, bufferWithArgs, 0);}        }private void OnDestroy(){if (bufferWithArgs != null){bufferWithArgs.Release();}if(instanceParamBufferData != null){instanceParamBufferData.Release();}        }
}

这个脚本的意思是,设置一个网格和一个材质,通过随机获取的实例化参数,渲染这个网格的多个实例:

GPU实例化的关键接口是Graphics.DrawMeshInstancedIndirect()。Graphics对象的一系列接口是Unity的底层API,它是需要每一帧调用的。Graphics.DrawMeshInstanced()也可以实例绘制,但是最多只能绘制1023个实例。所以还是Graphics.DrawMeshInstancedIndirect()比较好。

实例化参数InstanceParam和GPU缓冲区参数bufferWithArgs都是存储于一个ComputeBuffer对象中。ComputeBuffe定义了一个GPU数据缓冲区对象,能够映射到Unity Shader中的 StructuredBuffer中。实例化参数InstanceParam存储了每个实例化对象的位置,姿态、缩放以及颜色信息,通过Material.SetBuffer(),传递到着色器中:

Shader "Custom/SimpleInstanceShader"
{Properties{        }SubShader{Tags{"Queue" = "Geometry"}Pass{ CGPROGRAM#include "UnityCG.cginc" #pragma vertex vert #pragma fragment frag#pragma target 4.5sampler2D _MainTex;float4x4 ObjectToWorld;struct InstanceParam{          float4 color;float4x4 instanceToObjectMatrix;};#if SHADER_TARGET >= 45          StructuredBuffer<InstanceParam> dataBuffer;#endif//顶点着色器输入struct a2v{float4  position : POSITION;float3  normal: NORMAL;float2  texcoord : TEXCOORD0; };//顶点着色器输出struct v2f{float4 position: SV_POSITION;float2 texcoord: TEXCOORD0;float4 color: COLOR;};v2f vert(a2v v, uint instanceID : SV_InstanceID){#if SHADER_TARGET >= 45float4x4 instanceToObjectMatrix = dataBuffer[instanceID].instanceToObjectMatrix;float4 color = dataBuffer[instanceID].color;#elsefloat4x4 instanceToObjectMatrix = float4x4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);float4 color = float4(1.0f, 1.0f, 1.0f, 1.0f);#endiffloat4 localPosition = mul(instanceToObjectMatrix, v.position);//float4 localPosition = v.position;float4 worldPosition = mul(ObjectToWorld, localPosition);                     v2f o;//o.position = UnityObjectToClipPos(v.position);o.position = mul(UNITY_MATRIX_VP, worldPosition);       o.texcoord = v.texcoord;o.color = color;return o;}fixed4 frag(v2f i) : SV_Target {                                                return i.color;                 }ENDCG}}Fallback "Diffuse"
}

这是一个改进自《Unity3D学习笔记3——Unity Shader的初步使用》的简单实例化着色器。实例化绘制往往位置并不是固定的,这意味着Shader中获取的模型矩阵UNITY_MATRIX_M一般是不正确的。因而实例化绘制的关键就在于对模型矩阵的重新计算,否则绘制的位置是不正确的。实例化的数据往往位置比较接近,所以可以先传入一个基准位置(矩阵ObjectToWorld),然后实例化数据就可以只传入于这个位置的相对矩阵(instanceToObjectMatrix)。

最终的运行结果如下,绘制了大量不同位置、不同姿态、不同大小以及不同颜色的胶囊体,并且性能基本上不受影响。

3. 参考

  1. 《Unity3D学习笔记3——Unity Shader的初步使用》
  2. Graphics.DrawMeshInstanced

具体实现代码

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

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

    文章目录 1. 概述 2. 详论 2.1. 自动实例化 2.2. MaterialPropertyBlock 3. 参考 1. 概述 在前两篇文章<Unity3D学习笔记6--GPU实例化(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. android eclipse 环境
  2. python基础教程 pdf github_python基础教程之Jupyter导出PDF从入门到绝望(已解|python基础教程|python入门|python教程...
  3. MySQL 5.5.19 GA 发布 修复多个Bug
  4. 什么是JVM内存模型
  5. 如何让你瞬间拥有百万粉丝 前端F12的那些装X小技巧
  6. BURP 测试出 OPTIONS PUT DELETE TRACE 方法
  7. linux 虚拟机挂载本地,CentOS 在VMWare中挂载本地yum源
  8. 微控制器8位到32位变迁
  9. 金融系统中BER-TLV的解析,更改、增加、删除TAG的实现
  10. 开始我们的Snippets!
  11. day6:vcp考试
  12. MQTT 客户端工具
  13. windows操作系统下新建txt文件快捷键
  14. Q7:难道不想手工搞个环境?
  15. XCTF-MFW Git泄露,命令执行漏洞 详解
  16. java vcard格式_关于vcard 文件数据格式,以备不时之需
  17. android利用多线程加载图片【不使用第三方库】
  18. Ubuntu 16.04 安装GTX 1060 显卡驱动和CUDA 10.2
  19. 米家app扫描不到石头机器人_12月米家剁手清单,第二款冬天必备!
  20. 微信小程序 -语音合成:将文字转为语音(小程序插件:微信同声传译)

热门文章

  1. 拉普拉斯算子的疑惑--拉普拉斯算子作用于矢量和定义Hessian矩阵时遇到的问题
  2. css字体倾斜角度_css怎么设置倾斜的字体样式?(代码详解)
  3. 如何躲避红蜘蛛的控制
  4. Android设置来电号码归属地
  5. 记录极致cms当前位置间隔符号替换
  6. 小白尝试——去除apk流氓权限
  7. Linux高性能服务器架构
  8. platform框架--Linux MISC杂项框架--Linux INPUT子系统框架--串行集成电路总线I2C设备驱动框架--串行外设接口SPI 设备驱动框架---通用异步收发器UART驱动框架
  9. 正常计算机的c盘空间多大,往往C盘剩余空间多少为宜
  10. Java学习历程——JVM的JMM模型