Files
GTASource/game/shader_source/Lighting/importance_sampling.fxh
T
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

416 lines
15 KiB
HLSL

#pragma strip off
//
// Filtered importance sampling
//
// Copyright (C) 1999-2013 Rockstar Games. All Rights Reserved.
//
#ifndef __RSG_FILTERED_IMPORTANCE_SAMPLING_FXH
#define __RSG_FILTERED_IMPORTANCE_SAMPLING_FXH
#define PARAB_REFLECTION_TARGET_WIDTH (512)
#define PARAB_REFLECTION_TARGET_HEIGHT (256)
#define CUBE_REFLECTION_TARGET_SIZE (256)
#if REFLECTION_CUBEMAP_SAMPLING
#define REFLECTIONS_MAX_MIP (8)
#else
#define REFLECTIONS_MAX_MIP (3)
#endif // REFLECTIONS_SAMPLE_CUBEMAP
#define USE_FILTERED_IMPORTANCE_SAMPLING (1 && (__SHADERMODEL >= 40) && (RSG_PC || RSG_ORBIS || RSG_DURANGO))
#if USE_FILTERED_IMPORTANCE_SAMPLING
#if USE_FILTERED_IMPORTANCE_SAMPLING
#define FIS_ONLY(x) x
#define FIS_SWITCH(__if,__else) (__if)
#else
#define FIS_ONLY(x)
#define FIS_SWITCH(__if,__else) (__else)
#endif
// ----------------------------------------------------------------------------------------------- //
#define NUM_PRECOMPUTED_FIS_SAMPLES (8)
#define NUM_FIS_SAMPLES (8)
#define FIS_PI (3.1415926535897932384626433832795)
// ----------------------------------------------------------------------------------------------- //
#define FIS_BRDF_USE_SMITH_SHLICK_VIS 0
// Non-precomputed path doesn't work on SM3 hardware (due to lack true of integer ops)
#if (RSG_XENON||RSG_PS3) && (NUM_FIS_SAMPLES != NUM_PRECOMPUTED_FIS_SAMPLES)
#error "NUM_FIS_SAMPLES must equal NUM_PRECOMPUTED_FIS_SAMPLES on this platform so that we can use the precomputed table look up!"
#endif // (RSG_XENON||RSG_PS3) && (NUM_FIS_SAMPLES != NUM_PRECOMPUTED_FIS_SAMPLES)
#if REFLECTION_CUBEMAP_SAMPLING
#define REFLECTION_MAP_SIZE PARAB_REFLECTION_TARGET_HEIGHT
#define FISSamplerType samplerCUBE
#else
#define REFLECTION_MAP_SIZE CUBE_REFLECTION_TARGET_SIZE
#define FISSamplerType sampler2D
#endif
// ----------------------------------------------------------------------------------------------- //
float RadicalInverseVdC(uint bits)
{
bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
return float(bits) * 2.3283064365386963e-10; // / 0x100000000
}
// ----------------------------------------------------------------------------------------------- //
float3 PreComputedHammersley3(uint i)
{
// rand = [0,1]
// Xi = <rand, cosRand, sinRand>
#if NUM_PRECOMPUTED_FIS_SAMPLES == 8
float3 g_Hammersley3[NUM_PRECOMPUTED_FIS_SAMPLES] =
{
float3(0.000001,1.000000, 0.000000),
float3(0.125000,-1.000000, 0.000000),
float3(0.250000,0.000000, 1.000000),
float3(0.375000,-0.000000, -1.000000),
float3(0.500000,0.707107, 0.707107),
float3(0.625000,-0.707107, -0.707107),
float3(0.750000,-0.707107, 0.707107),
float3(0.875000,0.707107, -0.707107)
};
#else // of 3
float3 g_Hammersley3[NUM_PRECOMPUTED_FIS_SAMPLES] =
{
float3(0.000001,1.000000, 0.000000),
float3(0.3333333,-1.000000, 0.000000),
float3(0.6666666,0.000000, 1.000000)
};
#endif
return g_Hammersley3[i];
}
// ----------------------------------------------------------------------------------------------- //
float3 NonPreComputedHammersley3(uint i, uint N)
{
// i : sample index
// N : sample count
const float fMinRand = 0.000001f;
const float fMaxRand = 1-fMinRand;
float fRandNum = ((float) i)/((float) N);
fRandNum = fRandNum < fMinRand ? fMinRand : fRandNum;
fRandNum = fRandNum > fMaxRand ? fMaxRand : fRandNum;
float fE2 = 2.0 * FIS_PI * RadicalInverseVdC(i);
return float3( fRandNum, cos(fE2), sin(fE2) );
}
// ----------------------------------------------------------------------------------------------- //
float3 Hammersley3(uint i, uint N)
{
// i : sample index
// N : sample count
/*
#if NUM_FIS_SAMPLES == NUM_PRECOMPUTED_FIS_SAMPLES
return PreComputedHammersley3(i);
#else
return NonPreComputedHammersley3(i, N);
#endif
*/
if ( NUM_PRECOMPUTED_FIS_SAMPLES == N ) // The compiler *should* strip this...
{
return PreComputedHammersley3(i);
}
else
{
return NonPreComputedHammersley3(i, N);
}
}
// ----------------------------------------------------------------------------------------------- //
float FISPreComputeLODFactor( float nNumSamples, float nMapWidth, float fSpecExponent )
{
// A 1-mip bias ensures some sample overlap which reduces aliasing due to under-sampling
// See section 20.4 of http://http.developer.nvidia.com/GPUGems3/gpugems3_ch20.html
//const float fMIPBias = (RSG_XENON || RSG_PS3) ? 1.0 : 0.0;
const float fMIPBias = saturate(1-(fSpecExponent/96.0));
float fLODPreComp = log2((nMapWidth*nMapWidth*6)/nNumSamples/(4.f*FIS_PI))*0.5f + fMIPBias;
return fLODPreComp;
}
float3 ImportanceSampleDiffuse(float3 Xi, float3 N, float fLODPreComp, out float fSampleLOD, out float fSampleScale )
{
float r = sqrt(Xi.x);
float3 vSampleDirection;
vSampleDirection.x = Xi.y * r;
vSampleDirection.y = Xi.z * r;
vSampleDirection.z = sqrt( 1.-dot(vSampleDirection.xy,vSampleDirection.xy));
float fPDF = vSampleDirection.z/FIS_PI;
fSampleLOD = max(0.f, fLODPreComp - log2(fPDF)*0.25f);
fSampleScale = 1;
// Tangent to world space
float3 UpVector = abs(N.z) < 0.999 ? float3(0,0,1) : float3(1,0,0);
float3 TangentX = normalize( cross( UpVector, N ) );
float3 TangentY = cross( N, TangentX );
float3 L = TangentX * vSampleDirection.x + TangentY * vSampleDirection.y + N * vSampleDirection.z;
return L;
}
// ----------------------------------------------------------------------------------------------- //
float3 ImportanceSampleLambertion(float3 Xi, float3 N )
{
float r = sqrt(Xi.x);
float3 vSampleDirection;
vSampleDirection.x = Xi.y * r;
vSampleDirection.y = Xi.z * r;
vSampleDirection.z = sqrt( 1.-dot(vSampleDirection.xy,vSampleDirection.xy));
// Tangent to world space could be computed once
float3 UpVector = abs(N.z) < 0.999 ? float3(0,0,1) : float3(1,0,0);
float3 TangentX = normalize( cross( UpVector, N ) );
float3 TangentY = cross( N, TangentX );
float3 L = TangentX * vSampleDirection.x + TangentY * vSampleDirection.y + N * vSampleDirection.z;
return L;
}
// ----------------------------------------------------------------------------------------------- //
float3 ImportanceSamplePhong( float3 Xi, float fSpecExponent, float3 R, float fLODPreComp, out float fSampleLOD, out float fSampleScale )
{
float fCosTheta = pow(Xi.x, 1.f/(fSpecExponent+1.f));
float fSinTheta = sqrt(1.f - fCosTheta*fCosTheta);
float3 vSampleDirection;
vSampleDirection.x = Xi.y * fSinTheta;
vSampleDirection.y = Xi.z * fSinTheta;
vSampleDirection.z = fCosTheta;
// Here's the full equation. As you can see, the PDF cancels out much of the BRDF terms. Find simplified version below.
//float fPDF = (fSpecExponent+1)/(FIS_PI*2.f) * pow(fCosTheta, fSpecExponent);
//fSampleLOD = max(0.f, fLODPreComp - log2(fPDF)*0.5f);
//fSampleScale = (fSpecExponent+1)/(FIS_PI*2.f) * pow(fCosTheta, fSpecExponent) * fCosTheta / fPDF;
// Simplified version of the above
float fPDF = (fSpecExponent+1)/(FIS_PI*2.f) * pow(fCosTheta, fSpecExponent);
fSampleLOD = max(0.f, fLODPreComp - log2(fPDF)*0.5f);
fSampleScale = fCosTheta;
// Tangent to world space
float3 UpVector = abs(R.z) < 0.999 ? float3(0,0,1) : float3(1,0,0);
float3 TangentX = normalize( cross( UpVector, R ) );
float3 TangentY = cross( R, TangentX );
float3 L = TangentX * vSampleDirection.x + TangentY * vSampleDirection.y + R * vSampleDirection.z;
return L;
}
// ----------------------------------------------------------------------------------------------- //
float3 ImportanceSamplePhong( float3 Xi, float fSpecExponent, float3 N )
{
float fLODPreComp=0;
float fSampleLOD=0;
float fSampleScale=0;
return ImportanceSamplePhong(Xi, fSpecExponent, N, fLODPreComp, fSampleLOD, fSampleScale);
}
// ----------------------------------------------------------------------------------------------- //
float3 ImportanceSampleBlinnPhong( float3 Xi, float fSpecExponent, float fFresnelCoef, float3 N, float3 V, float fLODPreComp, out float fSampleLOD, out float fSampleScale )
{
float fCosTheta = pow(Xi.x, 1.f/(fSpecExponent+1.f));
float fSinTheta = sqrt(1.f - fCosTheta*fCosTheta);
float3 vSampleDirection;
vSampleDirection.x = Xi.y * fSinTheta;
vSampleDirection.y = Xi.z * fSinTheta;
vSampleDirection.z = fCosTheta;
// Tangent to world space
float3 UpVector = abs(N.z) < 0.999 ? float3(0,0,1) : float3(1,0,0);
float3 TangentX = normalize( cross( UpVector, N ) );
float3 TangentY = cross( N, TangentX );
float3 H = TangentX * vSampleDirection.x + TangentY * vSampleDirection.y + N * vSampleDirection.z;
//H = dot( N, H ) < 0.0f ? -H : H; // Flip sample if it's below the horizon
H = dot( V, H ) < 0.0f ? -H : H; // Flip sample if it's below the horizon
// Reflect V about H to get the sampling direction L
float3 L = 2 * dot( V, H ) * H - V; // reflect( -V, N );
float HoV = dot(H, V);
float HoL = saturate( dot(H, L) );
float NoH = dot(N, H);
float NoV = dot(N, V);
float NoL = dot(N, L);
float fPDF = ((fSpecExponent+2) * pow(NoH, fSpecExponent)) / (2.0 * FIS_PI * 4.f * HoV);
fSampleLOD = max(0.f, fLODPreComp - log2(fPDF)*0.5f);
// F = Fresnel
// G = Geometric attenuation term
// D = Normal distribution
// BRDF = D * F * G / ( 4 * NoL * NoV )
// Original
float F = fresnelSlick( fFresnelCoef, HoL);
float D = pow(NoH, fSpecExponent) * ((fSpecExponent+2)/(2.0*FIS_PI));
#if FIS_BRDF_USE_SMITH_SHLICK_VIS
float k=2./sqrt(FIS_PI*(fSpecExponent+2.));
float Vi = 1./(4.* (NoL*(1.-k)+k)* (NoV*(1.-k)+k) );
float fBRDF = (Vi * D * F);
#else
float G = min(1.f, min((2.f * NoH * NoV / HoV),(2.f * NoH * NoL / HoV)));
float fBRDF = (G * D * F) / (4.0 * NoV * NoL );
#endif
fSampleScale = fPDF > 0.0 ? fBRDF * NoL / fPDF : 0;
#if 0
float SheenColor = 0.;//float3(1.f,.5,0.2);
fSampleScale += SheenColor * F*.1;
#endif
fSampleScale = fSampleScale / FIS_PI; // TODO: Move this out of the inner-loop
// WRONG! This is incorrect, I haven't gotten around to refactoring it yet.
// Simplified version of the original (a bunch of terms cancel out!)
//float F = fresnelSlick(fFresnelCoef, HoL);
//fSampleScale = F * 2.0 * FIS_PI * HoV;
//fSampleScale = F * 2.0 * HoV;
//fSampleScale = F * HoV;
//fSampleScale = F;
//fSampleScale = dot(N, L)<0 ? 0 : fSampleScale; // This shouldn't be necessary... and yet it is. There must be a problem with flipping H up above.
return L;
}
// ----------------------------------------------------------------------------------------------- //
#if !defined(SHADER_FINAL)
// Useful for visualizing the BRDF and Sample overlap on a unit sphere of constant specularity... such as in the testbed
// Visualize: BRDF support region, BRDF value, Importance sample locations
float4 FISVisualizeSampleDistribution ( float3 vNormal, float3 vView, float fSpecExponent, uint nNumSamples )
{
// Approximate solid angle of texel (assume no projection distortion)
float fLODPreComp = FISPreComputeLODFactor( nNumSamples, REFLECTION_MAP_SIZE, fSpecExponent );
float3 vReflectionVec = reflect(-vView, vNormal);
float fSampleLocationAccum = 0;
float fSamplePDFScaleAccum = 0;
for( uint i = 0; i < nNumSamples; i++ )
{
float3 Xi = Hammersley3( i, nNumSamples );
float fSampleLOD = 0, fSamplePDFScale = 1;
float3 H = ImportanceSamplePhong( Xi, fSpecExponent, float3(0,0,1), fLODPreComp, fSampleLOD, fSamplePDFScale );
fSampleLocationAccum += pow( saturate(dot(H,vNormal)), 8192.0) * fSamplePDFScale; // Super high exponent to "light" sample directions on sphere
fSamplePDFScaleAccum += fSamplePDFScale;
}
float fBlinnPhong = pow( saturate(dot(vNormal,float3(0,0,1))), fSpecExponent );
//float fBRDFSupport = (fBlinnPhong>0.0 && fBlinnPhong<1e-30) ? 0.125 : 0;
float fBRDFSupport = fBlinnPhong>0.0 ? 0.05 : 0;
float fBRDFFalloff = fBlinnPhong * 1.0;
float fSampleLocation = fSampleLocationAccum/fSamplePDFScaleAccum * 10;
return fSampleLocation.xxxx + fBRDFSupport.xxxx + fBRDFFalloff.xxxx;
}
#endif // !defined(SHADER_FINAL)
// ----------------------------------------------------------------------------------------------- //
float3 FISPhong(FISSamplerType sSampler, float3 vNormal, float3 vView, float fSpecExponent, uint nNumSamples )
{
// Useful for visualizing the sample distribution on a sphere with contant spec exponent (such as in the test levels)
//return FISVisualizeSampleDistribution( vNormal, vView, fSpecExponent, nNumSamples ).rgb;
// Approximate solid angle of texel (assume no projection distortion)
float fLODPreComp = FISPreComputeLODFactor( nNumSamples, REFLECTION_MAP_SIZE, fSpecExponent );
//fSpecExponent = max(1, fSpecExponent*0.25);
float4 vSampleAccumulator = 0;
float3 R = reflect( -vView, vNormal );
for( uint i = 0; i < nNumSamples; i++ )
{
float3 Xi = Hammersley3( i, nNumSamples );
float fSampleLOD = 0, fSamplePDFScale = 1;
float3 L = ImportanceSamplePhong( Xi, fSpecExponent, R, fLODPreComp, fSampleLOD, fSamplePDFScale );
//float NoL = saturate( dot( vNormal, L ) );
[branch] if( fSamplePDFScale > 0 )
{
float3 vSampleColor = texCUBElod(sSampler,float4(-L,fSampleLOD)).rgb;
float3 vSample = vSampleColor * fSamplePDFScale;
vSampleAccumulator += float4(vSample,1);
}
}
return (vSampleAccumulator.rgb/max(vSampleAccumulator.a,1));
}
// ----------------------------------------------------------------------------------------------- //
// This is not mathematically correct Blinn-Phong... use at your own risk
float3 FISBlinnPhong(FISSamplerType sSampler, float3 vNormal, float3 vView, float fSpecExponent, float fFresnelCoef, uint nNumSamples)
{
// Useful for visualizing the sample distribution on a sphere with contant spec exponent (such as in the test levels)
// return FISVisualizeSampleDistribution( vNormal, vView, fSpecExponent, nNumSamples ).rgb;
// Approximate solid angle of texel (assume no projection distortion)
float fLODPreComp = FISPreComputeLODFactor( nNumSamples, REFLECTION_MAP_SIZE, fSpecExponent );
//fSpecExponent = max(fSpecExponent,1);
float3 vReflect = reflect( -vView, vNormal );
float4 vSampleAccumulator = 0;
for( uint i = 0; i < nNumSamples; i++ )
{
float3 Xi = Hammersley3( i, nNumSamples );
float fSampleLOD = 0;
float fSamplePDFScale = 1;
float3 L = ImportanceSampleBlinnPhong( Xi, fSpecExponent, fFresnelCoef, vNormal, vView, fLODPreComp, fSampleLOD, fSamplePDFScale );
[branch] if( all(fSamplePDFScale > 0 ))
{
float3 vSampleColor = texCUBElod(sSampler, float4(L, 0)).rgb;
vSampleAccumulator += float4(vSampleColor.rgb * fSamplePDFScale, 1);
}
}
return (vSampleAccumulator.rgb/max(vSampleAccumulator.a,1));
}
float3 FISDiffuse(FISSamplerType sSampler, float3 vNormal, uint nNumSamples )
{
// Approximate solid angle of texel (assume no projection distortion)
float fLODPreComp = FISPreComputeLODFactor( nNumSamples, REFLECTION_MAP_SIZE, 512 );
float4 vSampleAccumulator = 0;
for( uint i = 0; i < nNumSamples; i++ )
{
float3 Xi = Hammersley3( i, nNumSamples );
float fSampleLOD = 0, fSamplePDFScale = 1;
float3 L = ImportanceSampleDiffuse( Xi, vNormal, fLODPreComp, fSampleLOD, fSamplePDFScale );
{
float3 vSampleColor = texCUBElod(sSampler, float4(-L, fSampleLOD)).rgb;
float3 vSample = vSampleColor * fSamplePDFScale;
vSampleAccumulator += float4(vSample,1);
}
}
return (vSampleAccumulator.rgb/nNumSamples);
}
#endif // USE_FILTERED_IMPORTANCE_SAMPLING
#endif // __RSG_FILTERED_IMPORTANCE_SAMPLING_FXH