在谈GPUInstance

筅森魡賤 提交于 2020-02-16 14:26:20

官方网址:https://docs.unity3d.com/Manual/GPUInstancing.html
之前的博客:https://blog.csdn.net/wodownload2/article/details/103705433
知乎:https://zhuanlan.zhihu.com/p/34499251
csdn:https://blog.csdn.net/leonwei/article/details/73274808

introduction
use gpu instancing to draw (or render) multiple copies of the same Mesh at once, ’
using a small number of draw calls.
it is useful for drawing objects such as buildings, trees and grass, or other things that appear repeatedly in a Scene.

gpu instancing only renders identical Meshes with each draw call, but each instance can have different parameters
(for example, color or scale) to add variation and reduce the appearance of repetition. 增加变化减少重复

gpu instancing can reduce the number of draw calls used per Scene.
this significantly improves the rendering performance of your project.

adding instancing to your materials 让你的材质支持实例化

To enable GPU Instancing on Materials, select your Material in the Project window, and in the Inspector
, tick the Enable Instancing checkbox.
在这里插入图片描述

unity only displays this checkbox if the material shader supports gpu instancing.
this includes standard, standardspecular and all surface shaders.

使用gpu instance的一些规则:

  1. unity automatically picks MeshRenderer components and Graphics.DrawMesh calls for instancing.
    note that SkinnedMeshRenderer is not supported.
  2. unity only batches GameObjects that share the same mesh and the same material in a single gpu instancing draw call.

u can also use the calls Graphics.DrawMeshInstanced and Graphics.DrawMeshInstancedIndirect to perform gpu instancing from your scripts.

gpu instancing在下面的平台和api中被支持:

  1. directx 11 and directx 12 on windows
  2. opengl core 4.1/es 3.0+ on windows, macOS, linux, ios and android.
  3. metal on macos and ios
  4. vulkan on windows, linux and android
  5. playstation4 and xbox one
  6. webgl (requires webgl 2.0 api)

表面着色器的添加gpu 实例
by default, unity only batches instance of gamobejcts with different transforms in each instanced draw call.
to add more variance to your instanced gameobjects, modify your shader to add per-instance properties such as material color.

the example below demonstrates how to create an instanced shader with different colour values for each instance.

Shader "Custom/InstancedColorSurfaceShader" 
{
    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
        // Use Shader model 3.0 target
        #pragma target 3.0
        sampler2D _MainTex;
        struct Input 
        {
            float2 uv_MainTex;
        };
        half _Glossiness;
        half _Metallic;
        UNITY_INSTANCING_BUFFER_START(Props)
           UNITY_DEFINE_INSTANCED_PROP(fixed4, _Color)
        UNITY_INSTANCING_BUFFER_END(Props)
        void surf (Input IN, inout SurfaceOutputStandard o) 
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * UNITY_ACCESS_INSTANCED_PROP(Props, _Color);
            o.Albedo = c.rgb;
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

when u declare _Color as an instanced property, unity will gather _Color values from the MaterialPropertyBlock
objects set on GameObjects and put them in a single draw call.

MaterialPropertyBlock props = new MaterialPropertyBlock();
MeshRenderer renderer;
foreach (GameObject obj in objects)
{
   float r = Random.Range(0.0f, 1.0f);
   float g = Random.Range(0.0f, 1.0f);
   float b = Random.Range(0.0f, 1.0f);
   props.SetColor("_Color", new Color(r, g, b));
  
   renderer = obj.GetComponent<MeshRenderer>();
   renderer.SetPropertyBlock(props);
}

for these changes to take effect, u mush enable gpu instancing.
to do this, select your shader in the project window, and in the inspector, tick the enable instancing checkbox.

在这里插入图片描述

给顶点/片元着色器添加实例化支持

the following example takes a simple unlit shader and makes it capable of instaning with different colors:

Shader "SimplestInstancedShader"
{
    Properties
    {
        _Color ("Color", Color) = (1, 1, 1, 1)
    }

    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_instancing
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                UNITY_VERTEX_INPUT_INSTANCE_ID // necessary only if you want to access instanced properties in __fragment Shader__.
            };

            UNITY_INSTANCING_BUFFER_START(Props)
                UNITY_DEFINE_INSTANCED_PROP(float4, _Color)
            UNITY_INSTANCING_BUFFER_END(Props)
           
            v2f vert(appdata v)
            {
                v2f o;

                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_TRANSFER_INSTANCE_ID(v, o); // necessary only if you want to access instanced properties in the fragment Shader.

                o.vertex = UnityObjectToClipPos(v.vertex);
                return o;
            }
           
            fixed4 frag(v2f i) : SV_Target
            {
                UNITY_SETUP_INSTANCE_ID(i); // necessary only if any instanced properties are going to be accessed in the fragment Shader.
                return UNITY_ACCESS_INSTANCED_PROP(Props, _Color);
            }
            ENDCG
        }
    }
}

shader的一些修饰符:

  1. #pragma multi_compile_instancing——use this to instruct unity to generate instancing variants. 实例化变体
    it is not necessary for surface shaders. 表面着色器不用这么做
  2. UNITY_VERTEX_INPUT_INSTANCE_ID——use this in the vertex shader input/output structure to define an instance ID.
  3. UNITY_INSTANCING_BUFFER_START(name) / UNITY_INSTANCING_BUFFER_END(name)——every per-instance property must be defined in a specially named constant buffer. use this pair of macros to wrap the properties u want to be made unique to each instance.
  4. UNITY_DEFINE_INSTANCED_PROP(float4, _Color)——Use this to define a per-instance Shader property with a type and a name. In this example, the _Color property is unique.
  5. UNITY_SETUP_INSTANCE_ID(v);——use this to make the instance ID accessible to shader functions. it must be used at the very begining of a vertex shader, and is optional for fragment shaders.
  6. UNITY_TRANSFER_INSTANCE_ID(v, o);—— use this to copy the instance ID from the input structure to the output structure in the vertex shader. this is only necessary if u need to access per-instance data in the fragment shader.
  7. UNITY_ACCESS_INSTANCED_PROP(arrayName, color)——Use this to access a per-instance Shader property declared in an instancing constant buffer. It uses an instance ID to index into the instance data array. The arrayName in the macro must match the one in UNITY_INSTANCING_BUFFER_END(name) macro.

两个注意点:

  1. When using multiple per-instance properties, you don’t need to fill all of them in MaterialPropertyBlocks.
  2. If one instance lacks the property, Unity takes the default value from the referenced Material. If the material does not have a default value for the specified property, Unity sets the value to 0. Do not put non-instanced properties in the MaterialPropertyBlock, because this disables instancing. Instead, create different Materials for them.

advanced gpu instance tips
高级篇

batching priority
when batching, unity prioritizes static batching over instancing.
if u mark one of your gameobejcts for static batching, and unity successfully batches it.
unity disables instancing on that gameobject, event if its renderer uses an instancing shader.
when this happens, the inspector window displays a warning message suggesting that
u disable static batching.
To do this, open the Player settings (Edit > Project Settings
, then select the Player category), open Other Settings for your platform, and under the Rendering
section, disable the Static Batching
setting.
unity prioritizes instancing over dynamic batching.
if unity can instance a mesh, it disables dynamic batching for that mesh.
静态合批》gpu instance-》动态合批

unity的几个函数:
1/ Graphics.DrawMeshInstanced
some factors can prevent gameobejcts from being instanced together automatically.
these factors include material changes and depth sorting.
use Graphics.DrawMeshInstanced to force unity to draw these objects using GPU instancing.
like Graphics.DrawMesh, this function draws meshes for one frame without creating uncessary gameobjects.

Graphics.DrawMeshInstancedIndirect
use DrawMeshInstanceIndirect in a script to read the parameters of instancing draw calls, including the number of instances, from a compute buffer.
this is useful if u want to populate all of the instance data from the GPU, and the cpu does not know the number of instances to draw (for example, when performing gpu cullinig).

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!