You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
no backscattering yet, might replace Preintegrated and Subsurface shading models, can be optimized, postprocess pass only runs if an object on the screen is using it, uses SeparableSSS by Jorge Jimenez and Diego Gutierrez [CL 2236313 by Martin Mittring in Main branch]
508 lines
15 KiB
Plaintext
508 lines
15 KiB
Plaintext
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
PostprocessAmbient.usf: To apply a ambient cubemap as a postprocess
|
|
=============================================================================*/
|
|
|
|
#include "Common.usf"
|
|
#include "PostProcessCommon.usf"
|
|
#include "DeferredShadingCommon.usf"
|
|
#include "CubemapCommon.usf"
|
|
#include "Random.usf"
|
|
#include "BRDF.usf"
|
|
#include "MonteCarlo.usf"
|
|
|
|
#define IMPORTANCE_SAMPLE 0
|
|
|
|
float3 DiffuseIBL( uint2 Random, float3 DiffuseColor, float3 N )
|
|
{
|
|
float3 DiffuseLighting = 0;
|
|
|
|
const uint NumSamples = 32;
|
|
for( uint i = 0; i < NumSamples; i++ )
|
|
{
|
|
float2 E = Hammersley( i, NumSamples, Random );
|
|
float3 L = TangentToWorld( CosineSampleHemisphere( E ).xyz, N );
|
|
|
|
float NoL = saturate( dot( N, L ) );
|
|
if( NoL > 0 )
|
|
{
|
|
float3 SampleColor = AmbientCubemap.SampleLevel( AmbientCubemapSampler, L, 0 ).rgb;
|
|
|
|
// lambert = DiffuseColor * NoL / PI
|
|
// pdf = NoL / PI
|
|
DiffuseLighting += SampleColor * DiffuseColor;
|
|
}
|
|
}
|
|
|
|
return DiffuseLighting / NumSamples;
|
|
}
|
|
|
|
float3 SpecularIBL( uint2 Random, float3 SpecularColor, float Roughness, float3 N, float3 V )
|
|
{
|
|
float3 SpecularLighting = 0;
|
|
|
|
const uint NumSamples = 32;
|
|
for( uint i = 0; i < NumSamples; i++ )
|
|
{
|
|
float2 E = Hammersley( i, NumSamples, Random );
|
|
float3 H = TangentToWorld( ImportanceSampleGGX( E, Roughness ).xyz, N );
|
|
float3 L = 2 * dot( V, H ) * H - V;
|
|
|
|
float NoV = saturate( dot( N, V ) );
|
|
float NoL = saturate( dot( N, L ) );
|
|
float NoH = saturate( dot( N, H ) );
|
|
float VoH = saturate( dot( V, H ) );
|
|
|
|
if( NoL > 0 )
|
|
{
|
|
float3 SampleColor = AmbientCubemap.SampleLevel( AmbientCubemapSampler, L, 0 ).rgb;
|
|
|
|
float Vis = Vis_Smith( Roughness, NoV, NoL );
|
|
float Fc = pow( 1 - VoH, 5 );
|
|
float3 F = (1 - Fc) * SpecularColor + Fc;
|
|
|
|
// Incident light = SampleColor * NoL
|
|
// Microfacet specular = D*G*F / (4*NoL*NoV) = D*Vis*F
|
|
// pdf = D * NoH / (4 * VoH)
|
|
SpecularLighting += SampleColor * F * ( NoL * Vis * (4 * VoH / NoH) );
|
|
}
|
|
}
|
|
|
|
return SpecularLighting / NumSamples;
|
|
}
|
|
|
|
float3 StandardShading( FGBufferData GBuffer, float Roughness, float3 L, float3 V, half3 N )
|
|
{
|
|
float3 H = normalize(V + L);
|
|
float NoL = saturate( dot(N, L) );
|
|
float NoV = saturate( dot(N, V) );
|
|
float NoH = saturate( dot(N, H) );
|
|
float VoH = saturate( dot(V, H) );
|
|
|
|
// Generalized microfacet specular
|
|
float D = D_GGX( Roughness, NoH );
|
|
float Vis = Vis_Schlick( Roughness, NoV, NoL );
|
|
float3 F = F_Schlick( GBuffer.SpecularColor, VoH );
|
|
|
|
//float k = Square( Roughness ) * 0.25;
|
|
//float Vis = 0.25 / Square( VoH * (1 - k) + k );
|
|
//float Vis = 0.25 / ( VoH * VoH * (1 - k*k) + k*k );
|
|
//float Vis = Vis_Smith( Roughness, NoV, NoL );
|
|
|
|
return Diffuse_Lambert( GBuffer.DiffuseColor ) + (D * Vis) * F;
|
|
}
|
|
|
|
float3 ClearCoatShading( FGBufferData GBuffer, float Roughness, float3 L, float3 V, half3 N )
|
|
{
|
|
const float ClearCoat = GBuffer.CustomData.x;
|
|
const float ClearCoatRoughness = GBuffer.CustomData.y;
|
|
const float Film = 1 * ClearCoat;
|
|
const float MetalSpec = 0.9;
|
|
|
|
#if 0
|
|
float3 H = normalize(V + L);
|
|
float NoL = saturate( dot(N, L) );
|
|
float NoV = saturate( dot(N, V) );
|
|
float NoH = saturate( dot(N, H) );
|
|
float VoH = saturate( dot(V, H) );
|
|
|
|
// Generalized microfacet specular
|
|
float D = D_GGX( ClearCoatRoughness, NoH );
|
|
float Vis = Vis_Kelemen( VoH );
|
|
|
|
// F_Schlick
|
|
float F0 = 0.04;
|
|
float Fc = pow( 1 - VoH, 5 );
|
|
float F = Fc + (1 - Fc) * F0;
|
|
F *= ClearCoat;
|
|
|
|
float Fr1 = D * Vis * F;
|
|
|
|
float LayerAttenuation = (1 - F);
|
|
|
|
// Generalized microfacet specular
|
|
float D2 = D_GGX( Roughness, NoH );
|
|
float Vis2 = Vis_Schlick( Roughness, NoV, NoL );
|
|
float3 F2 = saturate( 50.0 * GBuffer.SpecularColor.g ) * Fc + (1 - Fc) * GBuffer.SpecularColor;
|
|
|
|
float3 Fr2 = Diffuse_Lambert( GBuffer.DiffuseColor ) + (D2 * Vis2) * F2;
|
|
|
|
return Fr1 + Fr2 * LayerAttenuation;
|
|
#else
|
|
float3 H = normalize(V + L);
|
|
float NoL = saturate( dot(N, L) );
|
|
float NoV = saturate( dot(N, V) );
|
|
float NoH = saturate( dot(N, H) );
|
|
float VoH = saturate( dot(V, H) );
|
|
|
|
// Hard coded IOR of 1.5
|
|
|
|
// Generalized microfacet specular
|
|
float D = D_GGX( ClearCoatRoughness, NoH );
|
|
float Vis = Vis_Kelemen( VoH );
|
|
|
|
// F_Schlick
|
|
float F0 = 0.04;
|
|
float Fc = pow( 1 - VoH, 5 );
|
|
float F = Fc + (1 - Fc) * F0;
|
|
|
|
float Fr1 = D * Vis * F;
|
|
|
|
#if 1
|
|
// Refract rays
|
|
float3 L2 = refract( -L, -H, 1 / 1.5 );
|
|
float3 V2 = refract( -V, -H, 1 / 1.5 );
|
|
|
|
// LoH == VoH
|
|
float RefractBlend = sqrt( 4 * VoH*VoH + 5 ) / 3 + 2.0 / 3 * VoH;
|
|
//float3 L2 = RefractBlend * H - L / 1.5;
|
|
//float3 V2 = RefractBlend * H - V / 1.5;
|
|
|
|
float3 H2 = normalize( V2 + L2 );
|
|
float NoL2 = saturate( dot(N, L2) );
|
|
float NoV2 = saturate( dot(N, V2) );
|
|
float NoH2 = saturate( dot(N, H2) );
|
|
float VoH2 = saturate( dot(V2, H2) );
|
|
#else
|
|
// Approximation
|
|
float RefractBlend = (0.22 * VoH + 0.7) * VoH + 0.745; // 2 mad
|
|
// Dot products distribute. No need for L2 and V2.
|
|
float RefractNoH = RefractBlend * NoH; // 1 mul
|
|
float NoL2 = saturate( RefractNoH - (1 / 1.5) * NoL ); // 1 mad
|
|
float NoV2 = saturate( RefractNoH - (1 / 1.5) * NoV ); // 1 mad
|
|
// Should refract H too but unimportant
|
|
#endif
|
|
|
|
NoL2 = max( 0.001, NoL2 );
|
|
NoV2 = max( 0.001, NoV2 );
|
|
|
|
float3 AbsorptionColor = (1 - Film) + GBuffer.BaseColor * ( Film * (1 / MetalSpec) );
|
|
float AbsorptionDist = rcp(NoV2) + rcp(NoL2);
|
|
float3 Absorption = pow( AbsorptionColor, 0.5 * AbsorptionDist );
|
|
|
|
// Approximation
|
|
//float AbsorptionDist = ( NoV2 + NoL2 ) / ( NoV2 * NoL2 );
|
|
//float3 Absorption = AbsorptionColor * ( AbsorptionColor * (AbsorptionDist * 0.5 - 1) + (2 - 0.5 * AbsorptionDist) );
|
|
//float3 Absorption = AbsorptionColor + AbsorptionColor * (AbsorptionColor - 1) * (AbsorptionDist * 0.5 - 1); // use for shared version
|
|
|
|
#if 1
|
|
float F21 = F_Schlick( 0.04, saturate( dot(V2, H) ) );
|
|
|
|
float k = Square( Roughness ) * 0.5;
|
|
float G_SchlickV2 = NoV2 / ( NoV2 * (1 - k) + k );
|
|
float G_SchlickL2 = NoL2 / ( NoL2 * (1 - k) + k );
|
|
float TotalInternalReflection = 1 - F21 * G_SchlickV2 * G_SchlickL2;
|
|
float3 LayerAttenuation = ( (1 - F) * TotalInternalReflection ) * Absorption;
|
|
#else
|
|
// Approximation
|
|
float3 LayerAttenuation = (1 - F) * Absorption;
|
|
#endif
|
|
|
|
// Approximation for IOR == 1.5
|
|
//SpecularColor = ChangeBaseMedium( SpecularColor, 1.5 );
|
|
//SpecularColor = saturate( ( 0.55 * SpecularColor + (0.45 * 1.08) ) * SpecularColor - (0.45 * 0.08) );
|
|
// Treat SpecularColor as relative to IOR. Artist compensates.
|
|
|
|
// Generalized microfacet specular
|
|
#if 1
|
|
float D2 = D_GGX( Roughness, NoH2 );
|
|
float Vis2 = Vis_Schlick( Roughness, NoV2, NoL2 );
|
|
float3 F2 = F_Schlick( MetalSpec, VoH2 );
|
|
#else
|
|
// Approximation
|
|
float D2 = D_GGX( Roughness, NoH );
|
|
float Vis2 = Vis_Schlick( Roughness, NoV2, NoL2 );
|
|
float3 F2 = F_Schlick( GBuffer.SpecularColor, VoH );
|
|
#endif
|
|
|
|
float3 Fr2 = Diffuse_Lambert( GBuffer.DiffuseColor ) + (D2 * Vis2) * F2;
|
|
|
|
return Fr1 + Fr2 * LayerAttenuation;
|
|
#endif
|
|
}
|
|
|
|
|
|
float3 ImageBasedLightingMIS( FGBufferData GBuffer, float3 V, float3 N, uint2 Random )
|
|
{
|
|
float3 Lighting = 0;
|
|
|
|
float Roughness1 = GBuffer.Roughness;
|
|
float Roughness2 = 0.1;
|
|
|
|
uint NumSamples[] =
|
|
{
|
|
16,
|
|
16,
|
|
16,
|
|
};
|
|
|
|
UNROLL
|
|
for( uint Set = 0; Set < 3; Set++ )
|
|
{
|
|
LOOP
|
|
for( uint i = 0; i < NumSamples[ Set ]; i++ )
|
|
{
|
|
float2 E = Hammersley( i, NumSamples[ Set ], Random );
|
|
|
|
float3 L, H;
|
|
if( Set == 0 )
|
|
{
|
|
L = TangentToWorld( CosineSampleHemisphere( E ).xyz, N );
|
|
H = normalize(V + L);
|
|
}
|
|
else if( Set == 1 )
|
|
{
|
|
H = TangentToWorld( ImportanceSampleGGX( E, Roughness1 ).xyz, N );
|
|
L = 2 * dot( V, H ) * H - V;
|
|
}
|
|
else
|
|
{
|
|
H = TangentToWorld( ImportanceSampleGGX( E, Roughness2 ).xyz, N );
|
|
L = 2 * dot( V, H ) * H - V;
|
|
}
|
|
|
|
float NoL = saturate( dot(N, L) );
|
|
float NoH = saturate( dot(N, H) );
|
|
float VoH = saturate( dot(V, H) );
|
|
|
|
if( NoL > 0 && VoH > 0 )
|
|
{
|
|
float3 SampleColor = AmbientCubemap.SampleLevel( AmbientCubemapSampler, L, 0 ).rgb;
|
|
|
|
float PDF[] =
|
|
{
|
|
NoL / PI,
|
|
D_GGX( Roughness1, NoH ) * NoH / (4 * VoH),
|
|
D_GGX( Roughness2, NoH ) * NoH / (4 * VoH),
|
|
};
|
|
|
|
// MIS balance heuristic
|
|
float InvWeight = 0;
|
|
UNROLL for( uint j = 0; j < 3; j++ )
|
|
{
|
|
InvWeight += PDF[j] * NumSamples[j];
|
|
}
|
|
float Weight = rcp( InvWeight );
|
|
|
|
float3 Shading = 0;
|
|
BRANCH if( GBuffer.ShadingModelID == SHADINGMODELID_CLEAR_COAT )
|
|
{
|
|
Shading = ClearCoatShading( GBuffer, GBuffer.Roughness, L, V, N );
|
|
}
|
|
else
|
|
{
|
|
Shading = StandardShading( GBuffer, GBuffer.Roughness, L, V, N );
|
|
}
|
|
|
|
Lighting += SampleColor * Shading * ( NoL * Weight );
|
|
}
|
|
}
|
|
}
|
|
|
|
return Lighting;
|
|
}
|
|
|
|
float3 FilterEnvMap( uint2 Random, float Roughness, float3 N, float3 V )
|
|
{
|
|
float3 FilteredColor = 0;
|
|
float Weight = 0;
|
|
|
|
const uint NumSamples = 64;
|
|
for( uint i = 0; i < NumSamples; i++ )
|
|
{
|
|
float2 E = Hammersley( i, NumSamples, Random );
|
|
float3 H = TangentToWorld( ImportanceSampleGGX( E, Roughness ).xyz, N );
|
|
float3 L = 2 * dot( V, H ) * H - V;
|
|
|
|
float NoL = saturate( dot( N, L ) );
|
|
if( NoL > 0 )
|
|
{
|
|
FilteredColor += AmbientCubemap.SampleLevel( AmbientCubemapSampler, L, 0 ).rgb * NoL;
|
|
Weight += NoL;
|
|
}
|
|
}
|
|
|
|
return FilteredColor / max( Weight, 0.001 );
|
|
}
|
|
|
|
float3 PrefilterEnvMap( uint2 Random, float Roughness, float3 R )
|
|
{
|
|
float3 FilteredColor = 0;
|
|
float Weight = 0;
|
|
|
|
const uint NumSamples = 64;
|
|
for( uint i = 0; i < NumSamples; i++ )
|
|
{
|
|
float2 E = Hammersley( i, NumSamples, Random );
|
|
float3 H = TangentToWorld( ImportanceSampleGGX( E, Roughness ).xyz, R );
|
|
float3 L = 2 * dot( R, H ) * H - R;
|
|
|
|
float NoL = saturate( dot( R, L ) );
|
|
if( NoL > 0 )
|
|
{
|
|
FilteredColor += AmbientCubemap.SampleLevel( AmbientCubemapSampler, L, 0 ).rgb * NoL;
|
|
Weight += NoL;
|
|
}
|
|
}
|
|
|
|
return FilteredColor / max( Weight, 0.001 );
|
|
}
|
|
|
|
float2 IntegrateBRDF( uint2 Random, float Roughness, float NoV )
|
|
{
|
|
float3 V;
|
|
V.x = sqrt( 1.0f - NoV * NoV ); // sin
|
|
V.y = 0;
|
|
V.z = NoV; // cos
|
|
|
|
float A = 0;
|
|
float B = 0;
|
|
|
|
const uint NumSamples = 64;
|
|
for( uint i = 0; i < NumSamples; i++ )
|
|
{
|
|
float2 E = Hammersley( i, NumSamples, Random );
|
|
float3 H = ImportanceSampleGGX( E, Roughness ).xyz;
|
|
float3 L = 2 * dot( V, H ) * H - V;
|
|
|
|
float NoL = saturate( L.z );
|
|
float NoH = saturate( H.z );
|
|
float VoH = saturate( dot( V, H ) );
|
|
|
|
if( NoL > 0 )
|
|
{
|
|
float Vis = Vis_Schlick( Roughness, NoV, NoL );
|
|
|
|
// Incident light = NoL
|
|
// pdf = D * NoH / (4 * VoH)
|
|
// NoL * Vis / pdf
|
|
float NoL_Vis_PDF = NoL * Vis * (4 * VoH / NoH);
|
|
|
|
float Fc = pow( 1 - VoH, 5 );
|
|
A += (1 - Fc) * NoL_Vis_PDF;
|
|
B += Fc * NoL_Vis_PDF;
|
|
}
|
|
}
|
|
|
|
return float2( A, B ) / NumSamples;
|
|
}
|
|
|
|
float3 ApproximateSpecularIBL( uint2 Random, float3 SpecularColor, float Roughness, float3 N, float3 V )
|
|
{
|
|
// Function replaced with prefiltered environment map sample
|
|
float3 R = 2 * dot( V, N ) * N - V;
|
|
float3 PrefilteredColor = PrefilterEnvMap( Random, Roughness, R );
|
|
//float3 PrefilteredColor = FilterEnvMap( Random, Roughness, N, V );
|
|
|
|
// Function replaced with 2D texture sample
|
|
float NoV = saturate( dot( N, V ) );
|
|
float2 AB = IntegrateBRDF( Random, Roughness, NoV );
|
|
|
|
return PrefilteredColor * ( SpecularColor * AB.x + AB.y );
|
|
}
|
|
|
|
void MainPS(in float4 UVAndScreenPos : TEXCOORD0, out float4 OutColor : SV_Target0)
|
|
{
|
|
float2 UV = UVAndScreenPos.xy;
|
|
|
|
FScreenSpaceData ScreenSpaceData = GetScreenSpaceData(UV);
|
|
FGBufferData GBuffer = ScreenSpaceData.GBuffer;
|
|
float AbsoluteDiffuseMip = AmbientCubemapMipAdjust.z;
|
|
|
|
// screen position in [-1, 1] screen space
|
|
float2 ScreenSpacePos = UVAndScreenPos.zw;
|
|
int2 PixelPos = int2(UVAndScreenPos.zw * ScreenPosToPixel.xy + ScreenPosToPixel.zw + 0.5f);
|
|
|
|
float3 ScreenVector = normalize(mul(float4(ScreenSpacePos, 1, 0), View.ScreenToWorld).xyz);
|
|
float NoV = saturate( dot( GBuffer.WorldNormal, -ScreenVector ) );
|
|
float3 R = reflect( ScreenVector, GBuffer.WorldNormal );
|
|
|
|
uint2 Random = ScrambleTEA( PixelPos );
|
|
Random.x ^= View.Random;
|
|
Random.y ^= View.Random;
|
|
|
|
float3 NonSpecularContribution = 0;
|
|
float3 SpecularContribution = 0;
|
|
|
|
float3 DiffuseLookup = TextureCubeSampleLevel(AmbientCubemap, AmbientCubemapSampler, GBuffer.WorldNormal, AbsoluteDiffuseMip).rgb;
|
|
|
|
// Diffuse
|
|
{
|
|
// we want to access the mip with the preconvolved diffuse lighting (coneangle=90 degree)
|
|
NonSpecularContribution += GBuffer.DiffuseColor * DiffuseLookup;
|
|
}
|
|
|
|
// Specular
|
|
{
|
|
float Mip = ComputeCubemapMipFromRoughness( GBuffer.Roughness, AmbientCubemapMipAdjust.w );
|
|
float3 SampleColor = TextureCubeSampleLevel( AmbientCubemap, AmbientCubemapSampler, R, Mip ).rgb;
|
|
|
|
BRANCH if( GBuffer.ShadingModelID == SHADINGMODELID_CLEAR_COAT )
|
|
{
|
|
float ClearCoat = GBuffer.CustomData.x;
|
|
float2 AB = PreIntegratedGF.SampleLevel( PreIntegratedGFSampler, float2( NoV, GBuffer.Roughness ), 0 ).rg;
|
|
SpecularContribution += SampleColor * GBuffer.SpecularColor * AB.x + AB.y * (1 - ClearCoat);
|
|
}
|
|
else
|
|
{
|
|
SpecularContribution += SampleColor * EnvBRDF( GBuffer.SpecularColor, GBuffer.Roughness, NoV );
|
|
}
|
|
}
|
|
|
|
BRANCH if( GBuffer.ShadingModelID == SHADINGMODELID_CLEAR_COAT )
|
|
{
|
|
const float ClearCoat = GBuffer.CustomData.x;
|
|
const float ClearCoatRoughness = GBuffer.CustomData.y;
|
|
|
|
float Mip = ComputeCubemapMipFromRoughness( ClearCoatRoughness, AmbientCubemapMipAdjust.w );
|
|
float3 SampleColor = TextureCubeSampleLevel( AmbientCubemap, AmbientCubemapSampler, R, Mip ).rgb;
|
|
|
|
// F_Schlick
|
|
float F0 = 0.04;
|
|
float Fc = pow( 1 - NoV, 5 );
|
|
float F = Fc + (1 - Fc) * F0;
|
|
F *= ClearCoat;
|
|
|
|
float LayerAttenuation = (1 - F);
|
|
|
|
NonSpecularContribution *= LayerAttenuation;
|
|
SpecularContribution *= LayerAttenuation;
|
|
SpecularContribution += SampleColor * F;
|
|
}
|
|
|
|
#if IMPORTANCE_SAMPLE
|
|
if( GBuffer.ShadingModelID > 0 )
|
|
{
|
|
NonSpecularContribution = 0;
|
|
SpecularContribution = ImageBasedLightingMIS( GBuffer, -ScreenVector, GBuffer.WorldNormal, Random );
|
|
}
|
|
#endif
|
|
|
|
// apply darkening from ambient occlusion (does not use PostprocessInput1 to set white texture if SSAO is off)
|
|
float AmbientOcclusion = GBuffer.GBufferAO * ScreenSpaceData.AmbientOcclusion;
|
|
|
|
// Subsurface
|
|
BRANCH if(GBuffer.ShadingModelID == SHADINGMODELID_SUBSURFACE || GBuffer.ShadingModelID == SHADINGMODELID_PREINTEGRATED_SKIN)
|
|
{
|
|
// some view dependent and some non view dependent (hard coded)
|
|
float DependentSplit = 0.5f;
|
|
|
|
float3 SubsurfaceColor = ExtractSubsurfaceColor(GBuffer);
|
|
|
|
// view independent (shared lookup for diffuse for better performance
|
|
NonSpecularContribution += DiffuseLookup * SubsurfaceColor * (DependentSplit);
|
|
// view dependent (blurriness is hard coded)
|
|
SpecularContribution += TextureCubeSampleLevel(AmbientCubemap, AmbientCubemapSampler, ScreenVector, AbsoluteDiffuseMip - 2.5f).rgb * SubsurfaceColor * (AmbientOcclusion * (1.0f - DependentSplit));
|
|
}
|
|
|
|
FLightAccumulator LightAccumulator = (FLightAccumulator)0;
|
|
|
|
LightAccumulator_Add(LightAccumulator, NonSpecularContribution, SpecularContribution, AmbientCubemapColor.rgb);
|
|
|
|
OutColor = LightAccumulator_GetResult(LightAccumulator);
|
|
|
|
OutColor *= AmbientOcclusion;
|
|
}
|
|
|