Added frustum culling;

Added geometry data;

Changed name of VolumeObject to AoVolume;

Removed the capsule type in VolumeType, all volumes are box volume now;
This commit is contained in:
2025-02-19 23:20:38 +09:00
parent de8eafb713
commit ef2bdeac98
18 changed files with 348 additions and 39 deletions

View File

@@ -0,0 +1,120 @@
#ifndef FRUSTUM_CULLING
#define FRUSTUM_CULLING
#include "Packages/com.misaki.ao-volume/Runtime/Shader/Includes/GeometryData.cs.hlsl"
struct FrustumPlane
{
float3 normal;
float dist;
};
struct Frustum
{
FrustumPlane planes[6];
// Needs to be aligned on a float4, a bit of waste here
float4 corners[8];
};
float3 GetForward(OrientedBoundingBox value)
{
return cross(value.up, value.right);
}
bool CheckOverlap(OrientedBoundingBox obb, float3 planeNormal, float planeDistance)
{
// Max projection of the half-diagonal onto the normal (always positive).
float maxHalfDiagProj = obb.extent.x * abs(dot(planeNormal, obb.right))
+ obb.extent.y * abs(dot(planeNormal, obb.up))
+ obb.extent.z * abs(dot(planeNormal, GetForward(obb)));
// Positive distance -> center in front of the plane.
// Negative distance -> center behind the plane (outside).
float centerToPlaneDist = dot(planeNormal, obb.center) + planeDistance;
// outside = maxHalfDiagProj < -centerToPlaneDist
// outside = maxHalfDiagProj + centerToPlaneDist < 0
// overlap = overlap && !outside
return (maxHalfDiagProj + centerToPlaneDist >= 0);
}
bool FrustumOBBIntersection(OrientedBoundingBox obb, FrustumGPU frustum)
{
// Test the OBB against frustum planes. Frustum planes are inward-facing.
// The OBB is outside if it's entirely behind one of the frustum planes.
// See "Real-Time Rendering", 3rd Edition, 16.10.2.
bool overlap = CheckOverlap(obb, frustum.normal0, frustum.dist0);
overlap = overlap && CheckOverlap(obb, frustum.normal1, frustum.dist1);
overlap = overlap && CheckOverlap(obb, frustum.normal2, frustum.dist2);
overlap = overlap && CheckOverlap(obb, frustum.normal3, frustum.dist3);
overlap = overlap && CheckOverlap(obb, frustum.normal4, frustum.dist4);
overlap = overlap && CheckOverlap(obb, frustum.normal5, frustum.dist5);
// Test the frustum corners against OBB planes. The OBB planes are outward-facing.
// The frustum is outside if all of its corners are entirely in front of one of the OBB planes.
// See "Correct Frustum Culling" by Inigo Quilez.
// We can exploit the symmetry of the box by only testing against 3 planes rather than 6.
FrustumPlane planes[3];
planes[0].normal = obb.right;
planes[0].dist = obb.extent.x;
planes[1].normal = obb.up;
planes[1].dist = obb.extent.y;
planes[2].normal = GetForward(obb);
planes[2].dist = obb.extent.z;
for (int i = 0; overlap && i < 3; i++)
{
// We need a separate counter for the "box fully inside frustum" case.
bool outsidePos = true; // Positive normal
bool outsideNeg = true; // Reversed normal
float proj = 0.0;
// Merge 2 loops. Continue as long as all points are outside either plane.
// Corner 0
proj = dot(planes[i].normal, frustum.corner0.xyz - obb.center);
outsidePos = outsidePos && (proj > planes[i].dist);
outsideNeg = outsideNeg && (-proj > planes[i].dist);
// Corner 1
proj = dot(planes[i].normal, frustum.corner1.xyz - obb.center);
outsidePos = outsidePos && (proj > planes[i].dist);
outsideNeg = outsideNeg && (-proj > planes[i].dist);
// Corner 2
proj = dot(planes[i].normal, frustum.corner2.xyz - obb.center);
outsidePos = outsidePos && (proj > planes[i].dist);
outsideNeg = outsideNeg && (-proj > planes[i].dist);
// Corner 3
proj = dot(planes[i].normal, frustum.corner3.xyz - obb.center);
outsidePos = outsidePos && (proj > planes[i].dist);
outsideNeg = outsideNeg && (-proj > planes[i].dist);
// Corner 4
proj = dot(planes[i].normal, frustum.corner4.xyz - obb.center);
outsidePos = outsidePos && (proj > planes[i].dist);
outsideNeg = outsideNeg && (-proj > planes[i].dist);
// Corner 5
proj = dot(planes[i].normal, frustum.corner5.xyz - obb.center);
outsidePos = outsidePos && (proj > planes[i].dist);
outsideNeg = outsideNeg && (-proj > planes[i].dist);
// Corner 6
proj = dot(planes[i].normal, frustum.corner6.xyz - obb.center);
outsidePos = outsidePos && (proj > planes[i].dist);
outsideNeg = outsideNeg && (-proj > planes[i].dist);
// Corner 7
proj = dot(planes[i].normal, frustum.corner7.xyz - obb.center);
outsidePos = outsidePos && (proj > planes[i].dist);
outsideNeg = outsideNeg && (-proj > planes[i].dist);
// Combine data of the previous plane
overlap = overlap && !(outsidePos || outsideNeg);
}
return overlap;
}
#endif

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: afe7ff9488a706a4591d7d9b59aa8c94
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,74 @@
using UnityEngine;
using UnityEngine.Rendering;
namespace Misaki.AoVolume
{
[GenerateHLSL(PackingRules.Exact, false)]
internal struct FrustumGPU
{
// The data of the 6 planes of the frustum
public Vector3 normal0;
public float dist0;
public Vector3 normal1;
public float dist1;
public Vector3 normal2;
public float dist2;
public Vector3 normal3;
public float dist3;
public Vector3 normal4;
public float dist4;
public Vector3 normal5;
public float dist5;
// The data of the 8 corners of the frustum
public Vector4 corner0;
public Vector4 corner1;
public Vector4 corner2;
public Vector4 corner3;
public Vector4 corner4;
public Vector4 corner5;
public Vector4 corner6;
public Vector4 corner7;
}
[GenerateHLSL(PackingRules.Exact, false)]
internal struct OrientedBoundingBox
{
// 4 x float3 = 48 bytes.
// TODO: pack the axes into 16-bit UNORM per channel, and consider a quaternionic representation.
public Vector3 center;
public Vector3 right;
public Vector3 up;
public Vector3 extent;
//public ushort quatX;
//public ushort quatY;
//public ushort quatZ;
//public ushort quatW;
public readonly Vector3 Forward => Vector3.Cross(up, right);
public OrientedBoundingBox(Matrix4x4 trs)
{
var vecX = (Vector3)trs.GetColumn(0);
var vecY = (Vector3)trs.GetColumn(1);
var vecZ = (Vector3)trs.GetColumn(2);
center = trs.GetColumn(3);
right = vecX * (1.0f / vecX.magnitude);
up = vecY * (1.0f / vecY.magnitude);
extent.x = 0.5f * vecX.magnitude;
extent.y = 0.5f * vecY.magnitude;
extent.z = 0.5f * vecZ.magnitude;
}
}
[GenerateHLSL(PackingRules.Exact, false)]
internal struct CullingData
{
public float fadeStart;
public float fadeEnd;
}
}

View File

@@ -0,0 +1,52 @@
//
// This file was automatically generated. Please don't edit by hand. Execute Editor command [ Edit > Rendering > Generate Shader Includes ] instead
//
#ifndef GEOMETRYDATA_CS_HLSL
#define GEOMETRYDATA_CS_HLSL
// Generated from Misaki.AoVolume.CullingData
// PackingRules = Exact
struct CullingData
{
float fadeStart;
float fadeEnd;
};
// Generated from Misaki.AoVolume.FrustumGPU
// PackingRules = Exact
struct FrustumGPU
{
float3 normal0;
float dist0;
float3 normal1;
float dist1;
float3 normal2;
float dist2;
float3 normal3;
float dist3;
float3 normal4;
float dist4;
float3 normal5;
float dist5;
float4 corner0;
float4 corner1;
float4 corner2;
float4 corner3;
float4 corner4;
float4 corner5;
float4 corner6;
float4 corner7;
};
// Generated from Misaki.AoVolume.OrientedBoundingBox
// PackingRules = Exact
struct OrientedBoundingBox
{
float3 center;
float3 right;
float3 up;
float3 extent;
};
#endif

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 2573ddb62fa31574e95e916fafd36e09
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 57d4b8b0df999734f9ba0a8d7aa29a4c

View File

@@ -4,20 +4,13 @@ using UnityEngine.Rendering;
namespace Misaki.AoVolume
{
[GenerateHLSL]
internal enum VolumeType
{
Capsule,
Box
}
[Serializable]
[GenerateHLSL(needAccessors = false)]
[GenerateHLSL(PackingRules.Exact, false)]
internal struct VolumeData
{
public VolumeType volumeType;
[HideInInspector]
public Matrix4x4 worldMatrix;
[HideInInspector]
public Matrix4x4 inverseWorldMatrix;
public Vector3 size;

View File

@@ -4,17 +4,10 @@
#ifndef VOLUMEDATA_CS_HLSL
#define VOLUMEDATA_CS_HLSL
//
// Misaki.AoVolume.VolumeType: static fields
//
#define VOLUMETYPE_CAPSULE (0)
#define VOLUMETYPE_BOX (1)
// Generated from Misaki.AoVolume.VolumeData
// PackingRules = Exact
struct VolumeData
{
int volumeType;
float4x4 worldMatrix;
float4x4 inverseWorldMatrix;
float3 size;

View File

@@ -43,18 +43,7 @@ float CapsuleSDF(float3 positionWS, VolumeData data)
float EvaluateAOVolumeMask(VolumeData data, float3 positionWS, float3 normalWS)
{
float volumeSDF = 1.0;
switch (data.volumeType)
{
case VOLUMETYPE_CAPSULE:
volumeSDF = CapsuleSDF(positionWS, data);
break;
case VOLUMETYPE_BOX:
volumeSDF = BoxSDF(positionWS, data);
break;
}
return volumeSDF;
return BoxSDF(positionWS, data);
}
#endif