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

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

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

目录

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

标签: Unity3D , 实例化


相关实践学习
部署Stable Diffusion玩转AI绘画(GPU云服务器)
本实验通过在ECS上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。
相关文章
|
24天前
|
监控 异构计算
Jetson 学习笔记(八):htop查看CPU占用情况和jtop监控CPU和GPU
在NVIDIA Jetson平台上使用htop和jtop工具来监控CPU、GPU和内存的使用情况,并提供了安装和使用这些工具的具体命令。
83 0
|
3月前
|
缓存 图形学
Unity3D学习笔记12——渲染纹理
Unity3D学习笔记12——渲染纹理
38 2
|
3月前
|
API C# 图形学
Unity3D学习笔记9——加载纹理
Unity3D学习笔记9——加载纹理
36 2
|
3月前
|
存储 缓存 图形学
Unity3D学习笔记11——后处理
Unity3D学习笔记11——后处理
52 1
|
3月前
|
测试技术 C# 图形学
Unity3D学习笔记10——纹理数组
Unity3D学习笔记10——纹理数组
48 0
|
3月前
|
图形学 异构计算
Unity3D学习笔记8——GPU实例化(3)
Unity3D学习笔记8——GPU实例化(3)
40 0
|
3月前
|
存储 API 图形学
Unity3D学习笔记6——GPU实例化(1)
Unity3D学习笔记6——GPU实例化(1)
50 0
|
3月前
|
API 图形学 索引
Unity3D学习笔记5——创建子Mesh
Unity3D学习笔记5——创建子Mesh
27 0
|
3月前
|
API C# 图形学
Unity3D学习笔记4——创建Mesh高级接口
Unity3D学习笔记4——创建Mesh高级接口
38 0
|
3天前
|
弹性计算 人工智能 Serverless
阿里云ACK One:注册集群云上节点池(CPU/GPU)自动弹性伸缩,助力企业业务高效扩展
在当今数字化时代,企业业务的快速增长对IT基础设施提出了更高要求。然而,传统IDC数据中心却在业务存在扩容慢、缩容难等问题。为此,阿里云推出ACK One注册集群架构,通过云上节点池(CPU/GPU)自动弹性伸缩等特性,为企业带来全新突破。