Added AoVolumePassContext;

Added AoVolumePassBuffer;

Changed center of each OBB in FrustumCullingJob from world position to camera-relative world position;
Changed outputVolumeData and outputVolumeBounds from NativeList to NativeArray;
This commit is contained in:
2025-03-02 17:59:26 +09:00
parent d0fc79923c
commit f9ed0e7769
21 changed files with 547 additions and 583 deletions

View File

@@ -0,0 +1,164 @@
using System.Runtime.CompilerServices;
using System.Threading;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using Unity.Mathematics;
using static Unity.Mathematics.math;
namespace Misaki.AoVolume
{
//[BurstCompile]
internal struct FrustumCullingJob : IJobParallelFor
{
[ReadOnly]
public NativeArray<VolumeData> inputVolumeDatas;
public NativeFrustum frustum;
public float3 cameraPosition;
public bool useCameraRelativeRendering;
[WriteOnly]
public NativeArray<int> visibleCounter;
[WriteOnly]
public NativeArray<VolumeData> outputVolumeData;
[WriteOnly]
public NativeArray<OrientedBoundingBox> outputVolumeBounds;
private struct FrustumPlane
{
public float3 normal;
public float distance;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float3 GetForward(OrientedBoundingBox obb)
{
return cross(obb.up, obb.right);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool CheckOverlap(OrientedBoundingBox obb, float3 planeNormal, float planeDistance)
{
var maxHalfDiagonalProjection = obb.extentX * abs(dot(planeNormal, obb.right)) +
obb.extentY * abs(dot(planeNormal, obb.up)) +
obb.extentZ * abs(dot(planeNormal, GetForward(obb)));
var centerProjection = dot(planeNormal, obb.center) + planeDistance;
return maxHalfDiagonalProjection + centerProjection >= 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private readonly float4x4 ApplyCameraTranslationToMatrix(float4x4 modelMatrix)
{
if (useCameraRelativeRendering)
{
modelMatrix.c3.x -= cameraPosition.x;
modelMatrix.c3.y -= cameraPosition.y;
modelMatrix.c3.z -= cameraPosition.z;
}
return modelMatrix;
}
private bool FrustumObbIntersection(OrientedBoundingBox obb)
{
var overlap = CheckOverlap(obb, frustum.normal0, frustum.dist0);
overlap &= CheckOverlap(obb, frustum.normal1, frustum.dist1);
overlap &= CheckOverlap(obb, frustum.normal2, frustum.dist2);
overlap &= CheckOverlap(obb, frustum.normal3, frustum.dist3);
overlap &= CheckOverlap(obb, frustum.normal4, frustum.dist4);
overlap &= CheckOverlap(obb, frustum.normal5, frustum.dist5);
var planes = new NativeArray<FrustumPlane>(3, Allocator.Temp);
planes[0] = new FrustumPlane
{
normal = obb.right,
distance = obb.extentX
};
planes[1] = new FrustumPlane
{
normal = obb.up,
distance = obb.extentY
};
planes[2] = new FrustumPlane
{
normal = GetForward(obb),
distance = obb.extentZ
};
for (var i = 0; i < 3; i++)
{
// We need a separate counter for the "box fully inside frustum" case.
var outsidePos = true; // Positive normal
var outsideNeg = true; // Reversed normal
var center = new float3(obb.center);
var proj = 0.0f;
// Merge 2 loops. Continue as long as all points are outside either plane.
// Corner 0
proj = dot(planes[i].normal, frustum.corner0.xyz - center);
outsidePos = outsidePos && (proj > planes[i].distance);
outsideNeg = outsideNeg && (-proj > planes[i].distance);
// Corner 1
proj = dot(planes[i].normal, frustum.corner1.xyz - center);
outsidePos = outsidePos && (proj > planes[i].distance);
outsideNeg = outsideNeg && (-proj > planes[i].distance);
// Corner 2
proj = dot(planes[i].normal, frustum.corner2.xyz - center);
outsidePos = outsidePos && (proj > planes[i].distance);
outsideNeg = outsideNeg && (-proj > planes[i].distance);
// Corner 3
proj = dot(planes[i].normal, frustum.corner3.xyz - center);
outsidePos = outsidePos && (proj > planes[i].distance);
outsideNeg = outsideNeg && (-proj > planes[i].distance);
// Corner 4
proj = dot(planes[i].normal, frustum.corner4.xyz - center);
outsidePos = outsidePos && (proj > planes[i].distance);
outsideNeg = outsideNeg && (-proj > planes[i].distance);
// Corner 5
proj = dot(planes[i].normal, frustum.corner5.xyz - center);
outsidePos = outsidePos && (proj > planes[i].distance);
outsideNeg = outsideNeg && (-proj > planes[i].distance);
// Corner 6
proj = dot(planes[i].normal, frustum.corner6.xyz - center);
outsidePos = outsidePos && (proj > planes[i].distance);
outsideNeg = outsideNeg && (-proj > planes[i].distance);
// Corner 7
proj = dot(planes[i].normal, frustum.corner7.xyz - center);
outsidePos = outsidePos && (proj > planes[i].distance);
outsideNeg = outsideNeg && (-proj > planes[i].distance);
// Combine data of the previous plane
overlap = overlap && !(outsidePos || outsideNeg);
}
planes.Dispose();
return overlap;
}
public unsafe void Execute(int index)
{
var volumeData = inputVolumeDatas[index];
var obb = new OrientedBoundingBox(ApplyCameraTranslationToMatrix(volumeData.worldMatrix));
if (distance(cameraPosition, obb.center) > volumeData.cullDistance)
{
return;
}
if (FrustumObbIntersection(obb))
{
var currentIndex = Interlocked.Increment(ref UnsafeUtility.AsRef<int>(visibleCounter.GetUnsafePtr())) - 1;
outputVolumeData[currentIndex] = volumeData;
outputVolumeBounds[currentIndex] = obb;
}
}
}
}