Unity3D學(xué)習(xí)筆記7——GPU實(shí)例化(2)
1. 概述
在上一篇文章《Unity3D學(xué)習(xí)筆記6——GPU實(shí)例化(1)》詳細(xì)介紹了Unity3d中GPU實(shí)例化的實(shí)現(xiàn),并且給出了詳細(xì)代碼。不過其著色器實(shí)現(xiàn)是簡單的頂點(diǎn)+片元著色器實(shí)現(xiàn)的。Unity提供的很多著色器是表面著色器,通過表面著色器,也是可以實(shí)現(xiàn)GPU實(shí)例化的。
2. 詳論
2.1. 實(shí)現(xiàn)
首先,我們還是掛接與上篇文章一樣的腳本:
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();
}
}
}
不過,腳本的材質(zhì)設(shè)置需要使用我們新的材質(zhì):

這個(gè)材質(zhì)可以通過使用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. 解析
對(duì)比修改之前的著色器代碼:
#pragma multi_compile_instancing的意思是給這個(gè)著色器增加了實(shí)例化的變體,也就是增加了諸如INSTANCING_ON PROCEDURAL_ON這樣的關(guān)鍵字,可以編譯實(shí)例化的著色器版本。#pragma instancing_options procedural:setup是搭配Graphics.DrawMeshInstancedIndirect使用的,在頂點(diǎn)著色器階段開始時(shí),Unity會(huì)調(diào)用冒號(hào)后指定的setup()函數(shù)。- setup()函數(shù)的意思是通過實(shí)例化Id也就是unity_InstanceID,找到正確的實(shí)例化數(shù)據(jù),并且調(diào)整Unity的內(nèi)置變量unity_ObjectToWorld——也就是模型矩陣。正如上一篇文章所言,GPU實(shí)例化的關(guān)鍵就在于模型矩陣的重新計(jì)算。在Unity API官方示例中,還修改了其逆矩陣unity_WorldToObject。

浙公網(wǎng)安備 33010602011771號(hào)