add hi-z occlusion culling.culling

This commit is contained in:
StarBeats 2025-06-23 19:18:41 +08:00
parent 20c56fed0a
commit 3a68dfd783
13 changed files with 631 additions and 413 deletions

View File

@ -44066,6 +44066,10 @@ PrefabInstance:
serializedVersion: 3
m_TransformParent: {fileID: 87142414}
m_Modifications:
- target: {fileID: 204879369288025, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_RenderingLayerMask
value: 1
objectReference: {fileID: 0}
- target: {fileID: 2921625138699678336, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_Name
value: LanternStone_01_Prefab (2)
@ -44074,6 +44078,10 @@ PrefabInstance:
propertyPath: m_IsActive
value: 1
objectReference: {fileID: 0}
- target: {fileID: 5230092601067906215, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_RenderingLayerMask
value: 1
objectReference: {fileID: 0}
- target: {fileID: 5402048836714850101, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: maxDisplayDistance
value: 26
@ -44122,6 +44130,10 @@ PrefabInstance:
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8869709827930661810, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_RenderingLayerMask
value: 1
objectReference: {fileID: 0}
- target: {fileID: 9056249284700800750, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_IsActive
value: 1
@ -47362,6 +47374,10 @@ PrefabInstance:
propertyPath: m_IsActive
value: 1
objectReference: {fileID: 0}
- target: {fileID: 141850471471409436, guid: 8e5bed6b8dcfed240a38affefd9c58f6, type: 3}
propertyPath: m_Enabled
value: 1
objectReference: {fileID: 0}
- target: {fileID: 141850471471409437, guid: 8e5bed6b8dcfed240a38affefd9c58f6, type: 3}
propertyPath: m_LocalPosition.x
value: 0
@ -65894,6 +65910,10 @@ PrefabInstance:
serializedVersion: 3
m_TransformParent: {fileID: 87142414}
m_Modifications:
- target: {fileID: 204879369288025, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_RenderingLayerMask
value: 1
objectReference: {fileID: 0}
- target: {fileID: 2921625138699678336, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_Name
value: LanternStone_01_Prefab (3)
@ -65906,6 +65926,10 @@ PrefabInstance:
propertyPath: m_BoundingSphereOverride.x
value: -0.125
objectReference: {fileID: 0}
- target: {fileID: 5230092601067906215, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_RenderingLayerMask
value: 1
objectReference: {fileID: 0}
- target: {fileID: 6614022729291184415, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_RootOrder
value: 0
@ -65950,6 +65974,10 @@ PrefabInstance:
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8869709827930661810, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_RenderingLayerMask
value: 1
objectReference: {fileID: 0}
m_RemovedComponents:
- {fileID: 8762647674606602420, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
m_RemovedGameObjects: []
@ -73681,6 +73709,10 @@ PrefabInstance:
serializedVersion: 3
m_TransformParent: {fileID: 87142414}
m_Modifications:
- target: {fileID: 204879369288025, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_RenderingLayerMask
value: 1
objectReference: {fileID: 0}
- target: {fileID: 2921625138699678336, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_Name
value: LanternStone_01_Prefab (8)
@ -73689,6 +73721,10 @@ PrefabInstance:
propertyPath: m_IsActive
value: 1
objectReference: {fileID: 0}
- target: {fileID: 5230092601067906215, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_RenderingLayerMask
value: 1
objectReference: {fileID: 0}
- target: {fileID: 6614022729291184415, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_RootOrder
value: 0
@ -73733,6 +73769,10 @@ PrefabInstance:
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8869709827930661810, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_RenderingLayerMask
value: 1
objectReference: {fileID: 0}
m_RemovedComponents:
- {fileID: 8762647674606602420, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
m_RemovedGameObjects: []
@ -74798,6 +74838,10 @@ PrefabInstance:
serializedVersion: 3
m_TransformParent: {fileID: 87142414}
m_Modifications:
- target: {fileID: 204879369288025, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_RenderingLayerMask
value: 1
objectReference: {fileID: 0}
- target: {fileID: 2921625138699678336, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_Name
value: LanternStone_01_Prefab (1)
@ -74806,6 +74850,10 @@ PrefabInstance:
propertyPath: m_IsActive
value: 1
objectReference: {fileID: 0}
- target: {fileID: 5230092601067906215, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_RenderingLayerMask
value: 1
objectReference: {fileID: 0}
- target: {fileID: 6614022729291184415, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_RootOrder
value: 1
@ -74850,6 +74898,10 @@ PrefabInstance:
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8869709827930661810, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_RenderingLayerMask
value: 1
objectReference: {fileID: 0}
m_RemovedComponents:
- {fileID: 8762647674606602420, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
m_RemovedGameObjects: []
@ -113304,6 +113356,10 @@ PrefabInstance:
serializedVersion: 3
m_TransformParent: {fileID: 87142414}
m_Modifications:
- target: {fileID: 204879369288025, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_RenderingLayerMask
value: 1
objectReference: {fileID: 0}
- target: {fileID: 2921625138699678336, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_Name
value: LanternStone_01_Prefab (5)
@ -113312,6 +113368,10 @@ PrefabInstance:
propertyPath: m_IsActive
value: 1
objectReference: {fileID: 0}
- target: {fileID: 5230092601067906215, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_RenderingLayerMask
value: 1
objectReference: {fileID: 0}
- target: {fileID: 6614022729291184415, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_RootOrder
value: 0
@ -113356,6 +113416,10 @@ PrefabInstance:
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8869709827930661810, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_RenderingLayerMask
value: 1
objectReference: {fileID: 0}
m_RemovedComponents:
- {fileID: 8762647674606602420, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
m_RemovedGameObjects: []
@ -115248,6 +115312,10 @@ PrefabInstance:
serializedVersion: 3
m_TransformParent: {fileID: 87142414}
m_Modifications:
- target: {fileID: 204879369288025, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_RenderingLayerMask
value: 1
objectReference: {fileID: 0}
- target: {fileID: 2921625138699678336, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_Name
value: LanternStone_01_Prefab (6)
@ -115256,6 +115324,10 @@ PrefabInstance:
propertyPath: m_IsActive
value: 1
objectReference: {fileID: 0}
- target: {fileID: 5230092601067906215, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_RenderingLayerMask
value: 1
objectReference: {fileID: 0}
- target: {fileID: 5402048836714850101, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: maxDisplayDistance
value: 23
@ -115304,6 +115376,10 @@ PrefabInstance:
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8869709827930661810, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
propertyPath: m_RenderingLayerMask
value: 1
objectReference: {fileID: 0}
m_RemovedComponents:
- {fileID: 8762647674606602420, guid: f3465f68f6d568d498c6301e743eae85, type: 3}
m_RemovedGameObjects: []
@ -117817,6 +117893,7 @@ GameObject:
- component: {fileID: 966895463892816108}
- component: {fileID: 966895463892816109}
- component: {fileID: 966895463892816115}
- component: {fileID: 966895463892816116}
m_Layer: 0
m_Name: SceneSetup
m_TagString: Untagged
@ -117837,6 +117914,20 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
transform: {fileID: 0}
--- !u!114 &966895463892816116
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 966895463892816114}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 86f2125f6a508374c89fea25b64ef353, type: 3}
m_Name:
m_EditorClassIdentifier:
ShowDebug: 0
culledCnt: 0
--- !u!33 &966895463987172997
MeshFilter:
m_ObjectHideFlags: 0

View File

@ -114,5 +114,5 @@ MonoBehaviour:
m_PrefilterNativeRenderPass: 1
m_ShaderVariantLogLevel: 0
m_ShadowCascades: 0
superResolution: 3
superResolution: 0
vrsRate: 0

View File

@ -43,7 +43,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 386e439eeef2849448d91896c74ff1d5, type: 3}
m_Name: FSR
m_EditorClassIdentifier:
m_Active: 1
m_Active: 0
Quality: 0
v1setting:
EasuCompute: {fileID: 7200000, guid: 787b0c165dad9074e9489817de945916, type: 3}
@ -146,6 +146,20 @@ MonoBehaviour:
CullShader: {fileID: 7200000, guid: 029fde5b4200eeb4e919fe924f26fdd8, type: 3}
CullTextureSize: 64
UseThreeFrameReadback: 1
--- !u!114 &-3660967294238269343
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a5c181080479e2b4cbb22ad7fc3fa4d4, type: 3}
m_Name: HizCullingFeature1
m_EditorClassIdentifier:
m_Active: 0
_material: {fileID: 0}
--- !u!114 &-2621301742936824463
MonoBehaviour:
m_ObjectHideFlags: 0
@ -223,11 +237,15 @@ MonoBehaviour:
m_Active: 1
settings:
PyramidFunc: 0
SkipThreeMip: 0
UseThreeFrameReadback: 0
SkipThreeMip: 1
UseThreeFrameReadback: 1
ComputeShader: {fileID: 7200000, guid: b10c09f63c5a5864ca12e07b1b361d56, type: 3}
Spd: {fileID: 7200000, guid: 2c0519b45f80f5c47b4ee3edb2e7931b, type: 3}
CopyDepth: {fileID: 2100000, guid: 5f8ec7ea87b60a1448091fade6a0e68f, type: 2}
UseTowCullPass: 0
UseCompute: 0
CullMat: {fileID: 2100000, guid: ce13e27ede10980489261083a699b449, type: 2}
CullShader: {fileID: 7200000, guid: 029fde5b4200eeb4e919fe924f26fdd8, type: 3}
--- !u!114 &-1133205096357012623
MonoBehaviour:
m_ObjectHideFlags: 0
@ -293,8 +311,7 @@ MonoBehaviour:
- {fileID: 4071882023117421450}
- {fileID: -7284859345190182597}
- {fileID: -1629415145513658388}
- {fileID: -4150689035588204578}
m_RendererFeatureMap: bc3f630842f2e70dd6a559c442a94bfd4529d15534f2d3de228858dca8d12222716523fbf3439fdb7a327b7bff4bdd446ac59dfa966ffa88ca6373cd5da9013d6cff55ca297e5e908a7b3653203b82383b2141bb05fbe69aec5704e48e2763e9de07a83c50ca65c6
m_RendererFeatureMap: bc3f630842f2e70dd6a559c442a94bfd4529d15534f2d3de228858dca8d12222716523fbf3439fdb7a327b7bff4bdd446ac59dfa966ffa88ca6373cd5da9013d6cff55ca297e5e908a7b3653203b82383b2141bb05fbe69aec5704e48e2763e9
m_UseNativeRenderPass: 0
postProcessData: {fileID: 11400000, guid: 41439944d30ece34e96484bdb6645b55, type: 2}
shaders:

View File

@ -14,4 +14,8 @@ public class CullDebug : MonoBehaviour
Gizmos.DrawWireCube(Position, Size);
Gizmos.color = col;
}
private void LateUpdate()
{
}
}

View File

@ -1,4 +1,6 @@
using System;
using Unity.Collections;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering;
@ -6,7 +8,62 @@ using UnityEngine.Rendering.Universal;
namespace X.Rendering.Feature
{
public class HierarchicalZFeature : ScriptableRendererFeature
internal class CullResult : IDisposable
{
public RenderTexture ResultTex;
public NativeArray<half> ResultArray;
public bool ReadDone = false;
public bool DataReady = false;
public Action<AsyncGPUReadbackRequest> ReadBackAction;
private void ReadBack(AsyncGPUReadbackRequest readback)
{
if (readback.done && !readback.hasError)
{
var data = readback.GetData<half>();
if (ResultArray.IsCreated)
{
ResultArray.CopyFrom(data);
}
ReadDone = true;
}
else
{
ReadDone = false;
}
DataReady = true;
}
public CullResult CreateResources(int texSize)
{
ResultArray.Dispose();
ResultArray = new(texSize * texSize, Allocator.Persistent);
if (ResultTex != null)
{
ResultTex.Release();
}
ResultTex = new RenderTexture(texSize, texSize, 0, RenderTextureFormat.RHalf);
ResultTex.filterMode = FilterMode.Point;
ResultTex.enableRandomWrite = true;
ResultTex.Create();
ReadBackAction = ReadBack;
return this;
}
public void Dispose()
{
if (ResultTex != null)
{
ResultTex.Release();
}
ResultTex = null;
ResultArray.Dispose();
}
}
public class HierarchicalZOcclusionCullFeature : ScriptableRendererFeature
{
public enum EDepthPyramidFunc
{
@ -21,20 +78,28 @@ namespace X.Rendering.Feature
public EDepthPyramidFunc PyramidFunc = EDepthPyramidFunc.CopyDepth;
public bool SkipThreeMip;
// 留历史 depth做 tow pass cull
public bool UseThreeFrameReadback;
public bool UseThreeFrameReadback = true;
public ComputeShader ComputeShader;
public ComputeShader Spd;
public Material CopyDepth;
//TODO: 通过使用上一帧的摄像机位置包括矩阵和上一帧的深度图做剔除储存已经被剔除的物体和未被剔除的物体然后绘制未被剔除的物体到GBuffer包含深度图
//再二次生成HiZ DepthTexture并对已经被剔除的物体使用一遍新的深度和当前摄像机的位置做一次剔除判断
public bool UseTowCullPass;
public bool UseCompute;
public Material CullMat;
public ComputeShader CullShader;
}
[SerializeField]
private Settings settings;
private HizPass hizPass;
private HizOcclusionCullPass hizPass;
private static HierarchicalZOcclusionCullFeature instance;
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
if (renderingData.cameraData.cameraType == UnityEngine.CameraType.Game)
if ((renderingData.cameraData.cameraType == CameraType.Game))
{
renderer.EnqueuePass(hizPass);
}
@ -43,6 +108,7 @@ namespace X.Rendering.Feature
public override void Create()
{
hizPass = new(settings);
instance = this;
}
protected override void Dispose(bool disposing)
@ -54,9 +120,15 @@ namespace X.Rendering.Feature
}
}
internal class HizPass : ScriptableRenderPass, IDisposable
internal static CullResult GetCullResult()
{
return instance.hizPass.GetCullResult();
}
internal class HizOcclusionCullPass : ScriptableRenderPass, IDisposable
{
private readonly Settings settings;
private ProfilingSampler profiler;
private Vector2Int cachedHardwareTextureSize;
@ -73,15 +145,29 @@ namespace X.Rendering.Feature
RenderTexture SpdAtomicCounter;
public HizPass(Settings settings)
CullResult[] cullResults;
HizOcclusionCullPass instance;
public HizOcclusionCullPass(Settings settings)
{
profiler = new ProfilingSampler(nameof(HizOcclusionCullPass));
renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
profiler = new("DepthPyramid");
this.settings = settings;
mipLevelOffsets = new Vector2Int[16];
mipLevelSizes = new Vector2Int[16];
SpdAtomicCounter = new RenderTexture(1, 1, 0, GraphicsFormat.R32_UInt) { name = "FSR2_SpdAtomicCounter", enableRandomWrite = true };
SpdAtomicCounter.Create();
int texSize = 64;
cullResults = new CullResult[3]
{
new CullResult().CreateResources(texSize),
new CullResult().CreateResources(texSize),
new CullResult().CreateResources(texSize),
};
}
public static int HizIndex { get; private set; }
@ -100,6 +186,12 @@ namespace X.Rendering.Feature
return HizIndex;
}
public CullResult GetCullResult()
{
var hizIndex = GetHizIndex();
return cullResults[GetLastCullIndex(hizIndex)];
}
public void ComputePackedMipChainInfo(Vector2Int viewportSize)
{
if (cachedHardwareTextureSize == viewportSize && cachedSkipThreeMip == settings.SkipThreeMip)
@ -111,20 +203,25 @@ namespace X.Rendering.Feature
cachedSkipThreeMip = settings.SkipThreeMip;
mip0SizeNOP = viewportSize;
// PowerOfTwo 不会留缝隙
int resizeX = settings.PyramidFunc == EDepthPyramidFunc.SPD || Mathf.IsPowerOfTwo(viewportSize.x) ? viewportSize.x : Mathf.NextPowerOfTwo(viewportSize.x);
int resizeY = settings.PyramidFunc == EDepthPyramidFunc.SPD || Mathf.IsPowerOfTwo(viewportSize.y) ? viewportSize.y : Mathf.NextPowerOfTwo(viewportSize.y);
int resizeX = viewportSize.x;
int resizeY = viewportSize.y;
resizeX = Mathf.IsPowerOfTwo(viewportSize.x) ? viewportSize.x : Mathf.NextPowerOfTwo(viewportSize.x);
resizeY = Mathf.IsPowerOfTwo(viewportSize.y) ? viewportSize.y : Mathf.NextPowerOfTwo(viewportSize.y);
if (resizeX > viewportSize.x)
resizeX /= 2;
if (resizeY > viewportSize.y)
resizeY /= 2;
Vector2Int hardwareTextureSize = new Vector2Int(resizeX, resizeY);
mipLevelOffsets[0] = Vector2Int.zero;
mipLevelSizes[0] = Vector2Int.zero;
int mipLevel = 0;
Vector2Int mipSize = hardwareTextureSize;
Vector2Int texSize = Vector2Int.zero;
do
{
mipLevel++;
mipLevelCount = 1 + (int)math.floor(math.log2(math.max(resizeX, resizeY)));
// Round up.
for (int mipLevel = 1; mipLevel < mipLevelCount; mipLevel++)
{
mipSize.x = System.Math.Max(1, (mipSize.x + 1) >> 1);
mipSize.y = System.Math.Max(1, (mipSize.y + 1) >> 1);
@ -156,11 +253,16 @@ namespace X.Rendering.Feature
texSize.x = System.Math.Max(texSize.x, mipBegin.x + mipSize.x);
texSize.y = System.Math.Max(texSize.y, mipBegin.y + mipSize.y);
}
while ((mipSize.x > 1) || (mipSize.y > 1));
mipLevelSizes[0] = hardwareTextureSize;
//RT实际大小
depthPyramidTextureSize = new Vector2Int((int)Mathf.Ceil((float)texSize.x), (int)Mathf.Ceil((float)texSize.y));
mipLevelCount = mipLevel + 1;
for (int i = 0;i < mipLevelSizes.Length; i++)
{
mipOffsetAndSizes[i] = new Vector4(mipLevelOffsets[i].x, mipLevelOffsets[i].y, mipLevelSizes[i].x,
mipLevelSizes[i].y);
}
}
private static int[] depthMipId = null;
@ -180,20 +282,20 @@ namespace X.Rendering.Feature
cmd.BeginSample(sampleName);
cmd.SetViewProjectionMatrices(Matrix4x4.identity, Matrix4x4.identity);
cmd.SetGlobalTexture(HizShaderIds.DepthInputId, depthTex);
for (int i = 1; i < mipLevelCount; i++)
for (int i = 0; i < mipLevelCount; i++)
{
var index = i;
if (settings.SkipThreeMip && i < 3 + 1)
if (settings.SkipThreeMip && i < 3)
{
continue;
}
else if (settings.SkipThreeMip && i == 3 + 1)
else if (settings.SkipThreeMip && i == 3)
{
index = 1;
index = 0;
}
var mipSize = mipLevelSizes[i];
var inputMipSize = index == 1 ? mip0SizeNOP : mipLevelSizes[i - 1];
var inputMipSize = index == 0 ? mip0SizeNOP : mipLevelSizes[i - 1];
var texId = depthMipId[i];
cmd.SetGlobalVector(HizShaderIds.InputScaleAndMaxIndexId, new Vector4(inputMipSize.x / (float)mipSize.x, inputMipSize.y / (float)mipSize.y, inputMipSize.x - 1, inputMipSize.y - 1));
cmd.GetTemporaryRT(texId, mipSize.x, mipSize.y, 0, FilterMode.Point, RenderTextureFormat.RFloat);
@ -231,7 +333,6 @@ namespace X.Rendering.Feature
cmd.SetGlobalTexture(HizShaderIds.DepthInputId, texId);
var mipSize = mipLevelSizes[i];
var mipOffset = mipLevelOffsets[i];
mipOffsetAndSizes[i] = new (mipOffset.x, mipOffset.y, mipSize.x, mipSize.y);
cmd.SetViewport(new Rect(mipOffset.x, mipOffset.y, mipSize.x, mipSize.y));
cmd.DrawMesh(RenderingUtils.fullscreenMesh, Matrix4x4.identity, settings.CopyDepth, 0, 1);
}
@ -342,12 +443,7 @@ namespace X.Rendering.Feature
cmd.SetComputeIntParam(settings.Spd, "mips", mipLevelCount);
cmd.SetComputeIntParam(settings.Spd, "numWorkGroups", dispatchX);
for (int i = 0; i < mipLevelCount; i++)
{
var mipSize = mipLevelSizes[i];
var mipOffset = mipLevelOffsets[i];
mipOffsetAndSizes[i] = new(mipOffset.x, mipOffset.y, mipSize.x, mipSize.y);
}
cmd.SetComputeVectorArrayParam(settings.Spd, "_MipOffsetAndSizeArray", mipOffsetAndSizes);
cmd.SetComputeTextureParam(settings.Spd, 0, "_InputDepth", depthTex);
@ -358,16 +454,40 @@ namespace X.Rendering.Feature
cmd.SetGlobalTexture(HizShaderIds.DepthPyramidTexId, hizBuffer);
}
private void DoCulling(ScriptableRenderContext context, ref RenderingData renderingData)
{
var cmd = renderingData.commandBuffer;;
cmd.BeginSample("Hiz-Culling");
var hizIndex = GetHizIndex();
var cullResult = cullResults[hizIndex];
var world2Project = renderingData.cameraData.GetGPUProjectionMatrix() *
renderingData.cameraData.GetViewMatrix();
if (settings.UseCompute)
{
}
else
{
cmd.SetGlobalMatrix(HizShaderIds.GPUCullingVPId, world2Project);
cmd.SetRenderTarget(cullResult.ResultTex, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store);
cmd.DrawMesh(RenderingUtils.fullscreenMesh, Matrix4x4.identity, settings.CullMat, 0, 0);
cmd.RequestAsyncReadback(cullResult.ResultTex, 0, cullResult.ReadBackAction);
//cmd.RequestAsyncReadbackIntoNativeArray(ref cullResult.ResultArray, cullResult.ResultTex, 0, cullResult.ReadBackAction);
cullResult.DataReady = false;
}
cmd.EndSample("Hiz-Culling");
}
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
var cmd = renderingData.commandBuffer;
using var soc = new ProfilingScope(cmd, profiler);
Texture depthTex = Shader.GetGlobalTexture(HizShaderIds.CameraDepthTextureId);
if(depthTex == null)
{
return;
}
using var scp = new ProfilingScope(cmd, profiler);
ComputePackedMipChainInfo(new Vector2Int(depthTex.width, depthTex.height));
switch (settings.PyramidFunc)
@ -386,9 +506,40 @@ namespace X.Rendering.Feature
}
cmd.SetGlobalVector(HizShaderIds.Mip0SizeId, new Vector4(mip0SizeNOP.x, mip0SizeNOP.y, 0, 0));
cmd.SetGlobalVector(HizShaderIds.MipmapLevelMinMaxIndexId, new Vector4(1, mipLevelCount, 0, 0));
cmd.SetGlobalVectorArray(HizShaderIds.MipOffsetAndSizeArrayId, mipOffsetAndSizes);
if (settings.SkipThreeMip)
{
cmd.SetGlobalVector(HizShaderIds.MipmapLevelMinMaxIndexId, new Vector4(4, mipLevelCount - 1, 0, 0));
}
else
{
cmd.SetGlobalVector(HizShaderIds.MipmapLevelMinMaxIndexId, new Vector4(1, mipLevelCount - 1, 0, 0));
}
cmd.SetGlobalVectorArray(HizShaderIds.MipOffsetAndSizeArrayId, mipOffsetAndSizes);
DoCulling(context, ref renderingData);
}
private int GetLastCullIndex(int hizIndex)
{
if (!settings.UseThreeFrameReadback)
{
return 0;
}
switch (hizIndex)
{
case 0:
return 1;
case 1:
return 2;
case 2:
return 0;
default:
throw new ArgumentException("参数错误 hizIndex:" + hizIndex);
}
}
public void Dispose()
{
@ -403,6 +554,11 @@ namespace X.Rendering.Feature
var rt = depthPyramidTexs[i];
rt?.Release();
}
for (int i = 0; i < cullResults.Length; i++)
{
cullResults[i]?.Dispose();
}
}
}
}

View File

@ -0,0 +1,19 @@
using UnityEngine;
namespace X.Rendering.Feature
{
[RequireComponent(typeof(Renderer))]
public class HizObject : MonoBehaviour
{
private void OnEnable()
{
HizObjectsManager.Instance.Register(GetComponent<MeshRenderer>());
}
private void OnDisable()
{
HizObjectsManager.Instance.UnRegister(GetComponent<MeshRenderer>());
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: a0b8751ae563c82438124306d98fffd5
guid: 1ff7514cf209ebe4eb6d41ea454b721c
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -0,0 +1,231 @@
using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
namespace X.Rendering.Feature
{
public class HizObjectsManager : MonoBehaviour
{
public bool ShowDebug = false;
Texture2D centerTex;
Texture2D sizeTex;
private static HizObjectsManager intstance;
public static HizObjectsManager Instance => intstance;
private float4[] centers;
private float4[] extents;
private float3x2[] aabbs;
private List<MeshRenderer> renderers = new();
private bool[] cullResults;
private bool needUpdateAABB = true;
public int culledCnt = 0;
private void Awake()
{
intstance = new();
}
void Start()
{
int texSize = 64;
centerTex = new Texture2D(texSize, texSize, TextureFormat.RGBAFloat, 0, true);
centerTex.filterMode = FilterMode.Point;
sizeTex = new Texture2D(texSize, texSize, TextureFormat.RGBAFloat, 0, true);
sizeTex.filterMode = FilterMode.Point;
Shader.SetGlobalTexture(HizShaderIds.ObjectAABBCenterId, centerTex);
Shader.SetGlobalTexture(HizShaderIds.ObjectAABBSizeId, sizeTex);
}
void Update()
{
if (needUpdateAABB)
{
needUpdateAABB = false;
UpdateAABB(centerTex, sizeTex);
}
}
private void OnDestroy()
{
Texture.DestroyImmediate(centerTex);
Texture.DestroyImmediate(sizeTex);
}
private void LateUpdate()
{
ApplyCull(HierarchicalZOcclusionCullFeature.GetCullResult());
}
public void Register(MeshRenderer renderer)
{
if (!renderers.Contains(renderer))
{
renderers.Add(renderer);
needUpdateAABB = true;
}
}
public void UnRegister(MeshRenderer renderer)
{
if(renderers.Remove(renderer))
{
needUpdateAABB = true;
}
}
public void UpdateAABB(ComputeBuffer aabbBuffer)
{
if (aabbBuffer == null || aabbs.Length != renderers.Count)
{
aabbs = new float3x2[renderers.Count];
}
for (int i = 0; i < renderers.Count; i++)
{
var rdr = renderers[i];
if (rdr != null)
{
Bounds bounds = rdr.bounds;
aabbs[i] = new float3x2(bounds.center, bounds.size);
}
else
{
aabbs[i] = float3x2.zero;
}
}
aabbBuffer.SetData(aabbs);
}
public void UpdateAABB(Texture2D centerTex, Texture2D sizeTex)
{
if (centers == null || centers.Length != centerTex.width * centerTex.height)
{
centers = new float4[centerTex.width * centerTex.height];
extents = new float4[centerTex.width * centerTex.height];
}
if (renderers.Count == 0)
{
foreach (var item in GameObject.FindObjectsOfType<MeshRenderer>())
{
if (item.gameObject.isStatic)
{
Register(item);
}
}
}
if (cullResults == null || cullResults.Length != renderers.Count)
{
cullResults = new bool[renderers.Count];
}
int nullCnt = 0;
for (int i = 0; i < renderers.Count; i++)
{
var rdr = renderers[i];
if (rdr != null)
{
Bounds bounds = rdr.bounds;
centers[i] = new float4(bounds.center, 1);
extents[i] = new float4(bounds.extents, 1);
}
else
{
nullCnt++;
centers[i] = float4.zero;
extents[i] = float4.zero;
}
}
if (nullCnt == renderers.Count)
{
renderers.Clear();
}
centerTex.SetPixelData(centers, 0);
centerTex.Apply();
sizeTex.SetPixelData(extents, 0);
sizeTex.Apply();
Shader.SetGlobalTexture(HizShaderIds.ObjectAABBCenterId, centerTex);
Shader.SetGlobalTexture(HizShaderIds.ObjectAABBSizeId, sizeTex);
}
private void ApplyCull(CullResult cullResult)
{
if (cullResult.ReadDone)
{
culledCnt = 0;
for (var i = 0; i < renderers.Count; i++)
{
var rdr = renderers[i];
if (rdr)
{
bool needCull = cullResult.ResultArray[i] > 0;
#if UNITY_EDITOR
if (ShowDebug && rdr.GetComponent<CullDebug>() is CullDebug cullDebug && cullDebug)
{
cullDebug.Index = i;
cullDebug.IsCulled = needCull;
cullDebug.Position = centers[i].xyz;
cullDebug.Size = extents[i].xyz;
}
#endif
if (needCull)
{
culledCnt++;
}
if (cullResults[i] != needCull)
{
if (needCull)
{
culledCnt++;
rdr.renderingLayerMask = 0;
}
else
{
rdr.renderingLayerMask = 1;
}
cullResults[i] = needCull;
}
}
}
//Debug.Log("frameCount: " + Time.frameCount + " Buffer index " + Time.frameCount % 3 + " : cullcount = " + culledCnt);
}
else
{
ResetRenderer();
}
}
private void ResetRenderer()
{
for (var i = 0; i < renderers.Count; i++)
{
var rdr = renderers[i];
if (rdr)
{
if (cullResults[i])
{
rdr.renderingLayerMask = 1;
cullResults[i] = false;
}
}
}
}
private void OnDisable()
{
ResetRenderer();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 86f2125f6a508374c89fea25b64ef353
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,334 +0,0 @@
using System;
using System.Collections.Generic;
using Unity.Collections;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using static X.Rendering.Feature.HziCullingFeature;
namespace X.Rendering.Feature
{
public class SceneRenderObjects
{
private static SceneRenderObjects intstance;
public static SceneRenderObjects Instance => intstance;
private float4[] centers;
private float4[] sizes;
private float3x2[] aabbs;
List<Renderer> renderers = new();
static SceneRenderObjects()
{
intstance = new SceneRenderObjects();
}
public void Register(Renderer renderer)
{
if(!renderers.Contains(renderer))
{
renderers.Add(renderer);
}
}
public void UnRegister(Renderer renderer)
{
renderers.Remove(renderer);
}
public void UpdateAABB(ComputeBuffer aabbBuffer)
{
if(aabbBuffer == null || aabbs.Length != renderers.Count)
{
aabbs = new float3x2[renderers.Count];
}
for (int i = 0; i < renderers.Count; i++)
{
var rdr = renderers[i];
if (rdr != null)
{
Bounds bounds = rdr.bounds;
aabbs[i] = new float3x2(bounds.center,bounds.size);
}
else
{
aabbs[i] = float3x2.zero;
}
}
aabbBuffer.SetData(aabbs);
}
public void UpdateAABB(Texture2D centerTex, Texture2D sizeTex)
{
if (centers == null || centers.Length != centerTex.width * centerTex.height)
{
centers = new float4[centerTex.width * centerTex.height];
sizes = new float4[centerTex.width * centerTex.height];
}
if (renderers.Count == 0)
{
foreach (var item in GameObject.FindObjectsOfType<MeshRenderer>())
{
Register(item);
}
}
int nullCnt = 0;
for (int i = 0; i < renderers.Count; i++)
{
var rdr = renderers[i];
if (rdr != null)
{
Bounds bounds = rdr.bounds;
centers[i] = new float4(bounds.center, 1);
sizes[i] = new float4(bounds.size, 1);
}
else
{
nullCnt++;
centers[i] = float4.zero;
sizes[i] = float4.zero;
}
}
if (nullCnt == renderers.Count)
{
renderers.Clear();
}
centerTex.SetPixelData(centers, 0);
centerTex.Apply();
sizeTex.SetPixelData(sizes, 0);
sizeTex.Apply();
}
internal void ApplyCull(CullResult cullResult)
{
if (cullResult.ReadDone)
{
for (var i = 0; i < renderers.Count; i++)
{
var rdr = renderers[i];
if (rdr)
{
#if UNITY_EDITOR
if(rdr.GetComponent<CullDebug>() is CullDebug cullDebug && cullDebug)
{
cullDebug.Index = i;
cullDebug.IsCulled = cullResult.ResultArray[i] > 0;
cullDebug.Position = centers[i].xyz;
cullDebug.Size = sizes[i].xyz;
}
#endif
//if (cullResult.ResultArray[i] > 0)
//{
// rdr.renderingLayerMask = 0;
//}
//else
{
rdr.renderingLayerMask = 1;
}
}
}
}
}
}
public class HziCullingFeature : ScriptableRendererFeature
{
[Serializable]
public class Settings
{
//TODO: 通过使用上一帧的摄像机位置包括矩阵和上一帧的深度图做剔除储存已经被剔除的物体和未被剔除的物体然后绘制未被剔除的物体到GBuffer包含深度图
//再二次生成HiZ DepthTexture并对已经被剔除的物体使用一遍新的深度和当前摄像机的位置做一次剔除判断
public bool UseTowCullPass;
public bool UseTextureAABB = true;
public bool UseCompute;
public Material CullMat;
public ComputeShader CullShader;
[Range(32, 128)]
public int CullTextureSize = 64;
public bool UseThreeFrameReadback = true;
}
[SerializeField]
private Settings settings;
private CullPass cullPass;
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
if (renderingData.cameraData.cameraType == CameraType.Game)
{
renderer.EnqueuePass(cullPass);
}
}
public override void Create()
{
cullPass = new(settings);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
cullPass.Dispose();
}
internal class CullResult : IDisposable
{
public RenderTexture ResultTex;
public NativeArray<half> ResultArray;
public bool ReadDone = false;
public Action<AsyncGPUReadbackRequest> ReadBackAction;
private void ReadBack(AsyncGPUReadbackRequest readback)
{
if (readback.done && !readback.hasError)
{
var data = readback.GetData<half>();
if (ResultArray.IsCreated)
{
ResultArray.CopyFrom(data);
}
ReadDone = true;
}
else
{
ReadDone = false;
}
}
public CullResult CreateResources(int texSize)
{
ResultArray.Dispose();
ResultArray = new NativeArray<half>(texSize * texSize, Allocator.Persistent);
if(ResultTex != null)
{
ResultTex.Release();
}
ResultTex = new(texSize, texSize, 0, GraphicsFormat.R16_SFloat);
ResultTex.filterMode = FilterMode.Point;
ReadBackAction = ReadBack;
return this;
}
public void Dispose()
{
if (ResultTex != null)
{
ResultTex.Release();
}
ResultTex = null;
ResultArray.Dispose();
}
}
internal class CullPass : ScriptableRenderPass, IDisposable
{
private Settings settings;
private ProfilingSampler profiler;
Texture2D centerTex;
Texture2D sizeTex;
CullResult[] cullResults;
public CullPass(Settings settings)
{
profiler = new("Hi-Z Culling");
this.settings = settings;
int texSize = settings.CullTextureSize;
centerTex = new Texture2D(texSize, texSize, GraphicsFormat.R32G32B32A32_SFloat, TextureCreationFlags.None);
centerTex.filterMode = FilterMode.Point;
sizeTex = new Texture2D(texSize, texSize, GraphicsFormat.R32G32B32A32_SFloat, TextureCreationFlags.None);
sizeTex.filterMode = FilterMode.Point;
cullResults = new CullResult[3]
{
new CullResult().CreateResources(texSize),
new CullResult().CreateResources(texSize),
new CullResult().CreateResources(texSize),
};
renderPassEvent = RenderPassEvent.AfterRendering;
}
public void Dispose()
{
Texture.DestroyImmediate(centerTex);
Texture.DestroyImmediate(sizeTex);
for (int i = 0; i < cullResults.Length; i++)
{
cullResults[i]?.Dispose();
}
}
private int GetHizIndex()
{
if (settings.UseThreeFrameReadback)
{
return Time.frameCount % 3;
}
return 0;
}
private int GetLastCullIndex(int hizIndex)
{
switch (hizIndex)
{
case 0:
return 1;
case 1:
return 2;
case 2:
return 0;
default:
throw new ArgumentException("参数错误 hizIndex:" + hizIndex);
}
}
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
var cmd = CommandBufferPool.Get("HizBuf");
using var soc = new ProfilingScope(cmd, profiler);
cmd.Clear();
if (settings.UseTextureAABB)
{
SceneRenderObjects.Instance.UpdateAABB(centerTex, sizeTex);
}
var hizIndex = GetHizIndex();
var cullResult = cullResults[hizIndex];
SceneRenderObjects.Instance.ApplyCull(cullResults[GetLastCullIndex(hizIndex)]);
cullResult.ReadDone = false;
var world2Project = renderingData.cameraData.GetGPUProjectionMatrix() * renderingData.cameraData.GetViewMatrix();
if(settings.UseCompute)
{
}
else
{
world2Project.m11 *= -1;
world2Project.m13 *= -1;
cmd.SetGlobalMatrix(HizShaderIds.GPUCullingVPId, world2Project);
cmd.SetGlobalTexture(HizShaderIds.ObjectAABBCenterId, centerTex);
cmd.SetGlobalTexture(HizShaderIds.ObjectAABBSizeId, sizeTex);
cmd.SetRenderTarget(cullResult.ResultTex, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store);
cmd.DrawMesh(RenderingUtils.fullscreenMesh, Matrix4x4.identity, settings.CullMat, 0, 0);
var temp = new NativeArray<half>(centerTex.width * sizeTex.height, Allocator.Persistent);
cmd.RequestAsyncReadbackIntoNativeArray(ref temp, cullResult.ResultTex, 0, cullResult.ReadBackAction);
//cmd.RequestAsyncReadbackIntoNativeArray(ref cullResult.ResultArray, cullResult.ResultTex, 0, cullResult.ReadBackAction);
}
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
}
}
}

View File

@ -2,6 +2,9 @@ Shader "Hidden/CopyDepth"
{
SubShader
{
ZTest Off
ZWrite Off
Pass
{
HLSLPROGRAM

View File

@ -2,6 +2,9 @@
{
SubShader
{
ZTest Off
ZWrite Off
Pass
{
HLSLPROGRAM
@ -55,13 +58,21 @@
float3(-1, -1, 1),
float3(-1, -1, -1)
};
float3 TransferNDC(float3 pos)
{
float4 ndc = mul(_GPUCullingVP, float4(pos, 1.0));
ndc.xyz /= ndc.w;
ndc.xy = ndc.xy * 0.5f + 0.5f;
ndc.y = 1 - ndc.y;
return ndc.xyz;
}
half CullFrag(Varyings input) : SV_Target
{
float2 uv = input.uv;
float4 aabbCenter = SAMPLE_TEXTURE2D_LOD(_ObjectAABBTexture0, sampler_ObjectAABBTexture0, uv,0.0);
float4 aabbCenter = SAMPLE_TEXTURE2D_LOD(_ObjectAABBTexture0, sampler_ObjectAABBTexture0, uv, 0.0);
float4 aabbSize = SAMPLE_TEXTURE2D_LOD(_ObjectAABBTexture1, sampler_ObjectAABBTexture1, uv, 0.0);
float3 aabbExtent = aabbSize.xyz * 0.5;//贴图可以直接存extent
float3 aabbExtent = aabbSize.xyz;
UNITY_BRANCH
if (aabbCenter.a == 0.0)
{
@ -76,11 +87,16 @@
float2 maxXY = 0;
float2 minXY = 1;
for(uint i = 0; i < 8; ++i)
for (uint i = 0; i < 8; ++i)
{
float3 boxCorner = aabbCenter + aabbExtent * aggressiveExtentArray[i];
float4 clipPos = mul(_GPUCullingVP, float4(boxCorner, 1));
clipPos /= clipPos.w;
clipPos.xy = clipPos.xy * 0.5f + 0.5f;
#if UNITY_UV_STARTS_AT_TOP
clipPos.y = 1 - clipPos.y;
#endif
minXY = min(clipPos.xy, minXY);
maxXY = max(clipPos.xy, maxXY);
#ifdef UNITY_REVERSED_Z
@ -91,11 +107,16 @@
}
float4 boxUVs = float4(minXY, maxXY);
boxUVs = saturate(boxUVs * 0.5 + 0.5);
float2 size = (boxUVs.zw - boxUVs.xy) * _Mip0Size.xy;
float mip = (log2(max(size.x, size.y) / 2.0f));
mip = ceil(mip);
// if( boxUVs.x < 0 || boxUVs.y < 0 || boxUVs.x > 1 || boxUVs.y > 1 || boxUVs.z >1 || boxUVs.z<0)
// {
// return 1;
// }
float2 size = (boxUVs.zw - boxUVs.xy) * _Mip0Size.xy;
float mip = (log2(max(size.x, size.y)));
mip = ceil(mip) ;
mip = clamp(mip, _MipmapLevelMinMaxIndex.x, _MipmapLevelMinMaxIndex.y);
// float level_lower = max(mip - 1, 0);
@ -111,23 +132,22 @@
float4 offsetAndSize = _MipOffsetAndSize[mip];
int4 pxMinMax = boxUVs * offsetAndSize.zwzw + offsetAndSize.xyxy;
float4 depth = float4(LOAD_TEXTURE2D_LOD(_DepthPyramidTexture, pxMinMax.xy,0).r,
LOAD_TEXTURE2D_LOD(_DepthPyramidTexture, pxMinMax.zy,0).r,
LOAD_TEXTURE2D_LOD(_DepthPyramidTexture, pxMinMax.xw,0).r,
LOAD_TEXTURE2D_LOD(_DepthPyramidTexture, pxMinMax.zw,0).r
float4 depth = float4(LOAD_TEXTURE2D_LOD(_DepthPyramidTexture, pxMinMax.xy, 0).r,
LOAD_TEXTURE2D_LOD(_DepthPyramidTexture, pxMinMax.zy, 0).r,
LOAD_TEXTURE2D_LOD(_DepthPyramidTexture, pxMinMax.xw, 0).r,
LOAD_TEXTURE2D_LOD(_DepthPyramidTexture, pxMinMax.zw, 0).r
);
#ifdef UNITY_REVERSED_Z
depth.xy = min(depth.xy, depth.zw);
depth.x = min(depth.x, depth.y);
return minZ >= depth.x ? 0 : 1;
return minZ > depth.x ? 0 : 1;
#else
depth.xy = max(depth.xy, depth.zw);
depth.x = max(depth.x, depth.y);
return minZ <= depth.x ? 1 : 0;
return minZ < depth.x ? 1 : 0;
#endif
}
ENDHLSL
}