Files
2025-05-18 13:04:45 +08:00

126 lines
4.7 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
/*
Spiral blur function with IOR used to emulate BTDF of the material.
@param RandomTex - Random texture input for spiral blur
@param SurfaceScatter - Scattering by depth behind the object (0.0 to 1.0)
@param VolumeScatter - Scattering by object's thickness (0.0 to 1.0)
@param IOR - Index of refraction (defaults to 1.0 for no refraction)
@param Normal - Normal texture input
@param ObjectThickness - Estimated or calculated object's thickness
@param PixelDepth - Pixel depth input (TODO could be sampled in code)
@param NormalAlignFactor - Factor for Normal to Screen alignment, controls refraction and
keeps it within screen space (0.0 to positive infinity with 0.0
meaning no correction)
@param MagicRefractionNumber - Directly controls the amount of refraction offset when sampling
the scene (1.0 means no correction)
*/// --------------------------------------------------------------------------
const int RadialSteps = 48;
const int AngularSteps = 1;
const float MagicBlurNumber = 100.0;
// Thickness added to account for normal texture features when
// calculating refraction light bending.
const float RefractionThicknessCorrection = 50.0;
float UVDistance = 0.2;
float AngularOffset = 0.618;
float SizeOnScreenFactor = View.ViewToClip[0][0] / PixelDepth;
float2 ScreenUV = MaterialFloat2(ScreenAlignedPosition(Parameters.ScreenPosition).xy);
float2 BaseUV = ScreenUV;
// Calculate PixelNormal
float3 PixelNormal = mul(mul(Normal, Parameters.TangentToWorld), (MaterialFloat3x3)ResolvedView.TranslatedWorldToView);
// Fetch SceneDepth
float SceneDepth = CalcSceneDepth(BaseUV);
// Straighten PixelNormal to keep refraction closer to screen space
float3 DistortedNormal = PixelNormal;
DistortedNormal.z += sign(DistortedNormal.z) * NormalAlignFactor;
DistortedNormal = normalize(DistortedNormal);
// Adjust BaseUV for refraction (based on IOR, PixelNormal, and ObjectThickness)
float2 NormalUV = DistortedNormal.xy;
NormalUV.y = -NormalUV.y;
float2 RefractionOffset = (ObjectThickness + RefractionThicknessCorrection) * NormalUV * (1.0 / IOR - 1.0);
// Establish max RefractionOffset and scale by SizeOnScreenFactor
RefractionOffset *= MagicRefractionNumber * SizeOnScreenFactor;
BaseUV += RefractionOffset;
float2 NewUV = BaseUV;
float RadialStepSize = UVDistance / RadialSteps;
float2 CurOffset = { 0.0, 0.0 };
// Recalculate SceneDepth with NewUV
SceneDepth = CalcSceneDepth(NewUV);
// Screen aligned TemporalAA jitter
float2 ScenePixels = View.BufferSizeAndInvSize.xy * ScreenUV;
ScenePixels += View.TemporalAAParams.r;
float2 RandomSamp = ((uint)(ScenePixels.x) + 2 * (uint)(ScenePixels.y)) % 5;
RandomSamp += Texture2DSample(RandomTex, RandomTexSampler, View.BufferSizeAndInvSize.xy * ScreenUV / 64).xy;
RandomSamp /= 6;
RandomSamp -= 0.5;
float TempAARotation = RandomSamp.x;
float TempAADistance = RadialStepSize * RandomSamp.x;
BaseUV += View.BufferSizeAndInvSize.zw * RandomSamp;
// Fetch RandomSceneDepth (costly but could improve blur on SceneDepth sharp edges)
// float RandomSceneDepth = CalcSceneDepth(NewUV + RandomSamp * (UVDistance / RadialSteps));
// Calculate BlurAmount
float VolumeBlurAmount = 1.0 - exp(-ObjectThickness * VolumeScatter);
float SurfaceBlurAmount = 1.0 - 1.0 * exp(-(SceneDepth - PixelDepth) * SurfaceScatter); // - 0.5 * exp(-(RandomSceneDepth - PixelDepth) * SurfaceScatter);
float BlurAmount = saturate(VolumeBlurAmount + SurfaceBlurAmount);
float TwoPi = 6.283185;
float3 CurColor = { 0.0, 0.0, 0.0 };
float CurDistance = 0.0;
float Substep = 0.0;
if (BlurAmount == 0.0)
{
return DecodeSceneColorForMaterialNode(NewUV);
}
else
{
int i = 0;
// If the code is applied to scene texture, need to clamp based on viewport bilinear min max
//float2 BilinearOffset = View.BufferSizeAndInvSize.zw * 0.5f;
//float2 BilinearMinBufferUV = ViewportUVToBufferUV(float2(0.0f, 0.0f)) + BilinearOffset;
//float2 BilinearMaxBufferUV = ViewportUVToBufferUV(float2(1.0f, 1.0f)) - BilinearOffset;
while (i < RadialSteps)
{
for (int j = 0; j < AngularSteps; j++)
{
float Angle = TwoPi * ((TempAARotation + Substep) / AngularSteps);
CurOffset.x = cos(Angle);
CurOffset.y = sin(Angle);
CurOffset *= BlurAmount * MagicBlurNumber * View.ViewToClip[0][0] * 0.01;
NewUV = BaseUV + (CurOffset * (CurDistance + TempAADistance));
//NewUV = clamp(NewUV, BilinearMinBufferUV, BilinearMaxBufferUV);
CurColor += DecodeSceneColorForMaterialNode(NewUV);
Substep++;
}
CurDistance += RadialStepSize;
Substep += AngularOffset;
i++;
}
CurColor /= (float) (RadialSteps * AngularSteps);
return CurColor;
}