708 lines
22 KiB
HLSL
708 lines
22 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#define EYE_ADAPTATION_LOOSE_PARAMETERS 1
|
|
|
|
//------------------------------------------------------- CONFIG DISABLED DEFAULTS
|
|
|
|
#ifndef CONFIG_SPECULAR_RATIO_ESTIMATOR
|
|
#define CONFIG_SPECULAR_RATIO_ESTIMATOR 0
|
|
#endif
|
|
|
|
/** Color space to transform the color to. */
|
|
#ifndef CONFIG_COLOR_SPACE_TRANSFORMATION
|
|
#define CONFIG_COLOR_SPACE_TRANSFORMATION COLOR_SPACE_TRANSFORMATION_DISABLED
|
|
#endif
|
|
|
|
|
|
//------------------------------------------------------- INCLUDE
|
|
|
|
#include "SSDCommon.ush"
|
|
#include "SSDSignalCore.ush"
|
|
|
|
#include "DenoisingCommon.ush"
|
|
|
|
#include "../ColorSpace.ush"
|
|
#include "../EyeAdaptationCommon.ush"
|
|
#include "../LightShaderParameters.ush"
|
|
|
|
#if !defined(CONFIG_SIGNAL_PROCESSING)
|
|
#error Missing CONFIG_SIGNAL_PROCESSING
|
|
|
|
#elif CONFIG_SIGNAL_PROCESSING == SIGNAL_PROCESSING_SHADOW_VISIBILITY_MASK
|
|
#include "SSDPublicHarmonics.ush"
|
|
|
|
#elif CONFIG_SIGNAL_PROCESSING == SIGNAL_PROCESSING_POLYCHROMATIC_PENUMBRA_HARMONIC
|
|
|
|
#elif CONFIG_SIGNAL_PROCESSING == SIGNAL_PROCESSING_REFLECTIONS
|
|
#include "../MonteCarlo.ush"
|
|
#include "../BRDF.ush"
|
|
#include "../DeferredShadingCommon.ush"
|
|
#include "../CommonViewUniformBuffer.ush"
|
|
|
|
#elif CONFIG_SIGNAL_PROCESSING == SIGNAL_PROCESSING_AO
|
|
#elif CONFIG_SIGNAL_PROCESSING == SIGNAL_PROCESSING_DIFFUSE_INDIRECT_AND_AO
|
|
#elif CONFIG_SIGNAL_PROCESSING == SIGNAL_PROCESSING_DIFFUSE_SPHERICAL_HARMONIC
|
|
#elif CONFIG_SIGNAL_PROCESSING == SIGNAL_PROCESSING_SSGI
|
|
#elif CONFIG_SIGNAL_PROCESSING == SIGNAL_PROCESSING_DIFFUSE_PROBE_HIERARCHY
|
|
|
|
#else
|
|
#error Unknown CONFIG_SIGNAL_PROCESSING
|
|
|
|
#endif
|
|
|
|
|
|
//------------------------------------------------------- DOMAIN SPECIFIC KNOWLEDGE PARAMETERS
|
|
|
|
/** Knowledge specific to the signal being. */
|
|
struct FSSDSignalDomainKnowledge
|
|
{
|
|
// Light specific parameters
|
|
FLightShaderParameters Light;
|
|
|
|
// Directional light specific.
|
|
float HitDistanceToWorldBluringRadius;
|
|
|
|
// The type of light.
|
|
uint LightType;
|
|
};
|
|
|
|
float4 LightPositionAndRadius[MAX_SIGNAL_BATCH_SIZE];
|
|
float4 LightDirectionAndLength[MAX_SIGNAL_BATCH_SIZE];
|
|
DECLARE_SCALAR_ARRAY(float, HitDistanceToWorldBluringRadius, MAX_SIGNAL_BATCH_SIZE);
|
|
DECLARE_SCALAR_ARRAY(uint, LightType, MAX_SIGNAL_BATCH_SIZE);
|
|
|
|
uint FrameIndex;
|
|
|
|
/** Returns the FLightShaderParameters from the root shader parameters. */
|
|
FSSDSignalDomainKnowledge GetSignalDomainKnowledge(uint BatchedSignalId)
|
|
{
|
|
// Hopefully one day the shader compiler will be so nice we would no longer have to do this.
|
|
FSSDSignalDomainKnowledge SignalDomain;
|
|
SignalDomain.Light.InvRadius = 0;
|
|
SignalDomain.Light.Color = 0;
|
|
SignalDomain.Light.FalloffExponent = 0;
|
|
SignalDomain.Light.Tangent = 0;
|
|
SignalDomain.Light.SpotAngles = 0;
|
|
SignalDomain.Light.SpecularScale = 0;
|
|
SignalDomain.Light.DiffuseScale = 0;
|
|
SignalDomain.Light.SoftSourceRadius = 0;
|
|
SignalDomain.Light.TranslatedWorldPosition = LightPositionAndRadius[BatchedSignalId].xyz;
|
|
SignalDomain.Light.Direction = LightDirectionAndLength[BatchedSignalId].xyz;
|
|
SignalDomain.Light.SourceRadius = LightPositionAndRadius[BatchedSignalId].w;
|
|
SignalDomain.Light.SourceLength = LightDirectionAndLength[BatchedSignalId].w;
|
|
SignalDomain.HitDistanceToWorldBluringRadius = GET_SCALAR_ARRAY_ELEMENT(HitDistanceToWorldBluringRadius, BatchedSignalId);
|
|
|
|
#if !defined(DIM_LIGHT_TYPE)
|
|
{
|
|
SignalDomain.LightType = GET_SCALAR_ARRAY_ELEMENT(LightType, BatchedSignalId);
|
|
}
|
|
#else
|
|
{
|
|
SignalDomain.LightType = DIM_LIGHT_TYPE;
|
|
}
|
|
#endif
|
|
return SignalDomain;
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------- INTERNAL
|
|
|
|
float Luma4(float3 Color)
|
|
{
|
|
return (Color.g * 2.0) + (Color.r + Color.b);
|
|
}
|
|
|
|
float KarisHdrWeightY(float Color, float Exposure)
|
|
{
|
|
return rcp(Color * Exposure + 4.0);
|
|
}
|
|
|
|
float KarisHdrWeightInvY(float Color, float Exposure)
|
|
{
|
|
return 4.0 * rcp(1.0 - Color * Exposure);
|
|
}
|
|
|
|
float Luma_To_LumaLog(float x)
|
|
{
|
|
return max(x > 0 ? log2(x) : -100, -100);
|
|
}
|
|
|
|
/** Returns the exposure multiplier that should be used for the color transformation of the signal. */
|
|
float GetSignalColorTransformationExposure()
|
|
{
|
|
// TODO(Denoiser): expose that.
|
|
const float Tweak = 1;
|
|
|
|
float FrameExposureScale = EyeAdaptationLookup();
|
|
|
|
FrameExposureScale *= View.OneOverPreExposure;
|
|
|
|
return ToScalarMemory(FrameExposureScale * Tweak);
|
|
}
|
|
|
|
/** Returns the linear color space Luma with a 4 multiplier for ALU perf reasons. */
|
|
float GetSignalLuma4(FSSDSignalSample Sample, const uint Basis)
|
|
#if COMPILE_SIGNAL_COLOR
|
|
{
|
|
// TODO(Denoiser): should force this to be scalar load.
|
|
float FrameExposureScale = GetSignalColorTransformationExposure();
|
|
|
|
// TODO(Denoiser): exposes optimisation if sample is normalized.
|
|
const bool bIsNormalizedSample = false;
|
|
|
|
if (Basis & COLOR_SPACE_KARIS_WEIGHTING)
|
|
{
|
|
float KarisX = Luma4(Sample.SceneColor.rgb);
|
|
if ((Basis & 0x3))
|
|
{
|
|
KarisX = Sample.SceneColor.x;
|
|
}
|
|
|
|
if (!bIsNormalizedSample)
|
|
{
|
|
KarisX *= SafeRcp(Sample.SampleCount);
|
|
}
|
|
|
|
return KarisX * KarisHdrWeightInvY(KarisX, FrameExposureScale);
|
|
}
|
|
else
|
|
{
|
|
if ((Basis & 0x3))
|
|
{
|
|
return Sample.SceneColor.x;
|
|
}
|
|
|
|
return Luma4(Sample.SceneColor.rgb);
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/** Conveniently transform one simple from a source basis to a destination basis. */
|
|
FSSDSignalSample TransformSignal(FSSDSignalSample Sample, const uint SrcBasis, const uint DestBasis)
|
|
#if COMPILE_SIGNAL_COLOR
|
|
{
|
|
// If the source and destination bases are the same, early return to not pay ALU and floating point divergence.
|
|
if (SrcBasis == DestBasis)
|
|
{
|
|
return Sample;
|
|
}
|
|
|
|
// TODO(Denoiser): should force this to be scalar load.
|
|
float FrameExposureScale = GetSignalColorTransformationExposure();
|
|
|
|
// TODO(Denoiser): exposes optimisation if sample is normalized.
|
|
const bool bIsNormalizedSample = false;
|
|
const bool bDebugForceNormalizeColor = true;
|
|
const bool bIsNormalizedColor = bIsNormalizedSample || bDebugForceNormalizeColor;
|
|
|
|
|
|
const bool bUnchangeAlphaPremultiply = (SrcBasis & COLOR_SPACE_UNPREMULTIPLY) == (DestBasis & COLOR_SPACE_UNPREMULTIPLY);
|
|
const bool bUnchangeColorSpace = bUnchangeAlphaPremultiply && (SrcBasis & 0x3) == (DestBasis & 0x3);
|
|
|
|
if (bDebugForceNormalizeColor)
|
|
{
|
|
Sample.SceneColor *= SafeRcp(Sample.SampleCount);
|
|
}
|
|
|
|
// Remove Karis weighting before doing any color transformations.
|
|
if (SrcBasis & COLOR_SPACE_KARIS_WEIGHTING)
|
|
{
|
|
float KarisX = Luma4(Sample.SceneColor.rgb);
|
|
if ((SrcBasis & 0x3))
|
|
{
|
|
KarisX = Sample.SceneColor.x;
|
|
}
|
|
|
|
if (!bIsNormalizedColor)
|
|
{
|
|
KarisX *= SafeRcp(Sample.SampleCount);
|
|
}
|
|
|
|
Sample.SceneColor *= KarisHdrWeightInvY(KarisX, FrameExposureScale);
|
|
}
|
|
|
|
if (bUnchangeColorSpace)
|
|
{
|
|
// NOP
|
|
}
|
|
else if ((SrcBasis & 0x3) == COLOR_SPACE_YCOCG)
|
|
{
|
|
Sample.SceneColor.rgb = YCoCg_2_LinearRGB(Sample.SceneColor.rgb);
|
|
}
|
|
else if ((SrcBasis & 0x3) == COLOR_SPACE_LCOCG)
|
|
{
|
|
Sample.SceneColor.rgb = LCoCg_2_LinearRGB(Sample.SceneColor.rgb);
|
|
}
|
|
|
|
float Alpha = Sample.SceneColor.a * SafeRcp(Sample.SampleCount);
|
|
if (bIsNormalizedColor)
|
|
{
|
|
Alpha = Sample.SceneColor.a;
|
|
}
|
|
|
|
// Premultiplied RGBA
|
|
if (bUnchangeAlphaPremultiply)
|
|
{
|
|
// NOP
|
|
}
|
|
else if (SrcBasis & COLOR_SPACE_UNPREMULTIPLY)
|
|
{
|
|
Sample.SceneColor.rgb *= Alpha;
|
|
}
|
|
else //if (DestBasis & COLOR_SPACE_UNPREMULTIPLY)
|
|
{
|
|
Sample.SceneColor.rgb *= SafeRcp(Alpha);
|
|
}
|
|
|
|
float x = Luma4(Sample.SceneColor.rgb);
|
|
if ((DestBasis & 0x3) == COLOR_SPACE_YCOCG)
|
|
{
|
|
if (!bUnchangeColorSpace)
|
|
Sample.SceneColor.xyz = LinearRGB_2_YCoCg(Sample.SceneColor.rgb);
|
|
|
|
x = Sample.SceneColor.x;
|
|
}
|
|
else if ((DestBasis & 0x3) == COLOR_SPACE_LCOCG)
|
|
{
|
|
if (!bUnchangeColorSpace)
|
|
Sample.SceneColor.xyz = LinearRGB_2_LCoCg(Sample.SceneColor.rgb);
|
|
|
|
x = Sample.SceneColor.x;
|
|
}
|
|
|
|
if (DestBasis & COLOR_SPACE_KARIS_WEIGHTING)
|
|
{
|
|
if (bIsNormalizedColor)
|
|
{
|
|
Sample.SceneColor *= KarisHdrWeightY(x, FrameExposureScale);
|
|
}
|
|
else
|
|
{
|
|
Sample.SceneColor *= KarisHdrWeightY(x * SafeRcp(Sample.SampleCount), FrameExposureScale);
|
|
}
|
|
}
|
|
|
|
if (bDebugForceNormalizeColor)
|
|
{
|
|
Sample.SceneColor *= Sample.SampleCount;
|
|
}
|
|
|
|
return Sample;
|
|
}
|
|
#else
|
|
{
|
|
return Sample;
|
|
}
|
|
#endif
|
|
|
|
#if 0 // will probably be handy at some point.
|
|
float4 WeightedLerp( float4 ColorA, float WeightA, float4 ColorB, float WeightB, float Blend )
|
|
{
|
|
float BlendA = (1.0 - Blend) * WeightA;
|
|
float BlendB = Blend * WeightB;
|
|
float RcpBlend = rcp(BlendA + BlendB);
|
|
BlendA *= RcpBlend;
|
|
BlendB *= RcpBlend;
|
|
return ColorA * BlendA + ColorB * BlendB;
|
|
}
|
|
#endif
|
|
|
|
|
|
/** Create a spherical gaussian lob for a given roughness. */
|
|
FSphericalGaussian ComputeRoughnessLobe(float Roughness, float3 N, float3 V)
|
|
{
|
|
//float a = Pow2( max( 0.02, Roughness ) );
|
|
float a = Pow2( max( 0.001, Roughness ) );
|
|
float a2 = a*a;
|
|
|
|
float NoV = saturate( abs( dot(N, V) ) + 1e-5 );
|
|
//float NoV = saturate( abs( dot(N, V) ) );
|
|
|
|
float3 R = 2 * NoV * N - V;
|
|
|
|
FSphericalGaussian SpecularSG;
|
|
SpecularSG.Axis = R;
|
|
SpecularSG.Sharpness = 0.5 / ( a2 * max( NoV, 0.1 ) );
|
|
//SpecularSG.Sharpness = 0.5 / ( a2 * max( NoV, 0.1 ) );
|
|
SpecularSG.Amplitude = rcp( PI * a2 );
|
|
|
|
return SpecularSG;
|
|
}
|
|
|
|
/** Create a spherical gaussian lob for a given scene metadata. */
|
|
FSphericalGaussian ComputeRoughnessLobe(FSSDSampleSceneInfos RefSceneMetadata)
|
|
{
|
|
#if CONFIG_USE_VIEW_SPACE
|
|
float3 N = RefSceneMetadata.ViewNormal;
|
|
float3 V = float3(1, 0, 0); // TODO(Denoiser).
|
|
#else
|
|
float3 N = RefSceneMetadata.WorldNormal;
|
|
float3 V = -GetCameraVectorFromTranslatedWorldPosition(GetTranslatedWorldPosition(RefSceneMetadata));
|
|
#endif
|
|
|
|
float Roughness = RefSceneMetadata.Roughness;
|
|
|
|
return ComputeRoughnessLobe(Roughness, N, V);
|
|
}
|
|
|
|
/** Compute the factor to apply to inifinity bluring radius based on the sample's hit distance. */
|
|
float ComputeBluringOutOfFocusOfNormalizedSample(FSSDSignalFrequency SampleFrequency, FSSDSampleSceneInfos SceneMetadata)
|
|
{
|
|
float DistanceCameraToPixel = GetDistanceToCameraFromViewVector(View.TranslatedWorldCameraOrigin - GetTranslatedWorldPosition(SceneMetadata));
|
|
|
|
return ComputeDenoisingCoc(DistanceCameraToPixel, SampleFrequency.ClosestHitDistance);
|
|
}
|
|
|
|
/** Project the specular lobe in screen space. Returns the major axis of the lobe anysotropy, and the inifinity bluring in ViewportUV.x space. */
|
|
void ProjectSpecularLobeToScreenSpace(
|
|
FSSDSampleSceneInfos SceneMetadata,
|
|
out float2 NormalizedScreenMajorAxis,
|
|
out float InifinityMajorViewportRadius,
|
|
out float InifinityMinorViewportRadius)
|
|
{
|
|
float3 N = GetWorldNormal(SceneMetadata);
|
|
float3 V = -GetCameraVectorFromTranslatedWorldPosition(GetTranslatedWorldPosition(SceneMetadata));
|
|
|
|
// Project reflection axis onto screen.
|
|
{
|
|
float3 R = 2 * dot(V, N) * N - V;
|
|
|
|
float4 RayEndClip = mul(float4(GetTranslatedWorldPosition(SceneMetadata) + R * (0.5 * SceneMetadata.WorldDepth), 1), View.TranslatedWorldToClip);
|
|
float2 RayEndScreen = RayEndClip.xy * rcp(RayEndClip.w);
|
|
|
|
float2 RayDirectionScreen = RayEndScreen - SceneMetadata.ScreenPosition;
|
|
|
|
NormalizedScreenMajorAxis = normalize(RayDirectionScreen * View.ViewSizeAndInvSize.xy);
|
|
}
|
|
|
|
{
|
|
float MajorRoughnessLobeAngle;
|
|
float MinorRoughnessLobeAngle;
|
|
ComputeSpecularLobeAngles(V, N, SceneMetadata.Roughness, /* out */ MajorRoughnessLobeAngle, /* out */ MinorRoughnessLobeAngle);
|
|
|
|
/** Return the size of the bluring radius caused by the specular lobe in ViewportUV.x space. */
|
|
InifinityMajorViewportRadius = LobeAngleToViewportRadius(MajorRoughnessLobeAngle);
|
|
InifinityMinorViewportRadius = LobeAngleToViewportRadius(MinorRoughnessLobeAngle);
|
|
}
|
|
}
|
|
|
|
|
|
//------------------------------------------------------- MAIN FUNCTIONS TO IMPLEMENT FOR THE PASSES
|
|
|
|
float3 ComputeMajorLightRayDirection(FSSDSampleSceneInfos SceneMetadata, FSSDSignalDomainKnowledge DomainKnowledge)
|
|
#if CONFIG_SIGNAL_PROCESSING == SIGNAL_PROCESSING_SHADOW_VISIBILITY_MASK
|
|
{
|
|
BRANCH
|
|
if (DomainKnowledge.LightType == LIGHT_TYPE_DIRECTIONAL)
|
|
{
|
|
return -DomainKnowledge.Light.Direction;
|
|
}
|
|
else
|
|
{
|
|
float3 PixelToLightWorldVector = DomainKnowledge.Light.TranslatedWorldPosition - GetTranslatedWorldPosition(SceneMetadata);
|
|
return normalize(PixelToLightWorldVector);
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
// Not used by any pass.
|
|
return float3(0.0, 0.0, 1.0);
|
|
}
|
|
#endif
|
|
|
|
float ComputeLightSourceRadius(FSSDSignalDomainKnowledge DomainKnowledge)
|
|
#if CONFIG_SIGNAL_PROCESSING == SIGNAL_PROCESSING_SHADOW_VISIBILITY_MASK
|
|
{
|
|
return DomainKnowledge.Light.SourceRadius;
|
|
}
|
|
#else
|
|
{
|
|
return 0.0f;
|
|
}
|
|
#endif
|
|
|
|
/** COmpute the weight of the bilateral filter to use between a reference sample and a neighbor. */
|
|
float ComputeBilateralWeight(
|
|
const uint BilateralSettings,
|
|
float MaxWorldBluringDistance,
|
|
FSSDSignalDomainKnowledge DomainKnowledge,
|
|
FSSDSampleSceneInfos RefSceneMetadata,
|
|
FSSDSampleSceneInfos NeighborSceneMetadata,
|
|
float3 NeighborToRefVector)
|
|
{
|
|
if (DEBUG_DISABLE_BILATERAL)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
#if CONFIG_USE_VIEW_SPACE
|
|
float3 RefNormal = RefSceneMetadata.ViewNormal;
|
|
float3 NeighborNormal = NeighborSceneMetadata.ViewNormal;
|
|
#else
|
|
float3 RefNormal = RefSceneMetadata.WorldNormal;
|
|
float3 NeighborNormal = NeighborSceneMetadata.WorldNormal;
|
|
#endif
|
|
|
|
// TODO(Denoiser): extremely expensive with rcp()...
|
|
float InvMaxWorldBluringDistance = rcp(MaxWorldBluringDistance);
|
|
|
|
float PositionBilateralWeight = 1;
|
|
if ((BilateralSettings & 0xF) == 1)
|
|
{
|
|
const float WorldRadius = MaxWorldBluringDistance;
|
|
|
|
float RefAnisotropyInvFactor = ComputeAnisotropyInvFactor(RefSceneMetadata);
|
|
|
|
float3 CameraToRef = View.ViewForward;
|
|
|
|
float Z = dot(CameraToRef, NeighborToRefVector);
|
|
float XY = length2(NeighborToRefVector - CameraToRef * Z * (1 - RefAnisotropyInvFactor));
|
|
|
|
float DistSquare = XY;
|
|
|
|
float Multiplier = rcp(WorldRadius * WorldRadius);
|
|
|
|
PositionBilateralWeight = saturate(1 - DistSquare * Multiplier);
|
|
}
|
|
else if ((BilateralSettings & 0xF) == 2)
|
|
{
|
|
// TODO(Denoiser): save 2 VGPR by using SceneDepth instead of RefSceneMetadata.TranslatedWorldPosition (not sure it can work in math yet).
|
|
// float2 ClipPosition = GetScreenPositionForProjectionType(ScreenPosition, WorldDepth);
|
|
// Infos.TranslatedWorldPosition = mul(float4(ClipPosition, WorldDepth, 1), ScreenToTranslatedWorld).xyz;
|
|
|
|
float DistFromRefPlane = abs(dot(RefNormal, NeighborToRefVector));
|
|
|
|
PositionBilateralWeight = saturate(1 - DistFromRefPlane * InvMaxWorldBluringDistance);
|
|
}
|
|
else if ((BilateralSettings & 0xF) == 3)
|
|
{
|
|
if (dot(NeighborToRefVector, NeighborToRefVector) > MaxWorldBluringDistance * MaxWorldBluringDistance)
|
|
{
|
|
PositionBilateralWeight = 0;
|
|
}
|
|
}
|
|
else if ((BilateralSettings & 0xF) == 4)
|
|
{
|
|
// TODO(Denoiser): extremely expensive with rcp()...
|
|
PositionBilateralWeight = saturate(1 - dot(NeighborToRefVector, NeighborToRefVector) * rcp(MaxWorldBluringDistance * MaxWorldBluringDistance));
|
|
}
|
|
else if ((BilateralSettings & 0xF) == 5)
|
|
{
|
|
float LightTangentBilateral;
|
|
{
|
|
// Projet in the NeighborToRefVector into the the light tangential plane.
|
|
float3 NeighborToLight = ComputeMajorLightRayDirection(NeighborSceneMetadata, DomainKnowledge);
|
|
float3 ProjectedNeighborToRefVector = NeighborToRefVector - NeighborToLight * dot(NeighborToLight, NeighborToRefVector);
|
|
|
|
// Measure in the tangential space the distance to take into account anisotropy.
|
|
LightTangentBilateral = saturate(1 - dot(ProjectedNeighborToRefVector, ProjectedNeighborToRefVector) * (InvMaxWorldBluringDistance * InvMaxWorldBluringDistance));
|
|
}
|
|
|
|
float PlaneMisAlignementBilateral;
|
|
{
|
|
// Use source radius as encoding error to add more penalty to depth to avoid SSS backface transmission rim for spotlight with source radius > 0.
|
|
// It makes the scaling of the distance by (DistanceFromLight - HitT)/HitT instead of (DistanceFromLight - HitT)/HitT*SourceRadius
|
|
// When light is behind close to neighbor in view direction, the light tangent bilaterial will be close to 1. Little penalty
|
|
// is applied in depth. Adding the effect of SourceRadius on NeighborToRefError could add more penalty to depth to mitigate the
|
|
// bleeding error.
|
|
const bool bApplyDepthPenaltyWeightForSSS = RefSceneMetadata.ShadingModelID == SHADINGMODELID_SUBSURFACE_PROFILE && ComputeLightSourceRadius(DomainKnowledge) > 0.0f;
|
|
const float NormalEncodingError = bApplyDepthPenaltyWeightForSSS ? DomainKnowledge.Light.SourceRadius : -0.25;
|
|
|
|
|
|
float DistFromRefPlane = dot(RefNormal, -NeighborToRefVector);
|
|
|
|
float NeighborToRefPlaneDistance = length(-NeighborToRefVector - DistFromRefPlane * RefNormal);
|
|
float NeighborToRefError = NeighborToRefPlaneDistance * NormalEncodingError;
|
|
|
|
PlaneMisAlignementBilateral = saturate(1 - 6 * (abs(DistFromRefPlane) + NeighborToRefError) * InvMaxWorldBluringDistance);
|
|
}
|
|
|
|
PositionBilateralWeight = LightTangentBilateral * PlaneMisAlignementBilateral;
|
|
}
|
|
else if ((BilateralSettings & 0xF) == 6)
|
|
{
|
|
float PlaneMisAlignementBilateral;
|
|
{
|
|
const float NormalEncodingError = View.GeneralPurposeTweak;
|
|
|
|
float DistFromRefPlane = dot(RefNormal, -NeighborToRefVector);
|
|
|
|
float NeighborToRefPlaneDistance = length(-NeighborToRefVector - DistFromRefPlane * RefNormal);
|
|
float NeighborToRefError = NeighborToRefPlaneDistance * NormalEncodingError;
|
|
|
|
PlaneMisAlignementBilateral = saturate(1 - (abs(DistFromRefPlane) - NeighborToRefError) * InvMaxWorldBluringDistance);
|
|
}
|
|
|
|
PositionBilateralWeight = PlaneMisAlignementBilateral;
|
|
}
|
|
|
|
float NormalBilateralWeight = 1;
|
|
if (BilateralSettings & BILATERAL_NORMAL)
|
|
{
|
|
float NoN = max(dot(RefNormal, NeighborNormal), 0);
|
|
|
|
#if 1 // TODO(Denoiser)
|
|
NormalBilateralWeight = pow(NoN, 4);
|
|
#else
|
|
// [ Kontkanen et al 2004, "Irradiance Filtering for Monte Carlo Ray Tracing" ]
|
|
// TODO(Denoiser): looks broken
|
|
float m = MaxWorldBluringDistance;
|
|
float a = 0.3;
|
|
float d = sqrt(1 - NoN); // + length(RefSceneMetadata.TranslatedWorldPosition - NeighborSceneMetadata.TranslatedWorldPosition) * rcp(m);
|
|
float x = d * rcp(a);
|
|
|
|
NormalBilateralWeight = exp(-x * x);
|
|
#endif
|
|
}
|
|
|
|
// [ Tokoyashi 2015, "Specular Lobe-Aware Filtering and Upsampling for Interactive Indirect Illumination" ]
|
|
float LobeSimilarity = 1;
|
|
float AxesSimilarity = 1;
|
|
if (BilateralSettings & (BILATERAL_TOKOYASHI_LOBE | BILATERAL_TOKOYASHI_AXES))
|
|
{
|
|
const float Beta = 32;
|
|
|
|
FSphericalGaussian Ref = ComputeRoughnessLobe(RefSceneMetadata);
|
|
FSphericalGaussian Neighbor = ComputeRoughnessLobe(NeighborSceneMetadata);
|
|
|
|
float InvSharpnessSum = rcp(Ref.Sharpness + Neighbor.Sharpness);
|
|
|
|
if (BilateralSettings & BILATERAL_TOKOYASHI_LOBE)
|
|
LobeSimilarity = pow(2 * sqrt(Ref.Sharpness * Neighbor.Sharpness) * InvSharpnessSum, Beta);
|
|
|
|
if (BilateralSettings & BILATERAL_TOKOYASHI_AXES)
|
|
AxesSimilarity = exp(-(Beta * (Ref.Sharpness * Neighbor.Sharpness) * InvSharpnessSum) * saturate(1 - dot(Ref.Axis, Neighbor.Axis)));
|
|
}
|
|
|
|
if (BilateralSettings & BILATERAL_SHADING_MODEL)
|
|
{
|
|
FLATTEN
|
|
if (RefSceneMetadata.ShadingModelID != NeighborSceneMetadata.ShadingModelID)
|
|
return 0.0;
|
|
}
|
|
|
|
return PositionBilateralWeight * NormalBilateralWeight * LobeSimilarity * AxesSimilarity;
|
|
}
|
|
|
|
float GetSignalWorldBluringRadius(FSSDSignalFrequency SampleFrequency, FSSDSampleSceneInfos SceneMetadata, FSSDSignalDomainKnowledge DomainKnowledge)
|
|
#if CONFIG_SIGNAL_PROCESSING == SIGNAL_PROCESSING_SHADOW_VISIBILITY_MASK
|
|
{
|
|
if (SampleFrequency.ClosestHitDistance == DENOISER_INVALID_HIT_DISTANCE)
|
|
{
|
|
return WORLD_RADIUS_INVALID;
|
|
}
|
|
else if (SampleFrequency.ClosestHitDistance == DENOISER_MISS_HIT_DISTANCE)
|
|
{
|
|
return WORLD_RADIUS_MISS; // 100000;
|
|
}
|
|
|
|
// Sometime, artists might put occluder very close to the area light compared to they area, that may lead to negative values.
|
|
// TODO(Denoiser): the correct way to fix this is to move this world bluring radius computation into the RGS, and have DistanceFromLight = Ray's MaxT.
|
|
const float ClosestLightDistance = 0.001;
|
|
|
|
BRANCH
|
|
if (DomainKnowledge.LightType == LIGHT_TYPE_DIRECTIONAL)
|
|
{
|
|
return DomainKnowledge.HitDistanceToWorldBluringRadius * SampleFrequency.ClosestHitDistance;
|
|
}
|
|
else
|
|
{
|
|
return ComputeLightSampleWorldBluringRadius(
|
|
GetTranslatedWorldPosition(SceneMetadata),
|
|
DomainKnowledge.LightType, DomainKnowledge.Light,
|
|
SampleFrequency.ClosestHitDistance);
|
|
}
|
|
}
|
|
#elif CONFIG_SIGNAL_PROCESSING == SIGNAL_PROCESSING_POLYCHROMATIC_PENUMBRA_HARMONIC
|
|
{
|
|
// No need to compute the world bluring radius given the hit has been classified by harmonic frequencies already.
|
|
return 0;
|
|
}
|
|
#elif CONFIG_SIGNAL_PROCESSING == SIGNAL_PROCESSING_REFLECTIONS
|
|
{
|
|
// Not used by any pass.
|
|
return 0;
|
|
}
|
|
#elif CONFIG_SIGNAL_PROCESSING == SIGNAL_PROCESSING_AO || CONFIG_SIGNAL_PROCESSING == SIGNAL_PROCESSING_DIFFUSE_INDIRECT_AND_AO
|
|
{
|
|
if (SampleFrequency.ClosestHitDistance == DENOISER_INVALID_HIT_DISTANCE)
|
|
{
|
|
return WORLD_RADIUS_INVALID;
|
|
}
|
|
else if (SampleFrequency.ClosestHitDistance == DENOISER_MISS_HIT_DISTANCE)
|
|
{
|
|
return WORLD_RADIUS_MISS; // 100000;
|
|
}
|
|
|
|
// How much can be blured is about the the length of hit distance assuming some amount of visiiblity.
|
|
return SampleFrequency.ClosestHitDistance;
|
|
}
|
|
#elif CONFIG_SIGNAL_PROCESSING == SIGNAL_PROCESSING_DIFFUSE_SPHERICAL_HARMONIC
|
|
{
|
|
// Not used by any pass.
|
|
return 0;
|
|
}
|
|
#elif CONFIG_SIGNAL_PROCESSING == SIGNAL_PROCESSING_SSGI
|
|
{
|
|
// Not used by any pass.
|
|
return 0;
|
|
}
|
|
#elif CONFIG_SIGNAL_PROCESSING == SIGNAL_PROCESSING_DIFFUSE_PROBE_HIERARCHY
|
|
{
|
|
// Not used by any pass.
|
|
return 0;
|
|
}
|
|
#else
|
|
#error Unimplemented GetSignalWorldBluringRadius()
|
|
#endif
|
|
|
|
// Returns the penumbra of this sample, or 1 if invalid.
|
|
float GetSamplePenumbraSafe(FSSDSignalSample Sample)
|
|
{
|
|
return (Sample.SampleCount > 0 ? Sample.MissCount / Sample.SampleCount : 1);
|
|
}
|
|
|
|
// Get penumbra, but be aware of NaN.
|
|
float GetSamplePenumbra(FSSDSignalSample Sample)
|
|
{
|
|
if (1) // TODO(Denoiser): For debugging purpose of division by 0.
|
|
{
|
|
return GetSamplePenumbraSafe(Sample);
|
|
}
|
|
return Sample.MissCount / Sample.SampleCount;
|
|
}
|
|
|
|
|
|
// Matches GenerateCosineNormalRay()
|
|
// TODO(Denoiser): expose a Weight that multiply directly onto the Sample.SceneColor to reduce ALU
|
|
#if COMPILE_SIGNAL_COLOR_SH && COMPILE_SIGNAL_COLOR
|
|
FSSDSphericalHarmonicRGB ComputeSampleColorSH(FSSDSampleSceneInfos SampleSceneMetadata, FSSDSignalSample Sample, uint2 NeighborPixelCoord)
|
|
{
|
|
const uint SamplesPerPixel = 1;
|
|
RandomSequence RandSequence;
|
|
RandomSequence_Initialize(RandSequence, NeighborPixelCoord, /* SampleIndex */ 0, FrameIndex, SamplesPerPixel);
|
|
|
|
float2 RandSample = RandomSequence_GenerateSample2D(RandSequence);
|
|
|
|
float4 TangentDirection = CosineSampleHemisphere(RandSample);
|
|
float3 WorldDirection = TangentToWorld(TangentDirection.xyz, GetWorldNormal(SampleSceneMetadata));
|
|
|
|
#if SPHERICAL_HARMONIC_ORDER == 2
|
|
FSSDSphericalHarmonic SampleBasis = SHBasisFunction(WorldDirection);
|
|
return MulSH(SampleBasis, Sample.SceneColor.rgb);
|
|
|
|
#elif SPHERICAL_HARMONIC_ORDER == 3
|
|
FSSDSphericalHarmonic SampleBasis = SHBasisFunction3(WorldDirection);
|
|
return MulSH3(SampleBasis, Sample.SceneColor.rgb);
|
|
|
|
#endif
|
|
}
|
|
#endif
|