GPU instancing + CutOff + shadow代码

经过网上查资料和自己的修改,实现了DrawMeshInstancedIndirect方式的透明、阴影功能,可以调节金属度和高光。下面是三个脚本。

c#代码:

using UnityEngine;
using UnityEngine.Rendering;

/// <summary>
/// This demo shows the use of Compute Shaders to update the object's positions. 
/// The buffer is stored and updated directly in GPU.
/// The append buffer can be used when there is an unknown output buffer size.
/// </summary>
public class InstancedIndirectComputeAppend : MonoBehaviour
{
    public int instanceCount = 100000;//总数量
    public Mesh instanceMesh;
    public Material instanceMaterial;

    public ShadowCastingMode castShadows = ShadowCastingMode.Off;
    public bool receiveShadows = false;

    public ComputeShader positionComputeShader;
    private int positionComputeKernelId;

    private ComputeBuffer positionAppendBuffer;
    private ComputeBuffer argsBuffer;

    //void Start()
    //{
    //    /// it's 5 args: index count per instance, instance count, start index location, base vertex location, start instance location.
    //    argsBuffer = new ComputeBuffer(5, sizeof(uint), ComputeBufferType.IndirectArguments);
    //    CreateBuffers();
 //       //Debug.Log(sizeof(uint));
    //}
    void OnEnable()
    {
        argsBuffer = new ComputeBuffer(5, sizeof(uint), ComputeBufferType.IndirectArguments);
        CreateBuffers();
    }
    void Update()
    {
        // Update position buffer
        UpdateBuffers();

        // Render - same old, only now we'll have argsBuffer with the count set from the append result.
        Graphics.DrawMeshInstancedIndirect(instanceMesh, 0, instanceMaterial, instanceMesh.bounds, argsBuffer, 0, null, castShadows, receiveShadows);
    }

    void UpdateBuffers()
    {
        /// reset the append buffer counter,
        /// this is important! otherwise we'll keep appending to a buffer indefinitely!
        positionAppendBuffer.SetCounterValue(0);
        //instanceCount = Mathf.ClosestPowerOfTwo(instanceCount);
        //Debug.Log(instanceCount);
        /// TODO this only works with POT, integral sqrt vals
        int bs = instanceCount / 64;
       // Debug.Log(bs);

        positionComputeShader.SetBuffer(positionComputeKernelId, "positionBuffer", positionAppendBuffer);
       positionComputeShader.SetFloat("_Dim", Mathf.Sqrt(instanceCount));

        positionComputeShader.Dispatch(positionComputeKernelId, bs, 1, 1);

        /// as we don't know exactly how many positions were output, we use this function
        /// to copy the count from positionAppendBuffer to argsBuffer, which will be used for rendering.
        /// The offset 4 is because the instance count is placed in args[1] for the DrawMeshInstancedIndirect
        /// + info https://docs.unity3d.com/ScriptReference/ComputeBuffer.CopyCount.html
        ComputeBuffer.CopyCount(positionAppendBuffer, argsBuffer, 4);
    }

    void CreateBuffers()
    {
        if (instanceCount < 1)
            instanceCount = 1;

        instanceCount = Mathf.ClosestPowerOfTwo(instanceCount);
       // Debug.Log(instanceCount);

        positionComputeKernelId = positionComputeShader.FindKernel("CSPositionKernel");
        instanceMesh.bounds = new Bounds(Vector3.zero, Vector3.one * 10000f);

        if (positionAppendBuffer != null)
            positionAppendBuffer.Release();

        /// note the compute buffer append type!
        positionAppendBuffer = new ComputeBuffer(instanceCount, 16, ComputeBufferType.Append);
        positionAppendBuffer.SetCounterValue(0);
        instanceMaterial.SetBuffer("positionBuffer", positionAppendBuffer);

        // indirect args
        uint numIndices = (instanceMesh != null) ? (uint)instanceMesh.GetIndexCount(0) : 0;
        argsBuffer.SetData(new uint[5] { numIndices, (uint)instanceCount, 0, 0, 0 });//主要是顶点数量
    }

    void OnDisable()
    {
        if (positionAppendBuffer != null)
            positionAppendBuffer.Release();
        positionAppendBuffer = null;

        if (argsBuffer != null)
            argsBuffer.Release();
        argsBuffer = null;
    }

    void OnGUI()
    {
        GUI.Label(new Rect(265, 12, 200, 30), "Instance Count: " + instanceCount.ToString("N0"));
    }
}

Surface shader代码:

Shader "Instanced/InstancedIndirectComputeAppend" 
{
    Properties{
        _MainTex("Albedo (RGB)", 2D) = "white" {}
        _Glossiness("Smoothness", Range(0,1)) = 0.5
        _Metallic("Metallic", Range(0,1)) = 0.0
        _Cutoff("Cutoff Value",Range(0,1.1))=0.5
    }
        SubShader{
        Tags { "RenderType"="Opaque" "Queue"="Transparent" }
        LOD 200

        CGPROGRAM
        // Physically based Standard lighting model
        //#pragma surface surf Standard addshadow
        #pragma surface surf Standard addshadow alphatest:_Cutoff
        #pragma multi_compile_instancing
        #pragma instancing_options procedural:setup

        sampler2D _MainTex;

        struct Input {
            float2 uv_MainTex;
        };

        #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
            StructuredBuffer<float4> positionBuffer;
        #endif

    void setup()
    {
        #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
        /// Positions are calculated in the compute shader.
        /// here we just use them.
        float4 position = positionBuffer[unity_InstanceID];
        float scale = position.w;

        unity_ObjectToWorld._11_21_31_41 = float4(scale, 0, 0, 0);
        unity_ObjectToWorld._12_22_32_42 = float4(0, scale, 0, 0);
        unity_ObjectToWorld._13_23_33_43 = float4(0, 0, scale, 0);
        unity_ObjectToWorld._14_24_34_44 = float4(position.xyz, 1);
        unity_WorldToObject = unity_ObjectToWorld;
        unity_WorldToObject._14_24_34 *= -1;
        unity_WorldToObject._11_22_33 = 1.0f / unity_WorldToObject._11_22_33;
        #endif
    }

    half _Glossiness;
    half _Metallic;

    float rand(in float2 uv)
    {
        float2 noise = (frac(sin(dot(uv, float2(12.9898, 78.233) * 2.0)) * 43758.5453));
        return abs(noise.x + noise.y) * 0.5;
    }

    void surf(Input IN, inout SurfaceOutputStandard o) 
    {
        float4 col = 1.0f;
        #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
           col = float4(unity_ObjectToWorld._11, rand(unity_ObjectToWorld._14_34), 1, 1);
        #endif
        
        fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * col;
        o.Albedo = c.rgb;
        o.Metallic = _Metallic;
        o.Smoothness = _Glossiness;
        o.Alpha =1- c.a;
    }
    ENDCG
    }
    FallBack "Diffuse"
}

Computer shader 代码:

#pragma kernel CSPositionKernel
 
 #define thread_group_size_x 64
 #define thread_group_size_y 1

AppendStructuredBuffer<float4> positionBuffer;

float _Dim;

float rand(in float2 uv)
{
    float2 noise = (frac(sin(dot(uv ,float2(12.9898,78.233)*2.0)) * 43758.5453));
    return abs(noise.x + noise.y) * 0.5;
}

[numthreads(thread_group_size_x, thread_group_size_y, 1)]
void CSPositionKernel (uint3 id : SV_DispatchThreadID)
{
    // this uv assumes the # of instances is _Dim * _Dim. 
    // so we calculate the uv inside a grid of _Dim x _Dim elements.
    float2 uv = float2( floor(id.x / _Dim) / _Dim, (id.x % (int)_Dim) / _Dim);
    
    // in this case, _Dim can be replaced by the grid size in the world
    float4 pos = float4((uv.x - 0.5) * _Dim * 2, 0, (uv.y - 0.5) * _Dim * 2, rand(uv));

    /// in this example, not all threads will output a position.
    /// note: the output order is not deterministic!
    if (rand(uv) > 0.5)
        positionBuffer.Append(pos);
}

发布了13 篇原创文章 · 获赞 766 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/qq_34593121/article/details/98192532