Unity3D学习笔记7——GPU实例化(2)

博客 分享
0 163
张三
张三 2022-07-08 13:01:32
悬赏:0 积分 收藏

Unity3D学习笔记7——GPU实例化(2)

详细介绍了Unity3D中通过表面着色器实现GPU实例化。

目录
  • 1. 概述
  • 2. 详论
    • 2.1. 实现
    • 2.2. 解析
  • 3. 参考

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();        }    }}

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

imglink1

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

imglink2

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

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"}

最后的显示效果如下:
imglink3

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

具体实现代码

posted @ 2022-07-08 12:59 charlee44 阅读(0) 评论(0) 编辑 收藏 举报
回帖
    张三

    张三 (王者 段位)

    821 积分 (2)粉丝 (41)源码

     

    温馨提示

    亦奇源码

    最新会员