You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

227 lines
7.9 KiB

#ifndef REBLUR_UTILITIES_H_
#define REBLUR_UTILITIES_H_
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonLighting.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Builtin/BuiltinData.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Raytracing/ReBlurDenoiser.cs.hlsl"
// Accumulation loop is done on 32 frames
#define MAX_ACCUM_FRAME_NUM 32.0
#define MIP_LEVEL_COUNT 4.0
#define MAX_FRAME_NUM_WITH_HISTORY_FIX 4.0
float2 RotateVector(float4 rotator, float2 v)
{
return v.x * rotator.xz + v.y * rotator.yw;
}
float UVInScreen( float2 uv )
{
return float( all( saturate( uv ) == uv ) );
}
// IMPORTANT:
// - works for "negative x" only
// - huge error for x < -2, but still applicable for "weight" calculations
// https://www.desmos.com/calculator/cd3mvg1gfo
#define ExpApprox( x ) \
rcp( ( x ) * ( x ) - ( x ) + 1.0 )
// Must be used for noisy data
// https://www.desmos.com/calculator/9yoyc3is2g
// scale = 3-5 is needed to match energy in "_ComputeNonExponentialWeight" ( especially when used in a recurrent loop )
#define _ComputeExponentialWeight( x, px, py ) \
ExpApprox( -NRD_EXP_WEIGHT_DEFAULT_SCALE * abs( ( x ) * ( px ) + ( py ) ) )
// A good choice for non noisy data
// IMPORTANT: cutoffs are needed to minimize floating point precision drifting
#define _ComputeNonExponentialWeight( x, px, py ) \
smoothstep( 0.999, 0.001, abs( ( x ) * ( px ) + ( py ) ) )
float GetSpecularLobeHalfAngle( float linearRoughness, float percentOfVolume = 0.75 )
{
float m = linearRoughness * linearRoughness;
// https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf (page 72)
// TODO: % of NDF volume - is it the trimming factor from VNDF sampling?
return atan( m * percentOfVolume / ( 1.0 - percentOfVolume ) );
}
float GetSpecMagicCurve2( float roughness, float percentOfVolume = 0.987 )
{
float angle = GetSpecularLobeHalfAngle( roughness, percentOfVolume );
float almostHalfPi = GetSpecularLobeHalfAngle( 1.0, percentOfVolume );
return saturate( angle / almostHalfPi );
}
float GetCombinedWeight(float2 geometryWeightParams, float3 Nv, float3 Xvs, float normalWeightParams, float3 N, float4 Ns, float2 roughnessWeightParams = 0)
{
float3 a = float3( geometryWeightParams.x, normalWeightParams, roughnessWeightParams.x );
float3 b = float3( geometryWeightParams.y, 0.0, roughnessWeightParams.y );
float3 t;
t.x = dot( Nv, Xvs );
t.y = FastACos( saturate( dot( N, Ns.xyz ) ) );
t.z = Ns.w;
float3 w = _ComputeNonExponentialWeight( t, a, b );
return w.x * w.y * w.z;
}
#define SPECULAR_DOMINANT_DIRECTION_G2 0
#define SPECULAR_DOMINANT_DIRECTION_G1 1
#define SPECULAR_DOMINANT_DIRECTION_DEFAULT 2
float GetSpecularDominantFactor(float NoV, float linearRoughness)
{
float a = 0.298475 * log( 39.4115 - 39.0029 * linearRoughness );
float dominantFactor = pow(saturate(1.0 - NoV), 10.8649 ) * ( 1.0 - a ) + a;
return saturate(dominantFactor);
}
float3 GetSpecularDominantDirectionWithFactor( float3 N, float3 V, float dominantFactor )
{
float3 R = reflect( -V, N );
float3 D = lerp( N, R, dominantFactor );
return normalize( D );
}
float4 GetSpecularDominantDirection( float3 N, float3 V, float linearRoughness)
{
float NoV = abs( dot( N, V ) );
float dominantFactor = GetSpecularDominantFactor( NoV, linearRoughness);
return float4( GetSpecularDominantDirectionWithFactor( N, V, dominantFactor ), dominantFactor );
}
float2x3 GetKernelBasis( float3 V, float3 N, float linearRoughness)
{
float3x3 basis = GetLocalFrame(N);
float3 T = basis[0];
float3 B = basis[1];
float NoV = abs(dot( N, V ));
float f = GetSpecularDominantFactor(NoV, linearRoughness);
float3 R = reflect( -V, N );
float3 D = normalize( lerp( N, R, f ) );
float NoD = abs( dot( N, D ) );
if( NoD < 0.999 && linearRoughness != 1.0 )
{
float3 Dreflected = reflect( -D, N );
T = normalize( cross( N, Dreflected ) );
B = cross( Dreflected , T );
float NoV = abs( dot( N, V ) );
float acos01sq = saturate( 1.0 - NoV );
float skewFactor = lerp( 1.0, linearRoughness , sqrt( acos01sq ) );
T *= skewFactor;
}
return float2x3( T, B );
}
uint ReverseBits4( uint x )
{
x = ( ( x & 0x5 ) << 1 ) | ( ( x & 0xA ) >> 1 );
x = ( ( x & 0x3 ) << 2 ) | ( ( x & 0xC ) >> 2 );
return x;
}
uint Bayer4x4ui( uint2 samplePos, uint frameIndex)
{
uint2 samplePosWrap = samplePos & 3;
uint a = 2068378560 * ( 1 - ( samplePosWrap.x >> 1 ) ) + 1500172770 * ( samplePosWrap.x >> 1 );
uint b = ( samplePosWrap.y + ( ( samplePosWrap.x & 1 ) << 2 ) ) << 2;
return ( ( a >> b ) + frameIndex ) & 0xF;
}
// RESULT: [0; 1)
float Bayer4x4(uint2 samplePos, uint frameIndex)
{
uint bayer = Bayer4x4ui(samplePos, frameIndex);
return float(bayer) / 16.0;
}
float2 GetKernelSampleCoordinates(float3 offset, float3 X, float3 T, float3 B, float4 rotator)
{
// We can't rotate T and B instead, because T is skewed
offset.xy = RotateVector(rotator, offset.xy);
// Compute the world space position
float3 wsPos = X + T * offset.x + B * offset.y;
// Evaluate the NDC position
float4 hClip = TransformWorldToHClip(wsPos);
hClip.xyz /= hClip.w;
// Convert it to screen sample space
float2 nDC = hClip.xy * 0.5 + 0.5;
#if UNITY_UV_STARTS_AT_TOP
nDC.y = 1.0 - nDC.y;
#endif
return nDC;
}
float GetModifiedRoughnessFromNormalVariance(float linearRoughness, float3 nonNormalizedAverageNormal)
{
// https://blog.selfshadow.com/publications/s2013-shading-course/rad/s2013_pbs_rad_notes.pdf (page 20)
float l = length( nonNormalizedAverageNormal );
float kappa = saturate( 1.0 - l * l ) * rcp( l * ( 3.0 - l * l ) );
return sqrt(max(0, linearRoughness * linearRoughness + kappa));
}
float ComputeParallax(float3 currentViewWS, float3 previousPositionWS)
{
// Compute the previous view vector
float3 previousViewWS = normalize(_PrevCamPosRWS - previousPositionWS);
// Compute the cosine between both angles
float cosa = saturate(dot(currentViewWS, previousViewWS));
// Evaluate the tangent of the angle
return sqrt( 1.0 - cosa * cosa ) / max(cosa, 1e-6);
}
// SPEC_ACCUM_CURVE = 1.0 (aggressiveness of history rejection depending on viewing angle: 1 = low, 0.66 = medium, 0.5 = high)
#define SPEC_ACCUM_CURVE 0.5
// SPEC_ACCUM_BASE_POWER = 0.5-1.0 (greater values lead to less aggressive accumulation)
#define SPEC_ACCUM_BASE_POWER 1.0
float GetSpecAccumSpeed(float linearRoughness, float NoV, float parallax)
{
float acos01sq = 1.0 - NoV; // Approximation of acos^2 in normalized
float a = pow(saturate(acos01sq), SPEC_ACCUM_CURVE);
float b = 1.1 + linearRoughness * linearRoughness;
float parallaxSensitivity = (b + a) / (b - a);
float powerScale = 1.0 + parallax * parallaxSensitivity;
float f = 1.0 - exp2(-200.0 * linearRoughness * linearRoughness);
f *= pow(saturate(linearRoughness), SPEC_ACCUM_BASE_POWER * powerScale);
return MAX_ACCUM_FRAME_NUM * f;
}
#define K 0.5
float HitDistanceAttenuation(float linearRoughness, float cameraDistance, float hitDistance)
{
float f = hitDistance / (hitDistance + cameraDistance);
return lerp(K * linearRoughness, 1.0, f);
}
void EvaluateSurfaceMotionUV(uint2 currentCoord, PositionInputs posInputs,
out float2 historyUVUnscaled,
out float2 historySurfaceMotionCoord,
out float2 historySurfaceMotionUV,
out float2 velocity)
{
// Decode the velocity of the pixel
velocity = float2(0.0, 0.0);
DecodeMotionVector(LOAD_TEXTURE2D_X(_CameraMotionVectorsTexture, (float2)currentCoord.xy), velocity);
// Compute the pixel coordinate for the history tapping
historyUVUnscaled = posInputs.positionNDC - velocity;
historySurfaceMotionCoord = (float2)(historyUVUnscaled * _HistorySizeAndScale.xy);
historySurfaceMotionUV = historyUVUnscaled * _HistorySizeAndScale.zw;
}
#endif // REBLUR_UTILITIES_H_