2021-09-09 20:42:29 -04:00

222 lines
7.2 KiB
Plaintext

#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/ScreenSpaceLighting/GTAOCommon.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/NormalBuffer.hlsl"
//#pragma enable_d3d11_debug_symbols
#pragma kernel GTAOMain
#pragma multi_compile HALF_RES FULL_RES
#pragma multi_compile _ TEMPORAL
// --------------------------------------------
// Options
// --------------------------------------------
// Integral type
#define INTEGRAL_UNIFORM 0
#define INTEGRAL_COSINE 1
#define INTEGRAL_TYPE INTEGRAL_COSINE
// Noise options
#define TEMPORAL_ROTATION defined(TEMPORAL)
#define ENABLE_TEMPORAL_OFFSET defined(TEMPORAL)
// Normal fetching options
#define NORMAL_FROM_GBUFFER 1
#define NORMAL_COMPUTATION NORMAL_FROM_GBUFFER
// --------------------------------------------
// Integration functions
// --------------------------------------------
float IntegrateArcUniform(float horizon1, float horizon2)
{
return (1.0f - cos(horizon1) + (1.0 - cos(horizon2)));
}
float IntegrateArcCosWeighted(float horzion1, float horizon2, float n, float cosN)
{
float h1 = horzion1 * 2.0;
float h2 = horizon2 * 2.0;
float sinN = sin(n);
return 0.25 * ((-cos(h1 - n) + cosN + h1 * sinN) + (-cos(h2 - n) + cosN + h2 * sinN));
}
float UpdateHorizon(float maxH, float candidateH, float distSq)
{
float falloff = saturate((1.0 - (distSq * _AOInvRadiusSq)));
return (candidateH > maxH) ? lerp(maxH, candidateH, falloff) : lerp(maxH, candidateH, 0.03f); // TODO: Thickness heuristic here.
}
// --------------------------------------------
// Direction functions
// --------------------------------------------
float2 GetDirection(uint2 positionSS, int offset)
{
float noise = InterleavedGradientNoise(positionSS.xy, 0);
float rotations[] = { 60.0, 300.0, 180.0, 240.0, 120.0, 0.0 };
#if TEMPORAL_ROTATION
float rotation = (rotations[_AOTemporalRotationIdx] / 360.0);
#else
float rotation = (rotations[offset] / 360.0);
#endif
noise += rotation;
noise *= PI;
return float2(cos(noise), sin(noise));
}
// --------------------------------------------
// Get sample start offset
// --------------------------------------------
float GetOffset(uint2 positionSS)
{
// Spatial offset
float offset = 0.25 * ((positionSS.y - positionSS.x) & 0x3);
// Temporal offset
#if ENABLE_TEMPORAL_OFFSET
float offsets[] = { 0.0, 0.5, 0.25, 0.75 };
offset += offsets[_AOTemporalOffsetIdx];
#endif
return frac(offset);
}
// --------------------------------------------
// Input generation functions
// --------------------------------------------
float3 GetPositionVS(float2 positionSS, float depth)
{
float linearDepth = LinearEyeDepth(depth, _ZBufferParams);
return float3((positionSS * _AODepthToViewParams.xy - _AODepthToViewParams.zw) * linearDepth, linearDepth);
}
float3 GetPositionSampleVS(float2 positionSS, out float depth)
{
float linearDepth = LinearEyeDepth(depth, _ZBufferParams);
return float3((positionSS * _AODepthToViewParams.xy - _AODepthToViewParams.zw) * linearDepth, linearDepth);
}
float3 GetNormalVS(float4 normalBufferData)
{
NormalData normalData;
DecodeFromNormalBuffer(normalBufferData, normalData);
float3 normalVS = normalize(mul((float3x3)UNITY_MATRIX_V, normalData.normalWS));
return float3(normalVS.xy, -normalVS.z);
}
// --------------------------------------------
// Kernel
// --------------------------------------------
float HorizonLoop(float3 positionVS, float3 V, float2 rayStart, float2 rayDir, float rayOffset, float rayStep, int mipModifier)
{
float maxHorizon = -1.0f; // cos(pi)
float t = rayOffset * rayStep + rayStep;
const uint startWithLowerRes = min(max(0, _AOStepCount / 2 - 2), 3);
for (uint i = 0; i < _AOStepCount; i++)
{
float2 samplePos = max(2, min(rayStart + t * rayDir, _AOBufferSize.xy - 2));
// Find horizons at these steps:
float sampleDepth = GetDepthSample(samplePos, i > startWithLowerRes);
float3 samplePosVS = GetPositionVS(samplePos.xy, sampleDepth);
float3 deltaPos = samplePosVS - positionVS;
float deltaLenSq = dot(deltaPos, deltaPos);
float currHorizon = dot(deltaPos, V) * rsqrt(deltaLenSq);
maxHorizon = UpdateHorizon(maxHorizon, currHorizon, deltaLenSq);
t += rayStep;
}
return maxHorizon;
}
RW_TEXTURE2D_X(float, _AOPackedData);
[numthreads(8,8,1)]
void GTAOMain(uint3 dispatchThreadId : SV_DispatchThreadID)
{
UNITY_XR_ASSIGN_VIEW_INDEX(dispatchThreadId.z);
// Read buffers as early as possible.
float currDepth = GetDepthForCentral(dispatchThreadId.xy);
float3 positionVS = GetPositionVS(dispatchThreadId.xy, currDepth);
#if HALF_RES
float4 normalBufferData = LOAD_TEXTURE2D_X(_NormalBufferTexture, dispatchThreadId.xy * 2);
#else
float4 normalBufferData = LOAD_TEXTURE2D_X(_NormalBufferTexture, dispatchThreadId.xy);
#endif
float offset = GetOffset(dispatchThreadId.xy);
float2 rayStart = dispatchThreadId.xy;
float integral = 0;
#ifdef TEMPORAL
const int dirCount = 1;
#else
const int dirCount = _AODirectionCount;
#endif
float3 V = normalize(-positionVS);
float fovCorrectedradiusSS = clamp(_AORadius * _AOFOVCorrection * rcp(positionVS.z), _AOStepCount, _AOMaxRadiusInPixels);
float step = max(1, fovCorrectedradiusSS * _AOInvStepCountPlusOne);
// Note: We unroll here for Metal to work around a Metal shader compiler crash
#if defined(TEMPORAL) || defined(SHADER_API_METAL)
[unroll]
#endif
for (int i = 0; i < dirCount; ++i)
{
float2 dir = GetDirection(dispatchThreadId.xy, i);
// NOTE: Work around a shader compilation bug, where removing the tiny epsilon causes
// incorrect output on some drivers. The epsilon should be small enough to not affect
// the output.
float2 negDir = -dir + 1e-30;
// Find horizons
float2 maxHorizons;
maxHorizons.x = HorizonLoop(positionVS, V, rayStart, dir, offset, step, 0);
maxHorizons.y = HorizonLoop(positionVS, V, rayStart, negDir, offset, step, 0);
// We now can transform normal data into normal in view space (latency from read should have been hidden as much as possible)
float3 normalVS = GetNormalVS(normalBufferData);
// Integrate horizons
float3 sliceN = normalize(cross(float3(dir.xy, 0.0f), V.xyz));
float3 projN = normalVS - sliceN * dot(normalVS, sliceN);
float projNLen = length(projN);
float cosN = dot(projN / projNLen, V);
float3 T = cross(V, sliceN);
float N = -sign(dot(projN, T)) * GTAOFastAcos(cosN);
// Now we find the actual horizon angles
maxHorizons.x = -GTAOFastAcos(maxHorizons.x);
maxHorizons.y = GTAOFastAcos(maxHorizons.y);
maxHorizons.x = N + max(maxHorizons.x - N, -HALF_PI);
maxHorizons.y = N + min(maxHorizons.y - N, HALF_PI);
integral += AnyIsNaN(maxHorizons) ? 1 : IntegrateArcCosWeighted(maxHorizons.x, maxHorizons.y, N, cosN);
}
integral /= dirCount;
if (currDepth == UNITY_RAW_FAR_CLIP_VALUE || integral < -1e-2f)
{
integral = 1;
}
_AOPackedData[COORD_TEXTURE2D_X(dispatchThreadId.xy)] = PackAOOutput(saturate(integral), currDepth);
}