1. 概述

在上一篇文章《Unity3D学习笔记6——GPU实例化(1)》详细介绍了Unity3d中GPU实例化的实现,并且给出了详细代码。不过其着色器实现是简单的顶点+片元着色器实现的。Unity提供的很多着色器是表面着色器,通过表面着色器,也是可以实现GPU实例化的。

2. 详论

2.1. 实现

首先,我们还是挂接与上篇文章一样的脚本:

using UnityEngine;

[ExecuteInEditMode]
public class Note7Main : 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 update
void 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 frame
void 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();
}
}
}

不过,脚本的材质设置需要使用我们新的材质:

这个材质可以通过使用Standard Surface Shader作为我们修改的模板:

修改后的着色器代码如下:

Shader "Custom/SimpleSurfaceIntanceShader"
{
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
#pragma target 4.5
#pragma multi_compile_instancing
#pragma instancing_options procedural:setup struct InstanceParam
{
float4 color;
float4x4 instanceToObjectMatrix;
}; #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
StructuredBuffer<InstanceParam> dataBuffer;
#endif float4x4 ObjectToWorld; sampler2D _MainTex; struct Input
{
float2 uv_MainTex;
}; half _Glossiness;
half _Metallic;
fixed4 _Color; void setup()
{
#ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
InstanceParam data = dataBuffer[unity_InstanceID];
unity_ObjectToWorld = mul(ObjectToWorld, data.instanceToObjectMatrix);
#endif
} void surf (Input IN, inout SurfaceOutputStandard o)
{
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _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"
}

最后的显示效果如下:

2.2. 解析

对比修改之前的着色器代码:

  1. #pragma multi_compile_instancing的意思是给这个着色器增加了实例化的变体,也就是增加了诸如INSTANCING_ON PROCEDURAL_ON这样的关键字,可以编译实例化的着色器版本。
  2. #pragma instancing_options procedural:setup是搭配Graphics.DrawMeshInstancedIndirect 使用的,在顶点着色器阶段开始时,Unity会调用冒号后指定的setup()函数。
  3. setup()函数的意思是通过实例化Id也就是unity_InstanceID,找到正确的实例化数据,并且调整Unity的内置变量unity_ObjectToWorld——也就是模型矩阵。正如上一篇文章所言,GPU实例化的关键就在于模型矩阵的重新计算。在Unity API官方示例中,还修改了其逆矩阵unity_WorldToObject。

3. 参考

  1. 《Unity3D学习笔记6——GPU实例化(1)》
  2. Graphics.DrawMeshInstancedIndirect
  3. Declaring and using shader keywords in HLSL
  4. Creating shaders that support GPU instancing

具体实现代码

Unity3D学习笔记7——GPU实例化(2)的更多相关文章

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

    目录 1. 概述 2. 详论 2.1. 自动实例化 2.2. MaterialPropertyBlock 3. 参考 1. 概述 在前两篇文章<Unity3D学习笔记6--GPU实例化(1)&g ...

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

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

  3. unity3d学习笔记(一) 第一人称视角实现和倒计时实现

    unity3d学习笔记(一) 第一人称视角实现和倒计时实现 1. 第一人称视角 (1)让mainCamera和player(视角对象)同步在一起 因为我们的player是生成的,所以不能把mainCa ...

  4. Unity3D学习笔记2——绘制一个带纹理的面

    目录 1. 概述 2. 详论 2.1. 网格(Mesh) 2.1.1. 顶点 2.1.2. 顶点索引 2.2. 材质(Material) 2.2.1. 创建材质 2.2.2. 使用材质 2.3. 光照 ...

  5. 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. 渲染 ...

  6. Unity3D学习笔记4——创建Mesh高级接口

    目录 1. 概述 2. 详论 3. 其他 4. 参考 1. 概述 在文章Unity3D学习笔记2--绘制一个带纹理的面中使用代码的方式创建了一个Mesh,不过这套接口在Unity中被称为简单接口.与其 ...

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

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

  8. 一步一步学习Unity3d学习笔记系1.3 英雄联盟服务器集群架构猜想

    说到了网游那就涉及到服务器了,时下最火的属英雄联盟了,我也是它的粉丝,每周必撸一把,都说小撸怡情,大撸伤身,强撸灰飞烟灭,也告诫一下同仁们,注意身体,那么他的服务器架构是什么呢,给大家分享一下, 具体 ...

  9. Unity3D 学习笔记

    不是什么技术文章,纯粹是我个人学习是遇到一些觉得需要注意的要点,当成笔记. 1.关于调试,在Android下无法断点,Debug也无法查看,查看日志方法可以启动adb的log功能,或者自己写个GUI控 ...

随机推荐

  1. Google广告批量投放脚本

    该脚本文件是一个转化文件,将编辑的Google投广告模板转化成Google批量投放广告的脚本文件,实现批量投放广告,节省投广时间.PS:要与相应的模板结合使用,投广告模板在本人博客的文件中,可以下载. ...

  2. hibernate查询不到关联对象列表-fetchType的选择

    概述 昨天排查问题的时候,发现使用hibernate关联对象时,订单实体类对象死活无法获取关联的订单明细列表 Order order = orderDao.findById(201L); //明明数据 ...

  3. myeclipse10的问题

    1.导入项目后java文件头部出现The type java.lang.CharSequence cannot be resolved. It is indirectly referenced fro ...

  4. 4.18-token验证

    在postman编写的每一个叫测试用例,既然收测试用例,那么就会有结果对比 API测试断言tests(判断一个接口测试用例是否成功,或者说是通过,是根据断言的三个条件都成立的情况下得到的结果) 协议状 ...

  5. 灵感乍现!造了个与众不同的Dubbo注册中心扩展轮子

    hello大家好呀,我是小楼. 作为一名基础组件开发,服务好每一位业务开发同学是我们的义务(KPI). 客服群里经常有业务开发同学丢来一段代码.一个报错,而我们,当然要微笑服务,耐心解答. 有的问题, ...

  6. 使用allatori混淆代码

    引言 为了保密需要,我们开发出来的程序在实际部署之前,需要先将jar包的代码进行混淆,让用户使用反编译也无法获得源码. allatori就是这样一个混淆java jar包代码的工具. 操作步骤 Ste ...

  7. Go语言学习——map

    map 映射关系容器 内部使用散列表(hash)实现 map是引用类型 必须初始化才能使用 无序的基于key-value的数据结构 map定义 map的定义语法: map[KeyType]ValueT ...

  8. 1903021121—刘明伟—Java第三周作业—学习在eclipse上创建并运行java程序

    项目 内容 课程班级博客链接 19信计班(本) 作业要求链接 第三周作业 作业要求 每道题要有题目,代码,截图 扩展阅读 eclipse如何创建java程序 java语言基础(上) 扩展阅读心得: 想 ...

  9. 【Azure Developer】使用Azure PubSub服务示例代码时候遇见了.NET 6.0的代码转换问题

    问题描述 当本地环境中安装.NET 6.0后,用指令 dotnet new web 或  dotnet new console 生成的项目,使用的都是新模板生成的Program.cs文件.里面去掉了n ...

  10. Spring Security之短信登录

    实现短信验证码登录 前面实现了 用户名+密码 的登录方式,现在实现一下短信验证码登录. 开发短信验证码接口 短信验证码和图形验证码类似,用户从手机短信得到验证码和从图片得到验证码类似. 校验短信验证码 ...