111 lines
4.6 KiB
C#
111 lines
4.6 KiB
C#
using System.Numerics;
|
|
using System.Runtime.InteropServices;
|
|
using AcDream.App.Rendering.Gpu;
|
|
|
|
namespace AcDream.App.Rendering;
|
|
|
|
/// <summary>
|
|
/// Shader ABI SSOT companion for opt-in set 3 binding 6. The matching GLSL block is
|
|
/// directional_shadow_common.glsl; both are pinned at 336 std140 bytes.
|
|
/// </summary>
|
|
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
|
internal readonly struct DirectionalShadowUniforms
|
|
{
|
|
internal const int SizeInBytes = 336;
|
|
|
|
public readonly Matrix4x4 WorldToClip0;
|
|
public readonly Matrix4x4 WorldToClip1;
|
|
public readonly Matrix4x4 WorldToClip2;
|
|
public readonly Matrix4x4 WorldToClip3;
|
|
public readonly Vector4 SplitFarMeters;
|
|
public readonly Vector4 Control;
|
|
public readonly Vector4 BiasMeters;
|
|
public readonly UInt4 TextureAndFlags;
|
|
public readonly Vector4 LightDirectionAndSource;
|
|
|
|
internal DirectionalShadowUniforms(
|
|
Matrix4x4 worldToClip0,
|
|
Matrix4x4 worldToClip1,
|
|
Matrix4x4 worldToClip2,
|
|
Matrix4x4 worldToClip3,
|
|
Vector4 splitFarMeters,
|
|
Vector4 control,
|
|
Vector4 biasMeters,
|
|
UInt4 textureAndFlags,
|
|
Vector4 lightDirectionAndSource)
|
|
{
|
|
WorldToClip0 = worldToClip0;
|
|
WorldToClip1 = worldToClip1;
|
|
WorldToClip2 = worldToClip2;
|
|
WorldToClip3 = worldToClip3;
|
|
SplitFarMeters = splitFarMeters;
|
|
Control = control;
|
|
BiasMeters = biasMeters;
|
|
TextureAndFlags = textureAndFlags;
|
|
LightDirectionAndSource = lightDirectionAndSource;
|
|
}
|
|
|
|
internal static DirectionalShadowUniforms Create(
|
|
ReadOnlySpan<DirectionalShadowCascade> cascades,
|
|
in DirectionalShadowEnvironmentState environment,
|
|
in DirectionalShadowQuality quality,
|
|
GpuTextureSlot textureSlot)
|
|
{
|
|
if (cascades.Length != quality.CascadeCount)
|
|
throw new ArgumentException("The cascade span must match the selected quality.", nameof(cascades));
|
|
if (!textureSlot.IsAssigned)
|
|
throw new ArgumentException("The directional depth array requires an assigned texture slot.", nameof(textureSlot));
|
|
|
|
Matrix4x4 matrix0 = cascades[0].WorldToShadowClip;
|
|
Matrix4x4 matrix1 = cascades.Length > 1 ? cascades[1].WorldToShadowClip : Matrix4x4.Identity;
|
|
Matrix4x4 matrix2 = cascades.Length > 2 ? cascades[2].WorldToShadowClip : Matrix4x4.Identity;
|
|
Matrix4x4 matrix3 = cascades.Length > 3 ? cascades[3].WorldToShadowClip : Matrix4x4.Identity;
|
|
float split0 = cascades[0].SplitFarMeters;
|
|
float split1 = cascades.Length > 1 ? cascades[1].SplitFarMeters : quality.MaximumReachMeters;
|
|
float split2 = cascades.Length > 2 ? cascades[2].SplitFarMeters : quality.MaximumReachMeters;
|
|
float split3 = cascades.Length > 3 ? cascades[3].SplitFarMeters : quality.MaximumReachMeters;
|
|
|
|
// The pinned v1 receiver block carries one world-space bias triple.
|
|
// Publish the conservative outer-cascade values; the receiver derives
|
|
// each inner cascade's relative texel footprint from its projection
|
|
// matrix before applying this triple. That preserves the bias portion
|
|
// of the v1 ABI while the selected-light vec4 is appended at byte 320
|
|
// without applying the outer map's visibly excessive offset nearby.
|
|
DirectionalShadowWorldBias bias = cascades[^1].Bias;
|
|
float effectiveReachMeters = cascades[^1].SplitFarMeters;
|
|
return new DirectionalShadowUniforms(
|
|
matrix0,
|
|
matrix1,
|
|
matrix2,
|
|
matrix3,
|
|
new Vector4(split0, split1, split2, split3),
|
|
new Vector4(
|
|
environment.Strength,
|
|
environment.SoftnessMultiplier,
|
|
effectiveReachMeters,
|
|
MathF.Max(1f, effectiveReachMeters * 0.02f)),
|
|
new Vector4(
|
|
bias.ConstantDepthMeters,
|
|
bias.SlopeDepthMeters,
|
|
bias.NormalOffsetMeters,
|
|
cascades[0].CasterDepthPaddingMeters),
|
|
new UInt4(
|
|
textureSlot.Index,
|
|
checked((uint)quality.CascadeCount),
|
|
checked((uint)quality.MapResolution),
|
|
1u | (checked((uint)quality.PcfRadiusTexels) << 8)),
|
|
new Vector4(
|
|
environment.SurfaceToLightDirection,
|
|
checked((uint)environment.SourceKind)));
|
|
}
|
|
}
|
|
|
|
/// <summary>Four uints with the exact 16-byte std140 uvec4 representation.</summary>
|
|
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
|
internal readonly struct UInt4(uint x, uint y, uint z, uint w)
|
|
{
|
|
public readonly uint X = x;
|
|
public readonly uint Y = y;
|
|
public readonly uint Z = z;
|
|
public readonly uint W = w;
|
|
}
|