From 8ef84a12c38bcc4419890c409431b669326a0394 Mon Sep 17 00:00:00 2001 From: ColinLeung-NiloCat <7102676+ColinLeung-NiloCat@users.noreply.github.com> Date: Sat, 1 Aug 2020 23:37:05 +0800 Subject: [PATCH] +CPU per cell frustum culling, can render 10M grass on adreno612 now --- .../Core/CullingCompute.compute | 11 +- .../Core/InstancedIndirectGrass.mat | 2 +- .../Core/InstancedIndirectGrassPosDefine.cs | 67 ++++++ .../InstancedIndirectGrassPosDefine.cs.meta | 11 + .../Core/InstancedIndirectGrassRenderer.cs | 221 ++++++++++++------ .../TestScene/scene.unity | 16 +- 6 files changed, 248 insertions(+), 80 deletions(-) create mode 100644 Assets/URPMobileGrassInstancedIndirectDemo/InstancedIndirectGrass/Core/InstancedIndirectGrassPosDefine.cs create mode 100644 Assets/URPMobileGrassInstancedIndirectDemo/InstancedIndirectGrass/Core/InstancedIndirectGrassPosDefine.cs.meta diff --git a/Assets/URPMobileGrassInstancedIndirectDemo/InstancedIndirectGrass/Core/CullingCompute.compute b/Assets/URPMobileGrassInstancedIndirectDemo/InstancedIndirectGrass/Core/CullingCompute.compute index db0d004..5add8b8 100644 --- a/Assets/URPMobileGrassInstancedIndirectDemo/InstancedIndirectGrass/Core/CullingCompute.compute +++ b/Assets/URPMobileGrassInstancedIndirectDemo/InstancedIndirectGrass/Core/CullingCompute.compute @@ -5,14 +5,15 @@ //cullingComputeShader.SetMatrix("_VPMatrix", p * v); //set from C# float4x4 _VPMatrix; float _MaxDrawDistance; -StructuredBuffer _AllInstancesTransformBuffer; //will not change until instance count change -AppendStructuredBuffer _VisibleInstanceOnlyTransformIDBuffer; //will set counter to 0 per frame, then fill in by this compute shader +uint _StartOffset; +StructuredBuffer _AllInstancesPosWSBuffer; //will not change until instance count change +AppendStructuredBuffer _VisibleInstancesOnlyPosWSIDBuffer; //will set counter to 0 per frame, then fill in by this compute shader -[numthreads(1024,1,1)] +[numthreads(64,1,1)] void CSMain (uint3 id : SV_DispatchThreadID) { //posWS -> posCS - float4 absPosCS = abs(mul(_VPMatrix,float4(_AllInstancesTransformBuffer[id.x],1.0))); + float4 absPosCS = abs(mul(_VPMatrix,float4(_AllInstancesPosWSBuffer[id.x + _StartOffset],1.0))); //do culling test in clip space, result is the same as doing test in NDC space. //prefer clip space here because doing culling test in clip space is faster than doing culling test in NDC, because we can skip 1 division. @@ -21,5 +22,5 @@ void CSMain (uint3 id : SV_DispatchThreadID) //y test allow 50% more threshold (hardcode for grass) //x test allow 10% more threshold (hardcode for grass) if (absPosCS.z <= absPosCS.w && absPosCS.y <= absPosCS.w*1.5 && absPosCS.x <= absPosCS.w*1.1 && absPosCS.w <= _MaxDrawDistance) - _VisibleInstanceOnlyTransformIDBuffer.Append(id.x); + _VisibleInstancesOnlyPosWSIDBuffer.Append(id.x + _StartOffset); } diff --git a/Assets/URPMobileGrassInstancedIndirectDemo/InstancedIndirectGrass/Core/InstancedIndirectGrass.mat b/Assets/URPMobileGrassInstancedIndirectDemo/InstancedIndirectGrass/Core/InstancedIndirectGrass.mat index 9b08c35..9d55d1c 100644 --- a/Assets/URPMobileGrassInstancedIndirectDemo/InstancedIndirectGrass/Core/InstancedIndirectGrass.mat +++ b/Assets/URPMobileGrassInstancedIndirectDemo/InstancedIndirectGrass/Core/InstancedIndirectGrass.mat @@ -92,7 +92,7 @@ Material: - _ZWrite: 1 m_Colors: - _BaseColor: {r: 0.41936636, g: 0.7169812, b: 0.29423285, a: 1} - - _BoundSize: {r: 55.9017, g: 55.9017, b: 0, a: 0} + - _BoundSize: {r: 661.4378, g: 661.4378, b: 0, a: 0} - _Color: {r: 1, g: 1, b: 1, a: 1} - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} - _GroundColor: {r: 0.2195755, g: 0.46226418, b: 0.2581851, a: 1} diff --git a/Assets/URPMobileGrassInstancedIndirectDemo/InstancedIndirectGrass/Core/InstancedIndirectGrassPosDefine.cs b/Assets/URPMobileGrassInstancedIndirectDemo/InstancedIndirectGrass/Core/InstancedIndirectGrassPosDefine.cs new file mode 100644 index 0000000..8d01e2c --- /dev/null +++ b/Assets/URPMobileGrassInstancedIndirectDemo/InstancedIndirectGrass/Core/InstancedIndirectGrassPosDefine.cs @@ -0,0 +1,67 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class InstancedIndirectGrassPosDefine : MonoBehaviour +{ + [Range(1, 40000000)] + public int instanceCount = 1000000; + public float drawDistance = 125; + + int cacheCount = -1; + + // Start is called before the first frame update + void Start() + { + UpdatePosIfNeeded(); + } + + private void UpdatePosIfNeeded() + { + if (instanceCount == cacheCount) + return; + + Debug.Log("UpdatePos (Slow)"); + + //same seed to keep grass visual the same + UnityEngine.Random.InitState(123); + + //auto keep density the same + float scale = Mathf.Sqrt((instanceCount / 4)) / 2f; + transform.localScale = new Vector3(scale, transform.localScale.y, scale); + + ////////////////////////////////////////////////////////////////////////// + //can define any posWS in this section, random is just an example + ////////////////////////////////////////////////////////////////////////// + List positions = new List(instanceCount); + for (int i = 0; i < instanceCount; i++) + { + Vector3 pos = Vector3.zero; + + pos.x = UnityEngine.Random.Range(-1f, 1f) * transform.lossyScale.x; + pos.z = UnityEngine.Random.Range(-1f, 1f) * transform.lossyScale.z; + + //transform to posWS in C# + pos += transform.position; + + positions.Add(new Vector3(pos.x, pos.y, pos.z)); + } + + //send all posWS to renderer + InstancedIndirectGrassRenderer.instance.allGrassPos = positions; + cacheCount = positions.Count; + } + private void Update() + { + UpdatePosIfNeeded(); + } + private void OnGUI() + { + GUI.Label(new Rect(300, 50, 200, 30), "Instance Count: " + instanceCount/1000000 + "Million"); + instanceCount = Mathf.Max(1, (int)(GUI.HorizontalSlider(new Rect(300, 100, 200, 30), instanceCount / 1000000f, 1, 10)) * 1000000); + + GUI.Label(new Rect(300, 150, 200, 30), "Draw Distance: " + drawDistance); + drawDistance = Mathf.Max(1, (int)(GUI.HorizontalSlider(new Rect(300, 200, 200, 30), drawDistance / 25f, 1, 8)) * 25); + InstancedIndirectGrassRenderer.instance.drawDistance = drawDistance; + } +} diff --git a/Assets/URPMobileGrassInstancedIndirectDemo/InstancedIndirectGrass/Core/InstancedIndirectGrassPosDefine.cs.meta b/Assets/URPMobileGrassInstancedIndirectDemo/InstancedIndirectGrass/Core/InstancedIndirectGrassPosDefine.cs.meta new file mode 100644 index 0000000..980b95d --- /dev/null +++ b/Assets/URPMobileGrassInstancedIndirectDemo/InstancedIndirectGrass/Core/InstancedIndirectGrassPosDefine.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e9d6ee35f46cc6a449199c3d64518325 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/URPMobileGrassInstancedIndirectDemo/InstancedIndirectGrass/Core/InstancedIndirectGrassRenderer.cs b/Assets/URPMobileGrassInstancedIndirectDemo/InstancedIndirectGrass/Core/InstancedIndirectGrassRenderer.cs index 5267cbe..0a7ecaf 100644 --- a/Assets/URPMobileGrassInstancedIndirectDemo/InstancedIndirectGrass/Core/InstancedIndirectGrassRenderer.cs +++ b/Assets/URPMobileGrassInstancedIndirectDemo/InstancedIndirectGrass/Core/InstancedIndirectGrassRenderer.cs @@ -1,29 +1,40 @@ //see this for ref: https://docs.unity3d.com/ScriptReference/Graphics.DrawMeshInstancedIndirect.html using System; +using System.Collections.Generic; using UnityEngine; +using UnityEngine.Assertions; -[ExecuteAlways] +//[ExecuteAlways] public class InstancedIndirectGrassRenderer : MonoBehaviour { - [Range(1,1000000)] - public int instanceCount = 50000; + [Header("Settings")] public float drawDistance = 125; public Material instanceMaterial; + private int cellCountX = 11; //temp! + private int cellCountZ = 11; //temp! + + [Header("Internal")] public ComputeShader cullingComputeShader; - //global ref to this script - [HideInInspector] - public static InstancedIndirectGrassRenderer instance; + //===================================================== + [HideInInspector] + public static InstancedIndirectGrassRenderer instance;// global ref to this script + [NonSerialized] + public List allGrassPos = new List(); private int cachedInstanceCount = -1; private Mesh cachedGrassMesh; - private ComputeBuffer allInstanceTransformBuffer; - private ComputeBuffer visibleInstanceOnlyTransformBuffer; + private ComputeBuffer allInstancesPosWSBuffer; + private ComputeBuffer visibleInstancesOnlyPosWSIDBuffer; private ComputeBuffer argsBuffer; - private void Awake() + List[] cells; //for binning: put each posWS into correct cell + float minX, minZ, maxX, maxZ; + //===================================================== + + private void OnEnable() { instance = this; // assign global ref using this script } @@ -33,48 +44,88 @@ void LateUpdate() // recreate all buffers in grass shader if needed UpdateAllInstanceTransformBufferIfNeeded(); - //dispatch culling compute, fill visible instance into visibleInstanceOnlyTransformBuffer - visibleInstanceOnlyTransformBuffer.SetCounterValue(0); + // big cell frustum culling in CPU first + //==================================================================================== + List visibleCellIDList = new List(); + for (int i = 0; i < cells.Length; i++) + { + //create cell bound + Vector3 centerPosWS = new Vector3 (i % cellCountX + 0.5f, 0, i / cellCountX + 0.5f); + centerPosWS.x = Mathf.Lerp(minX, maxX, centerPosWS.x / cellCountX); + centerPosWS.z = Mathf.Lerp(minZ, maxZ, centerPosWS.z / cellCountZ); + Vector3 sizeWS = new Vector3(Mathf.Abs(maxX - minX) / cellCountX,0,Mathf.Abs(maxX - minX) / cellCountX); + Bounds cellBound = new Bounds(centerPosWS, sizeWS); + + //Do frustum culling using the above bound + //https://docs.unity3d.com/ScriptReference/GeometryUtility.CalculateFrustumPlanes.html + //https://docs.unity3d.com/ScriptReference/GeometryUtility.TestPlanesAABB.html + float cameraOriginalFarPlane = Camera.main.farClipPlane; + Camera.main.farClipPlane = drawDistance;//allow drawDistance control + Plane[] planes = GeometryUtility.CalculateFrustumPlanes(Camera.main);//Ordering: [0] = Left, [1] = Right, [2] = Down, [3] = Up, [4] = Near, [5] = Far + Camera.main.farClipPlane = cameraOriginalFarPlane;//revert + if (GeometryUtility.TestPlanesAABB(planes, cellBound)) + { + visibleCellIDList.Add(i); + continue; + } + } + + // then loop though only visible cells, each visible cell dispatch GPU culling job once + // will fill all visible instance into visibleInstancesOnlyPosWSIDBuffer + //==================================================================================== + Debug.Log($"after CPU cell Culling - visible cell count = {visibleCellIDList.Count}/{cellCountX*cellCountZ}"); + Matrix4x4 v = Camera.main.worldToCameraMatrix; Matrix4x4 p = Camera.main.projectionMatrix; Matrix4x4 vp = p * v; + + visibleInstancesOnlyPosWSIDBuffer.SetCounterValue(0); + + //set once only cullingComputeShader.SetMatrix("_VPMatrix", vp); cullingComputeShader.SetFloat("_MaxDrawDistance", drawDistance); - cullingComputeShader.SetBuffer(0, "_AllInstancesTransformBuffer", allInstanceTransformBuffer); - cullingComputeShader.SetBuffer(0, "_VisibleInstanceOnlyTransformIDBuffer", visibleInstanceOnlyTransformBuffer); - cullingComputeShader.Dispatch(0, Mathf.CeilToInt(instanceCount/1024f), 1, 1); - ComputeBuffer.CopyCount(visibleInstanceOnlyTransformBuffer, argsBuffer, 4); + cullingComputeShader.SetBuffer(0, "_AllInstancesPosWSBuffer", allInstancesPosWSBuffer); + cullingComputeShader.SetBuffer(0, "_VisibleInstancesOnlyPosWSIDBuffer", visibleInstancesOnlyPosWSIDBuffer); + //set per cell + for (int i = 0; i < visibleCellIDList.Count; i++) + { + int targetID = visibleCellIDList[i]; + int offset = 0; + for (int j = 0; j < targetID; j++) + { + offset += cells[j].Count; + } + cullingComputeShader.SetInt("_StartOffset", offset); //culling start getting data at offseted pos, will start from cell's total offset in memory + cullingComputeShader.Dispatch(0, Mathf.CeilToInt(cells[targetID].Count / 64f), 1, 1); //disaptch.X must match in shader + } + + //==================================================================================== + + // GPU culling finished, copy count to prepare DrawMeshInstancedIndirect draw amount + ComputeBuffer.CopyCount(visibleInstancesOnlyPosWSIDBuffer, argsBuffer, 4); // Render - Graphics.DrawMeshInstancedIndirect(GetGrassMeshCache(), 0, instanceMaterial, new Bounds(transform.position, transform.localScale * 2), argsBuffer); + Bounds renderBound = new Bounds(); + renderBound.SetMinMax(new Vector3(minX, 0, minZ), new Vector3(maxX, 0, maxZ));//if camera out of this bound, DrawMeshInstancedIndirect will not trigger + Graphics.DrawMeshInstancedIndirect(GetGrassMeshCache(), 0, instanceMaterial, renderBound, argsBuffer); } void OnDisable() { //release all compute buffers - if (allInstanceTransformBuffer != null) - allInstanceTransformBuffer.Release(); - allInstanceTransformBuffer = null; + if (allInstancesPosWSBuffer != null) + allInstancesPosWSBuffer.Release(); + allInstancesPosWSBuffer = null; - if (visibleInstanceOnlyTransformBuffer != null) - visibleInstanceOnlyTransformBuffer.Release(); - visibleInstanceOnlyTransformBuffer = null; + if (visibleInstancesOnlyPosWSIDBuffer != null) + visibleInstancesOnlyPosWSIDBuffer.Release(); + visibleInstancesOnlyPosWSIDBuffer = null; if (argsBuffer != null) argsBuffer.Release(); argsBuffer = null; - } - void OnGUI() - { - GUI.Label(new Rect(300, 50, 200, 30), "Instance Count: " + instanceCount.ToString()); - instanceCount = Mathf.Max(1,(int)(GUI.HorizontalSlider(new Rect(300, 100, 200, 30), instanceCount / 10000f, 0, 100)) *10000); - - float scale = Mathf.Sqrt((instanceCount / 4)) / 2f; - transform.localScale = new Vector3(scale, transform.localScale.y, scale); - - GUI.Label(new Rect(300, 150, 200, 30), "Draw Distance: " + drawDistance); - drawDistance = Mathf.Max(1, (int)(GUI.HorizontalSlider(new Rect(300, 200, 200, 30), drawDistance/ 25f, 1, 8))*25); + instance = null; } Mesh GetGrassMeshCache() @@ -84,12 +135,12 @@ Mesh GetGrassMeshCache() //if not exist, create a 3 vertices hardcode triangle grass mesh cachedGrassMesh = new Mesh(); - //first grass (vertices) + //single grass (vertices) Vector3[] verts = new Vector3[3]; verts[0] = new Vector3(-0.25f, 0); verts[1] = new Vector3(+0.25f, 0); verts[2] = new Vector3(-0.0f, 1); - //first grass (Triangles index) + //single grass (Triangle index) int[] trinagles = new int[3] { 2, 1, 0, }; //order to fit Cull Back in grass shader cachedGrassMesh.SetVertices(verts); @@ -105,11 +156,11 @@ void UpdateAllInstanceTransformBufferIfNeeded() instanceMaterial.SetVector("_PivotPosWS", transform.position); instanceMaterial.SetVector("_BoundSize", new Vector2(transform.localScale.x, transform.localScale.z)); - //early exit if no need update buffer - if (cachedInstanceCount == instanceCount && + //early exit if no need to update buffer + if (cachedInstanceCount == allGrassPos.Count && argsBuffer != null && - allInstanceTransformBuffer != null && - visibleInstanceOnlyTransformBuffer != null) + allInstancesPosWSBuffer != null && + visibleInstancesOnlyPosWSIDBuffer != null) { return; } @@ -117,46 +168,67 @@ void UpdateAllInstanceTransformBufferIfNeeded() ///////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////// + Debug.Log("UpdateAllInstanceTransformBuffer (Slow)"); + /////////////////////////// - // Transform buffer + // allInstancesPosWSBuffer buffer /////////////////////////// - if (allInstanceTransformBuffer != null) - allInstanceTransformBuffer.Release(); - allInstanceTransformBuffer = new ComputeBuffer(instanceCount, sizeof(float)*3); //float3 posWS only, per grass + if (allInstancesPosWSBuffer != null) + allInstancesPosWSBuffer.Release(); + allInstancesPosWSBuffer = new ComputeBuffer(allGrassPos.Count, sizeof(float)*3); //float3 posWS only, per grass + + if (visibleInstancesOnlyPosWSIDBuffer != null) + visibleInstancesOnlyPosWSIDBuffer.Release(); + visibleInstancesOnlyPosWSIDBuffer = new ComputeBuffer(allGrassPos.Count, sizeof(uint), ComputeBufferType.Append); //uint only, per visible grass + + //find all instances's posWS XZ bound min max + minX = float.MaxValue; + minZ = float.MaxValue; + maxX = float.MinValue; + maxZ = float.MinValue; + for (int i = 0; i < allGrassPos.Count; i++) + { + Vector3 target = allGrassPos[i]; + minX = Mathf.Min(target.x, minX); + minZ = Mathf.Min(target.z, minZ); + maxX = Mathf.Max(target.x, maxX); + maxZ = Mathf.Max(target.z, maxZ); + } + + //init per cell posWS list memory + cells = new List[cellCountX * cellCountZ]; //flatten 2D array + for (int i = 0; i < cells.Length; i++) + { + cells[i] = new List(); + } + + //binning, put each posWS into the correct cell + for (int i = 0; i < allGrassPos.Count; i++) + { + Vector3 pos = allGrassPos[i]; - if (visibleInstanceOnlyTransformBuffer != null) - visibleInstanceOnlyTransformBuffer.Release(); - visibleInstanceOnlyTransformBuffer = new ComputeBuffer(instanceCount, sizeof(uint), ComputeBufferType.Append); //uint only, per visible grass + //find cellID + int xID = Mathf.Min(cellCountX-1,Mathf.FloorToInt(Mathf.InverseLerp(minX, maxX, pos.x) * cellCountX)); //use min to force within 0~[cellCountX-1] + int zID = Mathf.Min(cellCountZ-1,Mathf.FloorToInt(Mathf.InverseLerp(minZ, maxZ, pos.z) * cellCountZ)); //use min to force within 0~[cellCountZ-1] - //keep grass visual the same - UnityEngine.Random.InitState(123); + cells[xID + zID * cellCountX].Add(pos); + } - //spawn grass inside gizmo cube - Vector3[] positions = new Vector3[instanceCount]; - for (int i = 0; i < instanceCount; i++) + //combine to a flatten array for compute buffer + int offset = 0; + Vector3[] allGrassPosWSSortedByCell = new Vector3[allGrassPos.Count]; + for (int i = 0; i < cells.Length; i++) { - Vector3 pos = Vector3.zero; - - //can define any posWS in this section, random is just an example - //TODO: allow API call to set posWS - //=================================================== - //local pos - pos.x = UnityEngine.Random.Range(-1f, 1f); - pos.y = 0; - pos.z = UnityEngine.Random.Range(-1f, 1f); - - //transform to posWS in C# - pos.x *= transform.lossyScale.x; - pos.z *= transform.lossyScale.z; - pos += transform.position; - //=================================================== - - positions[i] = new Vector3(pos.x,pos.y,pos.z); + for (int j = 0; j < cells[i].Count; j++) + { + allGrassPosWSSortedByCell[offset] = cells[i][j]; + offset++; + } } - allInstanceTransformBuffer.SetData(positions); - instanceMaterial.SetBuffer("_AllInstancesTransformBuffer", allInstanceTransformBuffer); - instanceMaterial.SetBuffer("_VisibleInstanceOnlyTransformIDBuffer", visibleInstanceOnlyTransformBuffer); + allInstancesPosWSBuffer.SetData(allGrassPosWSSortedByCell); + instanceMaterial.SetBuffer("_AllInstancesTransformBuffer", allInstancesPosWSBuffer); + instanceMaterial.SetBuffer("_VisibleInstanceOnlyTransformIDBuffer", visibleInstancesOnlyPosWSIDBuffer); /////////////////////////// // Indirect args buffer @@ -167,14 +239,17 @@ void UpdateAllInstanceTransformBufferIfNeeded() argsBuffer = new ComputeBuffer(1, args.Length * sizeof(uint), ComputeBufferType.IndirectArguments); args[0] = (uint)GetGrassMeshCache().GetIndexCount(0); - args[1] = (uint)instanceCount; + args[1] = (uint)allGrassPos.Count; args[2] = (uint)GetGrassMeshCache().GetIndexStart(0); args[3] = (uint)GetGrassMeshCache().GetBaseVertex(0); args[4] = 0; argsBuffer.SetData(args); + /////////////////////////// + // Update Cache + /////////////////////////// //update cache to prevent future no-op buffer update, which waste performance - cachedInstanceCount = instanceCount; + cachedInstanceCount = allGrassPos.Count; } } \ No newline at end of file diff --git a/Assets/URPMobileGrassInstancedIndirectDemo/InstancedIndirectGrass/TestScene/scene.unity b/Assets/URPMobileGrassInstancedIndirectDemo/InstancedIndirectGrass/TestScene/scene.unity index bd3527e..b4ac16b 100644 --- a/Assets/URPMobileGrassInstancedIndirectDemo/InstancedIndirectGrass/TestScene/scene.unity +++ b/Assets/URPMobileGrassInstancedIndirectDemo/InstancedIndirectGrass/TestScene/scene.unity @@ -2704,6 +2704,7 @@ GameObject: m_Component: - component: {fileID: 1709084318} - component: {fileID: 1709084317} + - component: {fileID: 1709084319} m_Layer: 0 m_Name: InstancedIndirectGrass m_TagString: Untagged @@ -2723,7 +2724,6 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 2ed67b81fb4b64d498a58bbb1c32d63d, type: 3} m_Name: m_EditorClassIdentifier: - instanceCount: 50000 drawDistance: 125 instanceMaterial: {fileID: 2100000, guid: 2a0ca5d9e755e74448eaac7a643303db, type: 2} cullingComputeShader: {fileID: 7200000, guid: 4ff257ce2a1c37546a9b544e669e976b, @@ -2742,6 +2742,20 @@ Transform: m_Father: {fileID: 652083985} m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1709084319 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1709084316} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e9d6ee35f46cc6a449199c3d64518325, type: 3} + m_Name: + m_EditorClassIdentifier: + instanceCount: 1000000 + drawDistance: 125 --- !u!1 &1721825726 GameObject: m_ObjectHideFlags: 0