Added new PBR roughness estimation by Kennedith98

This commit is contained in:
Robert Beckebans 2024-11-27 16:31:43 +01:00
parent 025627b155
commit 9ba5e92b42
4 changed files with 89 additions and 19 deletions

View file

@ -1704,9 +1704,8 @@ void idRenderBackend::RenderInteractions( const drawSurf_t* surfList, const view
// apply the world-global overbright and the 2x factor for specular
idVec4 diffuseColor = lightColor;
idVec4 specularColor = lightColor * 2.0f; // RB: skip 2x factor for specular PBR formulars
// jmarshall
idVec4 specularColor = lightColor * 2.0f;
if( vLight->lightDef->parms.noSpecular )
{
specularColor.Zero();

View file

@ -102,7 +102,7 @@ float Visibility_SmithGGX( half vdotN, half ldotN, float alpha )
return ( 1.0 / max( V1 * V2, 0.15 ) );
}
// HACK calculate roughness from D3 gloss maps
// RB: HACK calculate roughness from D3 gloss maps
float EstimateLegacyRoughness( float3 specMapSRGB )
{
float Y = dot( LUMINANCE_SRGB.rgb, specMapSRGB );
@ -115,6 +115,65 @@ float EstimateLegacyRoughness( float3 specMapSRGB )
return roughness;
}
// Kennedith98 begin
// takes a gamma-space specular texture
// outputs F0 color and roughness for PBR
void PBRFromSpecmap( float3 specMap, out float3 F0, out float roughness )
{
// desaturate specular
float specLum = max( specMap.r, max( specMap.g, specMap.b ) );
// fresnel base
F0 = _float3( 0.04 );
// fresnel contrast (will tighten low spec and broaden high spec, stops specular looking too flat or shiny)
float contrastMid = 0.214;
float contrastAmount = 2.0;
float contrast = saturate( ( specLum - contrastMid ) / ( 1 - contrastMid ) ); //high spec
contrast += saturate( specLum / contrastMid ) - 1.0; //low spec
contrast = exp2( contrastAmount * contrast );
F0 *= contrast;
// reverse blinn BRDF to perfectly match vanilla specular brightness
// fresnel is affected when specPow is 0, experimentation is desmos showed that happens at F0/4
float linearBrightness = Linear1( 2.0 * specLum );
float specPow = max( 0.0, ( ( 8 * linearBrightness ) / F0.y ) - 2.0 );
F0 *= min( 1.0, linearBrightness / ( F0.y * 0.25 ) );
// specular power to roughness
roughness = sqrt( 2.0 / ( specPow + 2.0 ) );
// RB: do another sqrt because PBR shader squares it
roughness = sqrt( roughness );
}
// takes roughness, unnormalized 0-1 normalmap texture and the distance to the camera
// returns roughness
float ApplySpecularAA( float roughness, float3 bumpMap, float camDist )
{
float normLen = saturate( length( bumpMap.xyz * 2.0 - 1.0 ) );
// roughness to specular power
float specPow = ( 2.0 / ( roughness * roughness ) ) - 2.0;
// toksvig AA to prevent speckles from sharp normal edges
float specAA = 1.0 / ( 1.0 + specPow * ( ( 1.0 / normLen ) - 1.0 ) );
// fade out AA close to the camera to prevent dull highlights close up
float distMax = 100.0;
float disMin = 1.0;
specAA = lerp( specAA, 1.0, smoothstep( disMin, distMax, camDist ) );
//apply AA
specPow *= specAA;
// specular power to roughness
roughness = sqrt( 2.0 / ( specPow + 2.0 ) );
return roughness;
}
// Kennedith98 end
// Environment BRDF approximations
// see s2013_pbs_black_ops_2_notes.pdf
/*

View file

@ -131,6 +131,13 @@ void main( PS_IN fragment, out PS_OUT result )
float3 diffuseColor = baseColor * ( 1.0 - metallic );
float3 specularColor = lerp( dielectricColor, baseColor, metallic );
#elif 1
float3 diffuseColor = diffuseMap;
float3 specularColor;
float roughness;
PBRFromSpecmap( specMapSRGB.rgb, specularColor, roughness );
#else
const float roughness = EstimateLegacyRoughness( specMapSRGB.rgb );
@ -139,8 +146,10 @@ void main( PS_IN fragment, out PS_OUT result )
#endif
// RB: compensate r_lightScale 3 and the division of Pi
// RB FIXME or not: compensate r_lightScale 3 and the division of Pi
//lambert *= 1.3;
// see http://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/
//lambert /= PI;
// rpDiffuseModifier contains light color multiplier
float3 lightColor = sRGBToLinearRGB( lightProj.xyz * lightFalloff.xyz );
@ -149,8 +158,8 @@ void main( PS_IN fragment, out PS_OUT result )
float vdotH = clamp( dot3( viewVector, halfAngleVector ), 0.0, 1.0 );
float ldotH = clamp( dot3( lightVector, halfAngleVector ), 0.0, 1.0 );
// compensate r_lightScale 3 * 2
float3 reflectColor = specularColor * rpSpecularModifier.rgb * 1.0;// * 0.5;
// keep in mind this is r_lightScale 3 * 2
float3 reflectColor = specularColor * rpSpecularModifier.rgb;
// cheap approximation by ARM with only one division
// http://community.arm.com/servlet/JiveServlet/download/96891546-19496/siggraph2015-mmg-renaldas-slides.pdf
@ -162,17 +171,14 @@ void main( PS_IN fragment, out PS_OUT result )
// disney GGX
float D = ( hdotN * hdotN ) * ( rrrr - 1.0 ) + 1.0;
float VFapprox = ( ldotH * ldotH ) * ( roughness + 0.5 );
float3 specularLight = ( rrrr / ( 4.0 * PI * D * D * VFapprox ) ) * ldotN * reflectColor;
//specularLight = float3( 0.0 );
//float3 specularLight = ( rrrr / ( 4.0 * PI * D * D * VFapprox ) ) * ldotN * reflectColor;
float3 specularLight = ( rrrr / ( 4.0 * D * D * VFapprox ) ) * ldotN * reflectColor;
#if 0
result.color = float4( _float3( VFapprox ), 1.0 );
return;
#endif
// see http://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/
//lambert /= PI;
//float3 diffuseColor = mix( diffuseMap, F0, metal ) * rpDiffuseModifier.xyz;
float3 diffuseLight = diffuseColor * lambert * ( rpDiffuseModifier.xyz );

View file

@ -489,6 +489,13 @@ void main( PS_IN fragment, out PS_OUT result )
float3 diffuseColor = baseColor * ( 1.0 - metallic );
float3 specularColor = lerp( dielectricColor, baseColor, metallic );
#elif 1
float3 diffuseColor = diffuseMap;
float3 specularColor;
float roughness;
PBRFromSpecmap( specMapSRGB.rgb, specularColor, roughness );
#else
const float roughness = EstimateLegacyRoughness( specMapSRGB.rgb );
@ -497,8 +504,10 @@ void main( PS_IN fragment, out PS_OUT result )
#endif
// RB: compensate r_lightScale 3 and the division of Pi
// RB FIXME or not: compensate r_lightScale 3 and the division of Pi
//lambert *= 1.3;
// see http://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/
//lambert /= PI;
// rpDiffuseModifier contains light color multiplier
float3 lightColor = sRGBToLinearRGB( lightProj.xyz * lightFalloff.xyz );
@ -507,8 +516,8 @@ void main( PS_IN fragment, out PS_OUT result )
float vdotH = clamp( dot3( viewVector, halfAngleVector ), 0.0, 1.0 );
float ldotH = clamp( dot3( lightVector, halfAngleVector ), 0.0, 1.0 );
// compensate r_lightScale 3 * 2
float3 reflectColor = specularColor * rpSpecularModifier.rgb * 1.0;// * 0.5;
// keep in mind this is r_lightScale 3 * 2
float3 reflectColor = specularColor * rpSpecularModifier.rgb;
// cheap approximation by ARM with only one division
// http://community.arm.com/servlet/JiveServlet/download/96891546-19496/siggraph2015-mmg-renaldas-slides.pdf
@ -520,17 +529,14 @@ void main( PS_IN fragment, out PS_OUT result )
// disney GGX
float D = ( hdotN * hdotN ) * ( rrrr - 1.0 ) + 1.0;
float VFapprox = ( ldotH * ldotH ) * ( roughness + 0.5 );
float3 specularLight = ( rrrr / ( 4.0 * PI * D * D * VFapprox ) ) * ldotN * reflectColor;
//specularLight = float3( 0.0 );
//float3 specularLight = ( rrrr / ( 4.0 * PI * D * D * VFapprox ) ) * ldotN * reflectColor;
float3 specularLight = ( rrrr / ( 4.0 * D * D * VFapprox ) ) * ldotN * reflectColor;
#if 0
result.color = float4( _float3( VFapprox ), 1.0 );
return;
#endif
// see http://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/
//lambert /= PI;
//float3 diffuseColor = mix( diffuseMap, F0, metal ) * rpDiffuseModifier.xyz;
float3 diffuseLight = diffuseColor * lambert * ( rpDiffuseModifier.xyz );