270 lines
6.8 KiB
HLSL
270 lines
6.8 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
// G( v; u,L,a ) = a * exp( L * (dot(u,v) - 1) )
|
|
|
|
struct FSphericalGaussian
|
|
{
|
|
float3 Axis; // u
|
|
float Sharpness; // L
|
|
float Amplitude; // a
|
|
};
|
|
|
|
float Evaluate( FSphericalGaussian G, float3 Direction )
|
|
{
|
|
// G( v; u,L,a ) = a * exp( L * (dot(u,v) - 1) )
|
|
|
|
return G.Amplitude * exp( G.Sharpness * (dot( G.Axis, Direction ) - 1) );
|
|
}
|
|
|
|
// Integral over the sphere
|
|
float Integral( FSphericalGaussian G )
|
|
{
|
|
// integral G = 2*PI * a/L * ( 1 - exp(-2*L) )
|
|
// integral G ~= 2*PI * a/L, L > 2
|
|
|
|
return (2*PI) * G.Amplitude / G.Sharpness * ( 1 - exp( -2 * G.Sharpness ) );
|
|
}
|
|
|
|
// G / integral G
|
|
FSphericalGaussian Normalize( FSphericalGaussian G )
|
|
{
|
|
// G / integral G = G( v; u, L, L / ( 2*PI * ( 1 - exp(-2*L) ) ) )
|
|
// G / integral G ~= G( v; u, L, L / (2*PI) ), L > 2
|
|
|
|
G.Amplitude = G.Sharpness / ( (2*PI) - (2*PI) * exp( -2 * G.Sharpness ) );
|
|
return G;
|
|
}
|
|
|
|
// G0 * G1
|
|
FSphericalGaussian Mul( FSphericalGaussian G0, FSphericalGaussian G1 )
|
|
{
|
|
// um = L0 * u0 + L1 * u1
|
|
// G0 * G1 = G( v; um / |um|, |um|, a0*a1 * exp( |um| - L0 - L1 ) )
|
|
|
|
float Lm = G0.Sharpness + G1.Sharpness;
|
|
float3 um = G0.Sharpness * G0.Axis + G1.Sharpness * G1.Axis;
|
|
float umLength = length(um);
|
|
|
|
FSphericalGaussian G =
|
|
{
|
|
um / umLength,
|
|
umLength,
|
|
G0.Amplitude * G1.Amplitude * exp( umLength - Lm )
|
|
};
|
|
|
|
return G;
|
|
}
|
|
|
|
// integral G0 * G1
|
|
float Dot( FSphericalGaussian G0, FSphericalGaussian G1 )
|
|
{
|
|
// integral G0 * G1 = 4*PI * a0 * a1 * exp( -L0 - L1 ) * sinh( |um| ) / |um|
|
|
// integral G0 * G1 = 2*PI * a0 * a1 * ( exp( |um| - L0 - L1 ) - exp( -|um| - L0 - L1 ) ) / |um|
|
|
// integral G0 * G1 ~= 2*PI * a0 * a1 * exp( |um| - L0 - L1 ) / |um|
|
|
|
|
float Lm = G0.Sharpness + G1.Sharpness;
|
|
float3 um = G0.Sharpness * G0.Axis + G1.Sharpness * G1.Axis;
|
|
float umLength = length(um);
|
|
|
|
//return 2*PI * G0.a * G1.a * ( exp( umLength - G0.L - G1.L ) ) / umLength;
|
|
//return 2*PI * G0.Amplitude * G1.Amplitude * ( exp( umLength - Lm ) - exp( -umLength - Lm ) ) / umLength;
|
|
return (2*PI) * G0.Amplitude * G1.Amplitude * exp( umLength - Lm ) * ( 1 - exp( -2 * umLength ) ) / umLength;
|
|
}
|
|
|
|
// [ Iwasaki 2012, "Interactive Bi-scale Editing of Highly Glossy Materials" ]
|
|
FSphericalGaussian Convolve( FSphericalGaussian G0, FSphericalGaussian G1 )
|
|
{
|
|
FSphericalGaussian G =
|
|
{
|
|
G0.Axis,
|
|
( G0.Sharpness * G1.Sharpness ) / ( G0.Sharpness + G1.Sharpness ),
|
|
(2*PI) * ( G0.Amplitude * G1.Amplitude ) / ( G0.Sharpness + G1.Sharpness )
|
|
};
|
|
|
|
return G;
|
|
}
|
|
|
|
// approximation using von Mises-Fisher
|
|
FSphericalGaussian ToSphericalGaussian( float3 r, float Value )
|
|
{
|
|
// L = |r| * ( 3 - |r|^2 ) / ( 1 - |r|^2 )
|
|
|
|
FSphericalGaussian G;
|
|
|
|
float LengthR2 = dot( r, r );
|
|
float InvLengthR = rsqrt( LengthR2 );
|
|
float LengthR = LengthR2 * InvLengthR;
|
|
|
|
G.Axis = r * InvLengthR;
|
|
G.Sharpness = LengthR * ( 3 - LengthR2 ) / ( 1 - min( LengthR2, 0.9999 ) );
|
|
G.Amplitude = Value * G.Sharpness / ( (2*PI) - (2*PI) * exp( -2 * G.Sharpness ) );
|
|
//G.u = Value * G.L * (0.5/PI);
|
|
|
|
return G;
|
|
}
|
|
|
|
FSphericalGaussian Add( FSphericalGaussian G0, FSphericalGaussian G1 )
|
|
{
|
|
// r = ( 1 / tanh(L) - 1/L ) * u
|
|
// r = ( ( 1 + exp( -2*L ) ) / ( 1 - exp( -2*L ) ) - 1/L ) * u
|
|
// r ~= ( 1 - 1/L + 2 * exp( -2*L ) ) * u, L > 2
|
|
|
|
float exp2L0 = exp( -2 * G0.Sharpness );
|
|
float exp2L1 = exp( -2 * G1.Sharpness );
|
|
|
|
float3 r0 = ( (1 + exp2L0) / (1 - exp2L0) - rcp( G0.Sharpness ) ) * G0.Axis;
|
|
float3 r1 = ( (1 + exp2L1) / (1 - exp2L1) - rcp( G1.Sharpness ) ) * G1.Axis;
|
|
float w0 = Integral( G0 );
|
|
float w1 = Integral( G1 );
|
|
|
|
float3 r = ( r0*w0 + r1*w1 ) / (w0 + w1);
|
|
float w = w0 + w1;
|
|
|
|
return ToSphericalGaussian( r, w );
|
|
}
|
|
|
|
// Angle from axis of cone. Half subtended angle.
|
|
float GetConeAngle( FSphericalGaussian G )
|
|
{
|
|
// cone angle ~= PI - PI*|r|
|
|
// r = ( 1 / tanh(k) - 1/k ) * u ~= ( 1 - 1/k + 2 * exp( -2*k ) ) * u, k > 2
|
|
// ConeAngle ~= sqrt( 2/L )
|
|
|
|
return sqrt( 2 / G.Sharpness );
|
|
}
|
|
|
|
// Inner product with cosine lobe
|
|
// Assumes G is normalized
|
|
float DotCosineLobe( FSphericalGaussian G, float3 N )
|
|
{
|
|
const float muDotN = dot( G.Axis, N );
|
|
|
|
const float c0 = 0.36;
|
|
const float c1 = 0.25 / c0;
|
|
|
|
float eml = exp( -G.Sharpness );
|
|
float em2l = eml * eml;
|
|
float rl = rcp( G.Sharpness );
|
|
|
|
float scale = 1.0f + 2.0f * em2l - rl;
|
|
float bias = (eml - em2l) * rl - em2l;
|
|
|
|
float x = sqrt( 1.0 - scale );
|
|
float x0 = c0 * muDotN;
|
|
float x1 = c1 * x;
|
|
|
|
float n = x0 + x1;
|
|
float y = ( abs( x0 ) <= x1 ) ? n*n / x : saturate( muDotN );
|
|
|
|
return scale * y + bias;
|
|
}
|
|
|
|
// [ Wang et al. 2009, "All-Frequency Rendering of Dynamic, Spatially-Varying Reflectance" ]
|
|
FSphericalGaussian ClampedCosine_ToSphericalGaussian( float3 Normal )
|
|
{
|
|
FSphericalGaussian G;
|
|
|
|
G.Axis = Normal;
|
|
G.Sharpness = 2.133;
|
|
G.Amplitude = 1.17;
|
|
|
|
// Integrate to PI
|
|
//G.Sharpness = 2.3;
|
|
//G.Amplitude = 0.5 * 2.3 / ( 1 - exp( -2 * 2.3 ) );
|
|
|
|
return G;
|
|
}
|
|
|
|
FSphericalGaussian Hemisphere_ToSphericalGaussian( float3 Normal )
|
|
{
|
|
FSphericalGaussian G;
|
|
|
|
G.Axis = Normal;
|
|
G.Sharpness = 0.81;
|
|
G.Amplitude = 0.81 / ( 1 - exp( -2 * 0.81 ) );
|
|
|
|
return G;
|
|
}
|
|
|
|
// Bent normal is normalized. AO is [0,1]. Both are cosine weighted.
|
|
FSphericalGaussian BentNormalAO_ToSphericalGaussian( float3 BentNormal, float AO )
|
|
{
|
|
// ConeAngle ~= sqrt( 2/L )
|
|
// L ~= 2/ConeAngle^2
|
|
|
|
FSphericalGaussian G;
|
|
|
|
G.Axis = BentNormal;
|
|
|
|
#if 1
|
|
// Cosine weighted integration of spherical cap
|
|
// PI * SinAlpha^2
|
|
// L ~= 2 / Pow2( acos( sqrt(1- AO) ) );
|
|
|
|
// Approximation (no acos)
|
|
G.Sharpness = ( 0.75 + 1.25 * sqrt( 1 - AO ) ) / AO;
|
|
#else
|
|
// Solid angle of cone = 2 * PI * (1 - CosTheta)
|
|
// Solid angle of cone = 2*PI * AO
|
|
// AO = 1 - cos( ConeAngle )
|
|
// L ~= 2 / Pow2( acos( 1- AO ) );
|
|
|
|
// Approximation (no acos)
|
|
G.Sharpness = ( 1 - 0.19 * AO ) / AO;
|
|
#endif
|
|
|
|
// AO=1 integrates to 2pi
|
|
const float HemisphereSharpness = 0.81;
|
|
G.Amplitude = HemisphereSharpness / ( 1 - exp( -2 * HemisphereSharpness ) );
|
|
|
|
return G;
|
|
}
|
|
|
|
/*
|
|
static const float C[5] =
|
|
{
|
|
0.5 / sqrt(PI),
|
|
0.5 * sqrt(3/PI),
|
|
};
|
|
|
|
struct FSphericalHarmonics
|
|
{
|
|
float SH[4];
|
|
};
|
|
|
|
FSphericalHarmonics Add( float3 Direction, float Value )
|
|
{
|
|
}*/
|
|
|
|
struct FAnisoSphericalGaussian
|
|
{
|
|
float3 AxisX;
|
|
float3 AxisY;
|
|
float3 AxisZ;
|
|
float SharpnessX;
|
|
float SharpnessY;
|
|
float Amplitude;
|
|
};
|
|
|
|
float Evaluate( FAnisoSphericalGaussian ASG, float3 Direction )
|
|
{
|
|
float L = ASG.SharpnessX * Pow2( dot( Direction, ASG.AxisX ) );
|
|
float u = ASG.SharpnessY * Pow2( dot( Direction, ASG.AxisY ) );
|
|
return ASG.Amplitude * saturate( dot( Direction, ASG.AxisZ ) ) * exp( -L - u );
|
|
}
|
|
|
|
float Dot( FAnisoSphericalGaussian ASG, FSphericalGaussian SG )
|
|
{
|
|
// ASG( v; u,nu,a ) = a * exp( 2 * nu * (dot(u,v) - 1) )
|
|
|
|
float nu = SG.Sharpness * 0.5;
|
|
|
|
ASG.Amplitude *= SG.Amplitude;
|
|
ASG.Amplitude *= PI * rsqrt( (nu + ASG.SharpnessX) * (nu + ASG.SharpnessY) );
|
|
ASG.SharpnessX = (nu * ASG.SharpnessX) / (nu + ASG.SharpnessX);
|
|
ASG.SharpnessY = (nu * ASG.SharpnessY) / (nu + ASG.SharpnessY);
|
|
|
|
return Evaluate( ASG, SG.Axis );
|
|
} |