Files
VR-WuKong/Assets/GameRes/Main/SceneArt/VRScene/Materials/GPUGrass/Grass.cs
2025-11-14 18:44:06 +08:00

159 lines
3.8 KiB
C#

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
public class Grass : MonoBehaviour
{
public Mesh mesh;
public Material material;
public Bounds bounds;
public float scaleMult;
public ShadowCastingMode shadowCastingMode;
public bool isReceiveShadow;
public Vector3 objectBoundMin;
public Vector3 objectBoundMax;
public ComputeShader computeShader;
int kernel = 0;
ComputeBuffer instDataBuffer;
ComputeBuffer cullIDsBuffer;
ComputeBuffer argsBuffer;
public Transform root;
List<InstData> dataInfors = new List<InstData>();
private struct InstData
{
public Vector3 pos;
public Vector3 angle;
public Vector3 scale;
//
public static int GetNum()
{
return sizeof(float) * 3 + sizeof(float) * 3 + sizeof(float) * 3;
}
}
private void Start()
{
GetDataInfors();
InitializeBuffers();
}
void GetDataInfors()
{
foreach (Transform t in root.GetComponentInChildren<Transform>())
{
InstData instData = new InstData();
instData.pos = t.position;
instData.angle = t.eulerAngles;
instData.scale = t.localScale * scaleMult;
dataInfors.Add(instData);
//
t.gameObject.SetActive(false);
}
}
private void InitializeBuffers()
{
// Arguments for drawing mesh.
uint[] args = new uint[5] { 0, 0, 0, 0, 0 };
// 0 == number of triangle indices, 1 == count, others are only relevant if drawing submeshes.
args[0] = (uint)mesh.GetIndexCount(0);
args[1] = (uint)dataInfors.Count;
args[2] = (uint)mesh.GetIndexStart(0);
args[3] = (uint)mesh.GetBaseVertex(0);
argsBuffer = new ComputeBuffer(1, args.Length * sizeof(uint), ComputeBufferType.IndirectArguments);
argsBuffer.SetData(args);
// Initialize buffer with the given population.
material = Instantiate(material);
instDataBuffer = new ComputeBuffer(dataInfors.Count, InstData.GetNum());
instDataBuffer.SetData(dataInfors);
material.SetBuffer("_InstDataBuffer", instDataBuffer);
cullIDsBuffer = new ComputeBuffer(dataInfors.Count, sizeof(uint), ComputeBufferType.Append);
material.SetBuffer("_CullIDsBuffer", cullIDsBuffer);
//
computeShader = Instantiate(computeShader);
kernel = computeShader.FindKernel("CSMain");
computeShader.SetBuffer(kernel, "_InstDataBuffer", instDataBuffer);
computeShader.SetVector("_BoundMin", objectBoundMin);
computeShader.SetVector("_BoundMax", objectBoundMax);
computeShader.SetBuffer(kernel, "_CullIDsBuffer", cullIDsBuffer);
}
private void Update()
{
cullIDsBuffer.SetCounterValue(0);
computeShader.SetVectorArray("_FrustumPlanes", GpuCullCamera.inst.frustumPlanes);
computeShader.Dispatch(kernel, Mathf.CeilToInt(dataInfors.Count / 128f), 1, 1);
ComputeBuffer.CopyCount(cullIDsBuffer, argsBuffer, sizeof(uint));
//
Graphics.DrawMeshInstancedIndirect(mesh, 0, material, bounds, argsBuffer, 0, null, shadowCastingMode, isReceiveShadow, LayerMask.NameToLayer("Instance"));
}
private void OnDrawGizmos()
{
Gizmos.color = Color.blue;
Gizmos.DrawWireCube(bounds.center, bounds.size);
}
private void OnDisable()
{
// Release gracefully.
if (instDataBuffer != null)
{
instDataBuffer.Release();
}
instDataBuffer = null;
if (cullIDsBuffer != null)
{
cullIDsBuffer.Release();
}
cullIDsBuffer = null;
if (argsBuffer != null)
{
argsBuffer.Release();
}
argsBuffer = null;
}
}