SV_InstanceID/UNITY_VERTEX_INPUT_INSTANCE_ID——GPU instancing

https://docs.unity3d.com/2019.4/Documentation/Manual/GPUInstancing.html

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 enbale 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.

when u use GPU instancing, the following restrictions apply:

  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. use a small number of Meshes and Materials for better instancing efficiency. to create variations, modify your shader scripts to add per-instance data.

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

adding per-instance data
by default, unity only batches instances of GameObjects with different Transforms (position, rotation, scale) 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);
}

note that in normal cases (where an instancing shader is not used, or _Color is not a per-instance property), draw call batches are broken due to different values in the MaterialPropertyBlock.

For these changes to take effect, you must enable GPU Instancing. To do this, select your Shader in the Project window, and in the Inspector, tick the Enable Instancing checkbox.

adding instancing to vertex and fragment shaders
the following example takes a simple unlit shader and makes it capable of instancing 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
        }
    }
}

UnityInstancing.cginc有宏定义。

shader modifications

  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. see SV_InstanceID for more information.

  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 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.

Unity中自定义Shader实现Mesh Instancing(实例化)下的阴影处理通常涉及到几个步骤: 1. **基础设置**: - 首先,确保场景中有启用Mesh Instancing的物体,并配置好所需的实例数量。 2. **获取实例数据**: - 在Shader中,你需要使用`_ObjectInstID`属性来访问每个实例的唯一标识符。这在`VertexLitInstance`着色器宏中可用。 3. **条件分支**: - 根据实例ID,编写一个if-else结构,检查当前顶点属于哪个实例,因为每个实例可能会有独立的阴影贴图或光照信息。 4. **阴影采样**: - 使用`SampleLevel`或`SampleSphericalHarmonics`等函数,根据实例的光照设置来采样正确的阴影贴图。这可能需要多个纹理(如主颜色、深度、阴影)来计算阴影效果。 5. **实例着色**: - 应用特定于实例的颜色和其他属性,比如实例间材质的变化、动画等。 6. **混合实例结果**: - 如果你想混合所有实例的结果,可能需要对颜色或其他特性做加权平均,这取决于你的需求和设计。 7. **保存结果**: - 最后,在像素着色阶段,将计算出的效果写入到屏幕缓冲区。 ```csharp CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float4 texcoord : TEXCOORD0; uint instanceID : INSTANCE_ID; // 添加实例ID属性 }; struct v2f { float4 vertex : SV_POSITION; float4 texcoord : TEXCOORD0; uint instanceID : SV_InstanceID; // Unity内置的实例ID通道 }; sampler2D _MainTex; float4 _Color; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.texcoord = v.texcoord; o.instanceID = v.instanceID; return o; } fixed4 frag (v2f i) : SV_Target { // 获取实例相关的变量 float4 instanceColor = tex2D(_MainTex, i.texcoord.xy); // 根据instanceID判断并计算阴影 // 这里只是一个简化示例,实际代码可能更复杂 fixed4 shadowColor; if (i.instanceID == 0) { shadowColor = shadowSample(i); } else { shadowColor = lerp(shadowSample(i), instanceColor, 0.5f); // 混合阴影和实例颜色 } return lerp(shadowColor, instanceColor, _Color.rgb); // 考虑到全局颜色影响 } // ...其他阴影采样和混合代码... ENDCG ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值