Files
UnrealEngineUWP/Engine/Shaders/ShadingModels.usf
Marc Audy 6183efcc36 Integrate Engine/Config, Engine/Plugins, Engine/Programs, Engine/Shaders and remaining Engine/Source from UE4-Orion to UE4 at CL# 2716634
#lockdown Ben.Marsh
#platformnotify Josh.Adams

[CL 2718314 by Marc Audy in Main branch]
2015-10-06 16:47:09 -04:00

683 lines
20 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "DeferredShadingCommon.usf"
#include "BRDF.usf"
#if 0
void StandardShadingShared( float3 DiffuseColor, float3 SpecularColor, float Roughness, float3 V, half3 N )
{
//float NoV = saturate( dot(N, V) );
float NoV = abs( dot(N, V) ) + 1e-5;
// Diffuse_Lambert
Shared.DiffuseMul = DiffuseColor * (1.0 / PI);
// D_GGX, Vis_SmithJointApprox
float m = Roughness * Roughness;
Shared.m2 = m * m;
Shared.SpecularMul = (0.5 / PI) * Shared.m2;
Shared.VisMad = float2( 2 * NoV * ( 1 - m ) + m, NoV * m );
// F_Schlick
Shared.SpecularMul *= saturate( 50.0 * SpecularColor.g );
}
void StandardShadingPerLight( Shared, float3 L, float3 V, half3 N )
{
float3 H = normalize(V + L); // 3 add, 2 mad, 4 mul, 1 rsqrt
float NoL = saturate( dot(N, L) ); // 2 mad, 1 mul
float NoH = saturate( dot(N, H) ); // 2 mad, 1 mul
float VoH = saturate( dot(V, H) ); // 2 mad, 1 mul
// D_GGX, Vis_SmithJointApprox
float d = ( NoH * Shared.m2 - NoH ) * NoH + 1; // 2 mad
float v = NoL * Shared.VisMad.x + Shared.VisMad.y; // 1 mad
float D_Vis = Shared.SpecularMul * rcp( d * d * v ); // 3 mul, 1 rcp
// F_Schlick
float Fc = pow( 1 - VoH, 5 ); // 1 sub, 3 mul
float3 F = Fc + (1 - Fc) * SpecularColor; // 1 sub, 3 mad
return Shared.DiffuseMul + D_Vis * F; // 3 mad
}
#endif
// @param DiffSpecMask .r: diffuse, .g:specular e.g. float2(1,1) for both, float2(1,0) for diffuse only
float3 StandardShading( FGBufferData GBuffer, float3 LobeRoughness, float3 LobeEnergy, float3 L, float3 V, half3 N, float2 DiffSpecMask )
{
float3 H = normalize(V + L);
float NoL = saturate( dot(N, L) );
//float NoV = saturate( dot(N, V) );
float NoV = max( dot(N, V), 1e-5 );
float NoH = saturate( dot(N, H) );
float VoH = saturate( dot(V, H) );
// Generalized microfacet specular
float D = D_GGX( LobeRoughness[1], NoH ) * LobeEnergy[1];
float Vis = Vis_SmithJointApprox( LobeRoughness[1], NoV, NoL );
float3 F = F_Schlick( GBuffer.SpecularColor, VoH );
float3 Diffuse = Diffuse_Lambert( GBuffer.DiffuseColor );
//float3 Diffuse = Diffuse_Burley( GBuffer.DiffuseColor, LobeRoughness[1], NoV, NoL, VoH );
//float3 Diffuse = Diffuse_OrenNayar( GBuffer.DiffuseColor, LobeRoughness[1], NoV, NoL, VoH );
return Diffuse * (LobeEnergy[2] * DiffSpecMask.r) + (D * Vis * DiffSpecMask.g) * F;
}
float3 SimpleShading( FGBufferData GBuffer, float Roughness, float3 L, float3 V, half3 N )
{
float3 H = normalize(V + L);
float NoH = saturate( dot(N, H) );
// Generalized microfacet specular
float D = D_GGX( Roughness, NoH );
float Vis = Vis_Implicit();
float3 F = F_None( GBuffer.SpecularColor );
return Diffuse_Lambert( GBuffer.DiffuseColor ) + (D * Vis) * F;
}
float3 ClearCoatShading( FGBufferData GBuffer, float3 LobeRoughness, float3 LobeEnergy, float3 L, float3 V, half3 N )
{
#if USE_CLEARCOAT
const float ClearCoat = GBuffer.CustomData.x;
const float ClearCoatRoughness = GBuffer.CustomData.y;
const float Film = 1 * ClearCoat;
const float MetalSpec = 0.9;
#if 1
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( LobeRoughness[0], NoH ) * LobeEnergy[0];
float Vis = Vis_Kelemen( VoH );
// F_Schlick
float F0 = 0.04;
float Fc = Pow5( 1 - VoH );
float F = Fc + (1 - Fc) * F0;
F *= ClearCoat;
float Fr1 = D * Vis * F;
float LayerAttenuation = (1 - F);
// Generalized microfacet specular
float D2 = D_GGX( LobeRoughness[1], NoH ) * LobeEnergy[1];
float Vis2 = Vis_Schlick( LobeRoughness[1], NoV, NoL );
//float3 F2 = F_Schlick( GBuffer.SpecularColor, VoH );
float3 F2 = saturate( 50.0 * GBuffer.SpecularColor.g ) * Fc + (1 - Fc) * GBuffer.SpecularColor;
//float3 Fr2 = Diffuse_Burley( GBuffer.DiffuseColor, LobeRoughness[1], NoV, NoL, VoH ) * LobeEnergy[2] + (D2 * Vis2) * F2;
float3 Fr2 = Diffuse_Lambert( GBuffer.DiffuseColor ) * LobeEnergy[2] + (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 ) * LobeEnergy[0];
float Vis = Vis_Kelemen( VoH );
// F_Schlick
float F0 = 0.04;
float Fc = Pow5( 1 - VoH );
float F = Fc + (1 - Fc) * F0;
float Fr1 = D * Vis * F;
// 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;
//float NoL2 = saturate( dot(N, L2) );
//float NoV2 = saturate( dot(N, V2) );
// 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
NoL2 = max( 0.001, NoL2 );
NoV2 = max( 0.001, NoV2 );
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
//float F21 = Fresnel( 1 / 1.5, saturate( dot(V2, H) ) );
//float TotalInternalReflection = 1 - F21 * G_Schlick( Roughness, NoV2, NoL2 );
//float3 LayerAttenuation = ( (1 - F12) * TotalInternalReflection ) * Absorption;
// Approximation
float3 LayerAttenuation = (1 - F) * Absorption;
// 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
float D2 = D_GGX( Roughness, NoH ) * LobeEnergy[2];
float Vis2 = Vis_Schlick( Roughness, NoV2, NoL2 );
float3 F2 = F_Schlick( GBuffer.SpecularColor, VoH );
float3 Fr2 = Diffuse_Lambert( GBuffer.DiffuseColor ) * LobeEnergy[2] + (D2 * Vis2) * F2;
return Fr1 + Fr2 * LayerAttenuation;
#endif
#else //USE_CLEARCOAT
return float3(0.0f, 0.0f, 0.0f);
#endif //USE_CLEARCOAT
}
float Hair_g( float B, float Theta )
{
return exp( -0.5 * Pow2( Theta ) / (B*B) ) / ( sqrt(2*PI) * B );
}
float Hair_F( float CosTheta )
{
const float n = 1.55;
const float F0 = Pow2( (1 - n) / (1 + n) );
return F0 + (1 - F0) * Pow5( 1 - CosTheta );
}
#define HAIR_REFERENCE 0
#if HAIR_REFERENCE
struct FHairTemp
{
float SinThetaL;
float SinThetaV;
float CosThetaD;
float CosThetaT;
float CosPhi;
float CosHalfPhi;
float VoL;
float n_prime;
};
// Modified Bessel function
float I0( float x )
{
x = abs(x);
float a;
if( x < 3.75 )
{
float t = x / 3.75;
float t2 = t*t;
a = + 0.0045813;
a = a * t2 + 0.0360768;
a = a * t2 + 0.2659732;
a = a * t2 + 1.2067492;
a = a * t2 + 3.0899424;
a = a * t2 + 3.5156229;
a = a * t2 + 1.0;
}
else
{
float t = 3.75 / x;
a = + 0.00392377;
a = a * t - 0.01647633;
a = a * t + 0.02635537;
a = a * t - 0.02057706;
a = a * t + 0.00916281;
a = a * t - 0.00157565;
a = a * t + 0.00225319;
a = a * t + 0.01328592;
a = a * t + 0.39894228;
a *= exp(x) * rsqrt(x);
}
return a;
}
float LongitudinalScattering( float B, float SinThetaL, float SinThetaV )
{
float v = B*B;
float CosThetaL2 = 1 - SinThetaL * SinThetaL;
float CosThetaV2 = 1 - SinThetaV * SinThetaV;
float Mp = 0;
if( v < 0.1 )
{
float a = sqrt(CosThetaL2 * CosThetaV2) / v;
float b = -SinThetaL * SinThetaV / v;
float logI0a = a > 12 ? a + 0.5 * ( -log(2*PI) + log(1/a) + 0.125/a ) : log( I0(a) );
Mp = exp( logI0a + b - rcp(v) + 0.6931 + log(0.5 / v) );
}
else
{
Mp = rcp( exp(2/v) * v - v ) * exp( ( 1 - SinThetaL * SinThetaV ) / v ) * I0( sqrt(CosThetaL2 * CosThetaV2) / v );
}
return Mp;
}
float GaussianDetector( float Bp, float Phi )
{
float Dp = 0;
for( int k = -4; k <= 4; k++ )
{
// TODO use symmetry and detect for both Phi and -Phi
Dp += Hair_g( Bp, Phi - (2*PI) * k );
}
return Dp;
}
float3 Attenuation( uint p, float h, float3 Color, FHairTemp HairTemp )
{
float3 A;
if( p == 0 )
{
//A = F( cos( 0.5 * acos( HairTemp.VoL ) ) );
A = Hair_F( sqrt( 0.5 + 0.5 * HairTemp.VoL ) );
}
else
{
// ua is absorption
// ua = pe*Sigma_ae + pp*Sigma_ap
float3 Sigma_ae = { 0.419, 0.697, 1.37 };
float3 Sigma_ap = { 0.187, 0.4, 1.05 };
//float3 ua = 0.25 * Sigma_ae + 0.25 * Sigma_ap;
float3 ua = -0.25 * log( Color );
float3 ua_prime = ua / HairTemp.CosThetaT;
//float3 ua_prime = ua / sqrt( 1 - Pow2( HairTemp.CosThetaD ) / 2.4 );
float yi = asin(h);
float yt = asin(h / HairTemp.n_prime);
float f = Hair_F( HairTemp.CosThetaD * sqrt( 1 - h*h ) ); // (14)
//float3 T = exp( -2 * ua_prime * ( 1 + cos(2*yt) ) );
float3 T = exp( -2 * ua_prime * cos(yt) );
if( p == 1 )
A = Pow2(1 - f) * T; // (13)
else
A = Pow2(1 - f) * f * T*T; // (13)
}
return A;
}
float Omega( uint p, float h, FHairTemp HairTemp )
{
float yi = asin(h);
float yt = asin(h / HairTemp.n_prime);
return 2*p*yt - 2*yi + p*PI;
}
float3 AzimuthalScattering( uint p, float Bp, float3 Color, FHairTemp HairTemp, uint2 Random )
{
float Phi = acos( HairTemp.CosPhi );
// Np = 0.5 * Integral_-1^1( A(p,h) * Dp( Phi - Omega(p,h) ) * dh )
#if 0
#if 0
// Gauss–Legendre quadrature order 5
uint Num = 2;
float2 w0_x0 = { 0.56888888, 0 };
float2 wi_xi[] =
{
{ 0.47862867, 0.53846931 },
{ 0.23692688, 0.90617984 },
};
#elif 0
// Gauss–Legendre quadrature order 7
const uint Num = 3;
float2 w0_x0 = { 0.41795918, 0 };
float2 wi_xi[] =
{
{ 0.38183005, 0.40584515 },
{ 0.27970539, 0.74153118 },
{ 0.12948496, 0.94910791 },
};
#elif 0
// Gauss–Legendre quadrature order 11
const uint Num = 5;
float2 w0_x0 = { 0.27292508, 0 };
float2 wi_xi[] =
{
{ 0.26280454, 0.26954315 },
{ 0.23319376, 0.51909612 },
{ 0.18629021, 0.73015200 },
{ 0.12558036, 0.88706259 },
{ 0.05566856, 0.97822865 },
};
#else
// Gauss–Legendre quadrature order 35
const uint Num = 17;
float2 w0_x0 = { 0.08848679, 0.00000000 };
float2 wi_xi[] =
{
{ 0.08814053, 0.08837134 },
{ 0.08710444, 0.17605106 },
{ 0.08538665, 0.26235294 },
{ 0.08300059, 0.34660155 },
{ 0.07996494, 0.42813754 },
{ 0.07630345, 0.50632277 },
{ 0.07204479, 0.58054534 },
{ 0.06722228, 0.65022436 },
{ 0.06187367, 0.71481450 },
{ 0.05604081, 0.77381025 },
{ 0.04976937, 0.82674989 },
{ 0.04310842, 0.87321912 },
{ 0.03611011, 0.91285426 },
{ 0.02882926, 0.94534514 },
{ 0.02132297, 0.97043761 },
{ 0.01365082, 0.98793576 },
{ 0.00588343, 0.99770656 },
};
#endif
float w = w0_x0.x;
float h = w0_x0.y;
float3 Np = w * Attenuation( p, h, HairTemp ) * GaussianDetector( Bp, Phi - Omega( p, h, HairTemp ) );
for( uint i = 0; i < Num; i++ )
{
w = wi_xi[i].x;
h = wi_xi[i].y;
Np += w * Attenuation( p, h, HairTemp ) * GaussianDetector( Bp, Phi - Omega( p, h, HairTemp ) );
Np += w * Attenuation( p,-h, HairTemp ) * GaussianDetector( Bp, Phi - Omega( p,-h, HairTemp ) );
}
#else
float Offset = float( Random.x & 0xffff ) / (1<<16);
uint Num = 16;
float3 Np = 0;
for( uint i = 0; i < Num; i++ )
{
float h = ( (float)( i + Offset ) / Num ) * 2 - 1;
Np += Attenuation( p, h, Color, HairTemp ) * GaussianDetector( Bp, Phi - Omega( p, h, HairTemp ) );
}
Np *= 2.0 / Num;
#endif
return 0.5 * Np;
}
// [d'Eon et al. 2011, "An Energy-Conserving Hair Reflectance Model"]
// [d'Eon et al. 2014, "A Fiber Scattering Model with Non-Separable Lobes"]
float3 HairShadingRef( FGBufferData GBuffer, float3 L, float3 V, half3 N, uint2 Random )
{
float n = 1.55;
FHairTemp HairTemp;
// N is the vector parallel to hair pointing toward root
HairTemp.VoL = dot(V,L);
HairTemp.SinThetaL = dot(N,L);
HairTemp.SinThetaV = dot(N,V);
// SinThetaT = 1/n * SinThetaL
HairTemp.CosThetaT = sqrt( 1 - Pow2( (1/n) * HairTemp.SinThetaL ) );
HairTemp.CosThetaD = cos( 0.5 * abs( asin( HairTemp.SinThetaV ) - asin( HairTemp.SinThetaL ) ) );
float3 Lp = L - HairTemp.SinThetaL * N;
float3 Vp = V - HairTemp.SinThetaV * N;
HairTemp.CosPhi = dot(Lp,Vp) * rsqrt( dot(Lp,Lp) * dot(Vp,Vp) );
HairTemp.CosHalfPhi = sqrt( 0.5 + 0.5 * HairTemp.CosPhi );
HairTemp.n_prime = sqrt( n*n - 1 + Pow2( HairTemp.CosThetaD ) ) / HairTemp.CosThetaD;
float Alpha[] =
{
-0.05,
0.05 * 0.5,
0.05 * 1.5,
};
float B[] =
{
Pow2( GBuffer.Roughness ),
Pow2( GBuffer.Roughness ) / 2,
Pow2( GBuffer.Roughness ) * 2,
};
float3 S = 0;
UNROLL for( uint p = 0; p < 3; p++ )
{
float SinThetaV = HairTemp.SinThetaV;
float Bp = B[p];
if( p == 0 )
{
Bp *= sqrt(2.0) * HairTemp.CosHalfPhi;
float sa, ca;
sincos( Alpha[p], sa, ca );
SinThetaV -= 2*sa * ( HairTemp.CosHalfPhi * ca * sqrt( 1 - SinThetaV * SinThetaV ) + sa * SinThetaV );
}
else
{
SinThetaV = sin( asin(SinThetaV) - Alpha[p] );
}
float Mp = LongitudinalScattering( Bp, HairTemp.SinThetaL, SinThetaV );
float3 Np = AzimuthalScattering( p, B[p], GBuffer.BaseColor, HairTemp, Random );
float3 Sp = Mp * Np;
S += Sp;
}
return S;
}
#endif
// Approximation to HairShadingRef using concepts from the following papers:
// [Marschner et al. 2003, "Light Scattering from Human Hair Fibers"]
// [Pekelis et al. 2015, "A Data-Driven Light Scattering Model for Hair"]
float3 HairShading( FGBufferData GBuffer, float3 L, float3 V, half3 N, float Shadow, uint2 Random )
{
//const float3 DiffuseN = OctahedronToUnitVector( GBuffer.CustomData.xy * 2 - 1 );
const float Backlit = GBuffer.CustomData.z;
#if HAIR_REFERENCE
float3 S = HairShadingRef( GBuffer, L, V, N, Random );
//float3 S = HairShadingMarschner( GBuffer, L, V, N );
#else
// N is the vector parallel to hair pointing toward root
const float VoL = dot(V,L);
const float SinThetaL = dot(N,L);
const float SinThetaV = dot(N,V);
const float CosThetaD = cos( 0.5 * abs( asin( SinThetaV ) - asin( SinThetaL ) ) );
const float3 Lp = L - SinThetaL * N;
const float3 Vp = V - SinThetaV * N;
const float CosPhi = dot(Lp,Vp) * rsqrt( dot(Lp,Lp) * dot(Vp,Vp) + 1e-4 );
const float CosHalfPhi = sqrt( saturate( 0.5 + 0.5 * CosPhi ) );
const float Phi = acos( CosPhi );
float n = 1.55;
//float n_prime = sqrt( n*n - 1 + Pow2( CosThetaD ) ) / CosThetaD;
float n_prime = 1.2 / CosThetaD + 0.35 * CosThetaD;
float Shift = -0.05;
float Alpha[] =
{
Shift,
-Shift * 0.5,
-Shift * 1.5,
};
float B[] =
{
Pow2( GBuffer.Roughness ),
Pow2( GBuffer.Roughness ) / 2,
Pow2( GBuffer.Roughness ) * 2,
};
float3 S = 0;
// R
{
const float sa = sin( Alpha[0] );
const float ca = cos( Alpha[0] );
float Shift = (2*sa*ca) * CosHalfPhi * sqrt( 1 - SinThetaV * SinThetaV ) + (2*sa*sa) * SinThetaV;
float Mp = Hair_g( B[0] * sqrt(2.0) * CosHalfPhi, SinThetaL + SinThetaV - Shift );
float Np = 0.25 * CosHalfPhi;
float Fp = Hair_F( sqrt( 0.5 + 0.5 * VoL ) );
S += Mp * Np * Fp * ( GBuffer.Specular * 2 ) * lerp( 1, Backlit, saturate(-VoL) );
}
// TT
{
float Mp = Hair_g( B[1], SinThetaL + SinThetaV - Alpha[1] );
float a = 1 / n_prime;
float h = CosHalfPhi * rsqrt( 1 + a*a - 2*a * sqrt( 0.5 - 0.5 * CosPhi ) );
//float h = CosHalfPhi * ( ( 1 - Pow2( CosHalfPhi ) ) * a + 1 );
//float h = 0.4;
float yi = asin(h);
float yt = asin(h / n_prime);
float f = Hair_F( CosThetaD * sqrt( saturate( 1 - h*h ) ) );
float Fp = Pow2(1 - f);
//float3 Tp = pow( GBuffer.BaseColor, 0.5 * ( 1 + cos(2*yt) ) / CosThetaD );
//float3 Tp = pow( GBuffer.BaseColor, 0.4 / CosThetaD );
float3 Tp = pow( GBuffer.BaseColor, 0.5 * cos(yt) / CosThetaD );
//float t = asin( 1 / n_prime );
//float d = ( sqrt(2) - t ) / ( 1 - t );
//float s = -0.5 * PI * (1 - 1 / n_prime) * log( 2*d - 1 - 2 * sqrt( d * (d - 1) ) );
float s = 0.3;
float Np = exp( (Phi - PI) / s ) / ( s * Pow2( 1 + exp( (Phi - PI) / s ) ) );
S += Mp * Np * Fp * Tp * Backlit;
}
// TRT
{
float Mp = Hair_g( B[2], SinThetaL + SinThetaV - Alpha[2] );
//float h = 0.75;
float f = Hair_F( CosThetaD * 0.5 );
float Fp = Pow2(1 - f) * f;
//float3 Tp = pow( GBuffer.BaseColor, 1.6 / CosThetaD );
float3 Tp = pow( GBuffer.BaseColor, 0.8 / CosThetaD );
//float s = 0.3;
//float Np = exp( Phi / s ) / ( s * Pow2( 1 + exp( Phi / s ) ) );
float Np = (1/PI) * 2.6 * exp( 2 * 2.6 * (CosPhi - 1) );
S += Mp * Np * Fp * Tp;
}
#endif
float3 FakeNormal = normalize( V - N * dot(V,N) );
//N = normalize( DiffuseN + FakeNormal * 2 );
N = FakeNormal;
// Hack approximation for multiple scattering.
float Wrap = 1;
float NoL = saturate( ( dot(N, L) + Wrap ) / Square( 1 + Wrap ) );
float DiffuseScatter = (1 / PI) * NoL * GBuffer.Metallic;
float3 ScatterTint = pow( GBuffer.BaseColor / Luminance( GBuffer.BaseColor ), 1 - Shadow );
S += GBuffer.BaseColor * DiffuseScatter * ScatterTint;
return S;
}
float3 SubsurfaceShadingSubsurface( FGBufferData GBuffer, float3 L, float3 V, half3 N )
{
float3 SubsurfaceColor = ExtractSubsurfaceColor(GBuffer);
float Opacity = GBuffer.CustomData.a;
float3 H = normalize(V + L);
// to get an effect when you see through the material
// hard coded pow constant
float InScatter = pow(saturate(dot(L, -V)), 12) * lerp(3, .1f, Opacity);
// wrap around lighting, /(PI*2) to be energy consistent (hack do get some view dependnt and light dependent effect)
// Opacity of 0 gives no normal dependent lighting, Opacity of 1 gives strong normal contribution
float NormalContribution = saturate(dot(N, H) * Opacity + 1 - Opacity);
float BackScatter = GBuffer.GBufferAO * NormalContribution / (PI * 2);
// lerp to never exceed 1 (energy conserving)
return SubsurfaceColor * lerp(BackScatter, 1, InScatter);
}
float3 SubsurfaceShadingTwoSided( float3 SubsurfaceColor, float3 L, float3 V, half3 N )
{
// http://blog.stevemcauley.com/2011/12/03/energy-conserving-wrapped-diffuse/
float Wrap = 0.5;
float NoL = saturate( ( dot(-N, L) + Wrap ) / Square( 1 + Wrap ) );
// GGX scatter distribution
float VoL = saturate( dot(V, -L) );
float a = 0.6;
float a2 = a * a;
float d = ( VoL * a2 - VoL ) * VoL + 1; // 2 mad
float GGX = (a2 / PI) / (d * d); // 2 mul, 1 rcp
return NoL * GGX * SubsurfaceColor;
}
Texture2D PreIntegratedBRDF;
SamplerState PreIntegratedBRDFSampler;
float3 SubsurfaceShadingPreintegratedSkin( FGBufferData GBuffer, float3 L, float3 V, half3 N )
{
float3 SubsurfaceColor = ExtractSubsurfaceColor(GBuffer);
float Opacity = GBuffer.CustomData.a;
float3 PreintegratedBRDF = Texture2DSampleLevel(PreIntegratedBRDF, PreIntegratedBRDFSampler, float2(saturate(dot(N, L) * .5 + .5), 1 - Opacity), 0).rgb;
return PreintegratedBRDF * SubsurfaceColor;
}
// @param DiffSpecMask .r: diffuse, .g:specular e.g. float2(1,1) for both, float2(1,0) for diffuse only
float3 SurfaceShading( FGBufferData GBuffer, float3 LobeRoughness, float3 LobeEnergy, float3 L, float3 V, half3 N, float2 DiffSpecMask, uint2 Random )
{
switch( GBuffer.ShadingModelID )
{
case SHADINGMODELID_UNLIT:
case SHADINGMODELID_DEFAULT_LIT:
case SHADINGMODELID_SUBSURFACE:
case SHADINGMODELID_PREINTEGRATED_SKIN:
case SHADINGMODELID_SUBSURFACE_PROFILE:
case SHADINGMODELID_TWOSIDED_FOLIAGE:
return StandardShading( GBuffer, LobeRoughness, LobeEnergy, L, V, N, DiffSpecMask);
case SHADINGMODELID_CLEAR_COAT:
// this path does not support DiffSpecMask yet
return ClearCoatShading( GBuffer, LobeRoughness, LobeEnergy, L, V, N );
default:
return 0;
}
}
float3 SubsurfaceShading( FGBufferData GBuffer, float3 L, float3 V, half3 N, float Shadow, uint2 Random )
{
float3 SubsurfaceColor = ExtractSubsurfaceColor(GBuffer);
switch( GBuffer.ShadingModelID )
{
case SHADINGMODELID_SUBSURFACE:
return SubsurfaceShadingSubsurface( GBuffer, L, V, N );
case SHADINGMODELID_PREINTEGRATED_SKIN:
return SubsurfaceShadingPreintegratedSkin( GBuffer, L, V, N );
case SHADINGMODELID_TWOSIDED_FOLIAGE:
return SubsurfaceShadingTwoSided( SubsurfaceColor, L, V, N );
case SHADINGMODELID_HAIR:
return HairShading( GBuffer, L, V, N, Shadow, Random );
default:
return 0;
}
}