经过网上查资料和自己的修改,实现了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);
}