Files
2026-11-01 23:39:55 +02:00

116 lines
5.1 KiB
Plaintext

#pragma only_renderers d3d11 playstation xboxone xboxseries vulkan metal glcore ps5 switch2
#pragma kernel Lookup
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Sampling/Sampling.hlsl"
#include "Packages/com.unity.render-pipelines.core/Runtime/Sampling/QuasiRandom.hlsl"
#include "Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/Common.hlsl"
#include "Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/PatchUtil.hlsl"
#include "Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/IrradianceCompression.hlsl"
Texture2D<float> _ScreenDepths;
Texture2D<float3> _ScreenFlatNormals;
RWTexture2D<float3> _ResultL0;
RWTexture2D<float3> _ResultL10;
RWTexture2D<float3> _ResultL11;
RWTexture2D<float3> _ResultL12;
RWTexture2D<float> _ResultNdcDepths;
RWStructuredBuffer<PatchUtil::PatchStatisticsSet> _PatchStatistics;
StructuredBuffer<SphericalHarmonics::RGBL1> _PatchIrradiances;
StructuredBuffer<uint> _CellPatchIndices;
StructuredBuffer<int3> _VolumeCascadeOffsets;
uint _VolumeSpatialResolution;
uint _VolumeCascadeCount;
uint _SampleCount;
float _VolumeVoxelMinSize;
float4x4 _ClipToWorldTransform;
uint _FrameIdx;
float3 _VolumeTargetPos;
#define GROUP_SIZE 8
[numthreads(GROUP_SIZE, GROUP_SIZE, 1)]
void Lookup(uint2 lowResPixelPos : SV_DispatchThreadID)
{
uint2 lowResSize;
_ResultL0.GetDimensions(lowResSize.x, lowResSize.y);
if (any(lowResPixelPos >= lowResSize))
return;
const uint2 fullResPixelPos = lowResPixelPos * lowResScreenScaling;
uint2 fullResSize;
_ScreenDepths.GetDimensions(fullResSize.x, fullResSize.y);
const float ndcDepth = LoadNdcDepth(_ScreenDepths, fullResPixelPos);
_ResultNdcDepths[lowResPixelPos] = ndcDepth;
if (ndcDepth == invalidNdcDepth)
return;
const float2 uv = PixelPosToUvPos(fullResPixelPos, rcp(float2(fullResSize)));
const float3 worldPos = ComputeWorldSpacePosition(uv, ndcDepth, _ClipToWorldTransform);
const float3 worldNormal = _ScreenFlatNormals[fullResPixelPos];
QrngKronecker2D rng;
rng.Init(lowResPixelPos, 0);
PatchUtil::VolumeParamSet volumeParams;
volumeParams.targetPos = _VolumeTargetPos;
volumeParams.spatialResolution = _VolumeSpatialResolution;
volumeParams.voxelMinSize = _VolumeVoxelMinSize;
volumeParams.cascadeOffsets = _VolumeCascadeOffsets;
volumeParams.cascadeCount = _VolumeCascadeCount;
const int cascadeResolution = PatchUtil::ResolveCascadeIndex(_VolumeTargetPos, worldPos, _VolumeSpatialResolution, _VolumeCascadeCount, _VolumeVoxelMinSize);
const uint cascadeIdx = cascadeResolution == -1 ? _VolumeCascadeCount - 1 : cascadeResolution;
const float voxelSize = PatchUtil::GetVoxelSize(_VolumeVoxelMinSize, cascadeIdx);
SphericalHarmonics::RGBL1 irradianceSum = (SphericalHarmonics::RGBL1)0;
float weightSum = 0.0f;
{
uint patchIdx = PatchUtil::FindPatchIndexAndUpdateLastAccess(volumeParams, _CellPatchIndices, _PatchStatistics, worldPos, worldNormal, _FrameIdx);
if (patchIdx != PatchUtil::invalidPatchIndex)
{
const float weight = 0.01f;
irradianceSum = SphericalHarmonics::MulPure(_PatchIrradiances[patchIdx], weight);
weightSum = weight;
}
}
const float3x3 orthoBasis = OrthoBasisFromVector(worldNormal);
for (uint sampleIdx = 0; sampleIdx < _SampleCount; ++sampleIdx)
{
float2 jitter = rng.GetSample(0) * 2.0f - 1.0f;
const float3 jitteredWorldPos = worldPos + (orthoBasis[0] * jitter.x + orthoBasis[1] * jitter.y) * voxelSize;
jitter = rng.GetSample(1);
const float cosSpread = 0.9f; // This constant was empirically determined.
const float3 localJitteredDirection = SampleConeUniform(jitter.x, jitter.y, cosSpread);
const float3 jitteredWorldDir = mul(orthoBasis, localJitteredDirection);
uint patchIdx = PatchUtil::FindPatchIndex(volumeParams, _CellPatchIndices, jitteredWorldPos, jitteredWorldDir);
if (patchIdx != PatchUtil::invalidPatchIndex)
{
// Separating this into two if statements because certain shader compilers evaluate both conditions if we just put them together with an AND.
if (PatchUtil::GetRank(_PatchStatistics[patchIdx].counters) == 0) // rank used to exclude patches that are not visible from the camera
{
PatchUtil::WriteLastFrameAccess(_PatchStatistics, patchIdx, _FrameIdx);
SphericalHarmonics::AddMut(irradianceSum, _PatchIrradiances[patchIdx]);
weightSum += 1.0f;
}
}
rng.NextSample();
}
SphericalHarmonics::RGBL1 output = (SphericalHarmonics::RGBL1)0; // Initialization is theoretically not required. Only done to silence shader compiler warning.
PatchUtil::MarkInvalid(output);
if (weightSum != 0.0f)
output = SphericalHarmonics::MulPure(irradianceSum, rcp(weightSum));
IrradianceCompression::CompressAndStore(output, _ResultL0, _ResultL10, _ResultL11, _ResultL12, lowResPixelPos);
}