PBR metal workflow texture support for IBL

This commit is contained in:
Robert Beckebans 2016-07-10 23:46:24 +02:00
parent 21bdc82d13
commit 60f330874d
9 changed files with 160 additions and 26 deletions

View file

@ -60,6 +60,11 @@ half3 Fresnel_Schlick( half3 specularColor, half vdotH )
return specularColor + ( 1.0 - specularColor ) * pow( 1.0 - vdotH, 5.0 );
}
half3 Fresnel_Glossy( half3 specularColor, half roughness, half vdotH )
{
return specularColor + ( max( half3( 1.0 - roughness ), specularColor ) - specularColor ) * pow( 1.0 - vdotH, 5.0 );
}
// Visibility term G( l, v, h )
// Very similar to Marmoset Toolbag 2 and gives almost the same results as Smith GGX
float Visibility_Schlick( half vdotN, half ldotN, float alpha )

View file

@ -28,6 +28,7 @@ If you have questions concerning this license or the applicable additional terms
*/
#include "renderprogs/global.inc"
#include "renderprogs/BRDF.inc"
uniform sampler2D samp0 : register(s0); // texture 1 is the per-surface bump map
uniform sampler2D samp1 : register(s1); // texture 2 is the light falloff texture
@ -59,8 +60,7 @@ void main( PS_IN fragment, out PS_OUT result ) {
// half4 lightFalloff = idtex2Dproj( samp1, fragment.texcoord2 );
// half4 lightProj = idtex2Dproj( samp2, fragment.texcoord3 );
half4 YCoCG = tex2D( samp3, fragment.texcoord1.xy );
half4 specMapSRGB = tex2D( samp4, fragment.texcoord2.xy );
half4 specMap = sRGBAToLinearRGBA( specMapSRGB );
half4 specMap = tex2D( samp4, fragment.texcoord2.xy );
//half3 lightVector = normalize( fragment.texcoord0.xyz );
half3 diffuseMap = sRGBToLinearRGB( ConvertYCoCgToRGB( YCoCG ) );
@ -99,11 +99,41 @@ void main( PS_IN fragment, out PS_OUT result ) {
float3 reflectionVector = globalNormal * dot3( globalEye, globalNormal );
reflectionVector = ( reflectionVector * 2.0f ) - globalEye;
//half rim = 1.0f - saturate( hDotN );
//half rimPower = 8.0;
//half3 rimColor = sRGBToLinearRGB( half3( 0.125 ) * 1.2 ) * lightColor * pow( rim, rimPower );
#if defined(USE_PBR)
const half metallic = specMap.g;
const half roughness = specMap.r;
const half glossiness = 1.0 - roughness;
// the vast majority of real-world materials (anything not metal or gems) have F(0°)
// values in a very narrow range (~0.02 - 0.08)
float3 diffuseLight = sRGBToLinearRGB( texCUBE( samp7, globalNormal ).rgb ) * diffuseMap.rgb * ( rpDiffuseModifier.xyz ) * 1.0f;
// approximate non-metals with linear RGB 0.04 which is 0.08 * 0.5 (default in UE4)
const half3 dielectricColor = half3( 0.04 );
// derive diffuse and specular from albedo(m) base color
const half3 baseColor = diffuseMap;
half3 diffuseColor = baseColor * ( 1.0 - metallic );
half3 specularColor = lerp( dielectricColor, baseColor, metallic );
//diffuseColor = half3( 1.0 );
float3 diffuseLight = sRGBToLinearRGB( texCUBE( samp7, globalNormal ).rgb ) * diffuseColor * ( rpDiffuseModifier.xyz ) * 1.5f;
//specularColor = half3( 0.0 );
float mip = clamp( ( roughness * 7.0 ) + 3.0, 0.0, 10.0 );
float3 envColor = sRGBToLinearRGB( texCUBElod( samp8, float4( reflectionVector, mip ) ).rgb ) * ( rpSpecularModifier.xyz ) * 1.0f;
float3 specularLight = envColor * specularColor;
#else
half4 specMapSRGB = specMap;
specMap = sRGBAToLinearRGBA( specMap );
//float3 diffuseLight = sRGBToLinearRGB( texCUBE( samp7, globalNormal ).rgb ) * diffuseMap.rgb * ( rpDiffuseModifier.xyz ) * 1.5f;
float3 diffuseLight = diffuseMap.rgb * ( rpDiffuseModifier.xyz ) * 1.5f;
// HACK calculate roughness from D3 gloss maps
float Y = dot( LUMINANCE_SRGB.rgb, specMapSRGB.rgb );
@ -113,11 +143,32 @@ void main( PS_IN fragment, out PS_OUT result ) {
const float roughness = 1.0 - glossiness;
float mip = roughness * 7.0;
float3 specularLight = sRGBToLinearRGB( texCUBEbias( samp8, float4( reflectionVector, mip ) ).rgb ) * specMap.rgb * ( rpSpecularModifier.xyz ) * 1.0f;
float mip = clamp( ( roughness * 7.0 ) + 0.0, 0.0, 10.0 );
float3 envColor = sRGBToLinearRGB( texCUBElod( samp8, float4( reflectionVector, mip ) ).rgb ) * ( rpSpecularModifier.xyz ) * 1.0f;
float3 specularLight = envColor * specMap.rgb;
#endif
// add glossy fresnel
half hDotN = saturate( dot3( globalEye, globalNormal ) );
half3 specularColor2 = half3( 0.0 );
float3 glossyFresnel = Fresnel_Glossy( specularColor2, roughness, hDotN );
// horizon fade
const half horizonFade = 1.3;
half horiz = saturate( 1.0 + horizonFade * saturate( dot3( reflectionVector, globalNormal ) ) );
horiz *= horiz;
//horiz = clamp( horiz, 0.0, 1.0 );
//specularLight = glossyFresnel * envColor;
specularLight += glossyFresnel * envColor * ( rpSpecularModifier.xyz ) * 0.9 * horiz;
half3 lightColor = sRGBToLinearRGB( rpAmbientColor.rgb );
//result.color.rgb = diffuseLight;
//result.color.rgb = diffuseLight * lightColor;
//result.color.rgb = specularLight;
result.color.rgb = ( diffuseLight + specularLight ) * lightColor * fragment.color.rgb;
//result.color.rgb = localNormal.xyz * 0.5 + 0.5;

View file

@ -216,6 +216,8 @@ typedef enum
TD_COVERAGE, // coverage map for fill depth pass when YCoCG is used
TD_DEPTH, // depth buffer copy for motion blur
// RB begin
TD_SPECULAR_PBR_RMAO, // may be compressed, and always zeros the alpha channel, linear RGB R = roughness, G = metal, B = ambient occlusion
TD_SPECULAR_PBR_RMAOD, // may be compressed, alpha channel contains displacement map
TD_HIGHQUALITY_CUBE, // motorsep - Uncompressed cubemap texture (RGB colorspace)
TD_LOWQUALITY_CUBE, // motorsep - Compressed cubemap texture (RGB colorspace DXT5)
TD_SHADOW_ARRAY, // 2D depth buffer array for shadow mapping
@ -367,6 +369,11 @@ public:
return ( opts.format == FMT_DXT1 || opts.format == FMT_DXT5 );
}
textureUsage_t GetUsage() const
{
return usage;
}
bool IsLoaded() const;
static void GetGeneratedName( idStr& _name, const textureUsage_t& _usage, const cubeFiles_t& _cube );

View file

@ -137,6 +137,19 @@ ID_INLINE void idImage::DeriveOpts()
opts.format = FMT_DXT1;
opts.colorFormat = CFM_DEFAULT;
break;
case TD_SPECULAR_PBR_RMAO:
opts.gammaMips = false;
opts.format = FMT_DXT1;
opts.colorFormat = CFM_DEFAULT;
break;
case TD_SPECULAR_PBR_RMAOD:
opts.gammaMips = false;
opts.format = FMT_DXT5;
opts.colorFormat = CFM_DEFAULT;
break;
case TD_DEFAULT:
opts.gammaMips = true;
opts.format = FMT_DXT5;

View file

@ -1047,17 +1047,17 @@ void idMaterial::ParseBlend( idLexer& src, shaderStage_t* stage )
stage->drawStateBits = GLS_SRCBLEND_ZERO | GLS_DSTBLEND_ONE;
return;
}
if( !token.Icmp( "bumpmap" ) )
if( !token.Icmp( "bumpmap" ) || !token.Icmp( "normalmap" ) )
{
stage->lighting = SL_BUMP;
return;
}
if( !token.Icmp( "diffusemap" ) )
if( !token.Icmp( "diffusemap" ) || !token.Icmp( "basecolormap" ) )
{
stage->lighting = SL_DIFFUSE;
return;
}
if( !token.Icmp( "specularmap" ) )
if( !token.Icmp( "specularmap" ) || !token.Icmp( "rmaomap" ) )
{
stage->lighting = SL_SPECULAR;
return;
@ -1907,7 +1907,18 @@ void idMaterial::ParseStage( idLexer& src, const textureRepeat_t trpDefault )
td = TD_DIFFUSE;
break;
case SL_SPECULAR:
td = TD_SPECULAR;
if( idStr::FindText( imageName, "_rmaod", false ) != -1 )
{
td = TD_SPECULAR_PBR_RMAOD;
}
else if( idStr::FindText( imageName, "_rmao", false ) != -1 )
{
td = TD_SPECULAR_PBR_RMAO;
}
else
{
td = TD_SPECULAR;
}
break;
default:
break;
@ -1920,10 +1931,13 @@ void idMaterial::ParseStage( idLexer& src, const textureRepeat_t trpDefault )
// create new coverage stage
shaderStage_t* newCoverageStage = &pd->parseStages[numStages];
numStages++;
// copy it
*newCoverageStage = *ss;
// toggle alphatest off for the current stage so it doesn't get called during the depth fill pass
ss->hasAlphaTest = false;
// toggle alpha test on for the coverage stage
newCoverageStage->hasAlphaTest = true;
newCoverageStage->lighting = SL_COVERAGE;
@ -2475,7 +2489,7 @@ void idMaterial::ParseMaterial( idLexer& src )
continue;
}
// diffusemap for stage shortcut
else if( !token.Icmp( "diffusemap" ) )
else if( !token.Icmp( "diffusemap" ) || !token.Icmp( "basecolormap" ) )
{
str = R_ParsePastImageProgram( src );
idStr::snPrintf( buffer, sizeof( buffer ), "blend diffusemap\nmap %s\n}\n", str );
@ -2497,7 +2511,7 @@ void idMaterial::ParseMaterial( idLexer& src )
continue;
}
// normalmap for stage shortcut
else if( !token.Icmp( "bumpmap" ) )
else if( !token.Icmp( "bumpmap" ) || !token.Icmp( "normalmap" ) )
{
str = R_ParsePastImageProgram( src );
idStr::snPrintf( buffer, sizeof( buffer ), "blend bumpmap\nmap %s\n}\n", str );

View file

@ -1265,7 +1265,7 @@ void idRenderBackend::SetupInteractionStage( const shaderStage_t* surfaceStage,
idRenderBackend::DrawSingleInteraction
=================
*/
void idRenderBackend::DrawSingleInteraction( drawInteraction_t* din )
void idRenderBackend::DrawSingleInteraction( drawInteraction_t* din, bool useIBL )
{
if( din->bumpImage == NULL )
{
@ -1298,6 +1298,35 @@ void idRenderBackend::DrawSingleInteraction( drawInteraction_t* din )
return;
}
if( useIBL )
{
const textureUsage_t specUsage = din->specularImage->GetUsage();
if( specUsage == TD_SPECULAR_PBR_RMAO || specUsage == TD_SPECULAR_PBR_RMAOD )
{
// PBR path with roughness, metal and AO
if( din->surf->jointCache )
{
renderProgManager.BindShader_ImageBasedLightingSkinned_PBR();
}
else
{
renderProgManager.BindShader_ImageBasedLighting_PBR();
}
}
else
{
if( din->surf->jointCache )
{
renderProgManager.BindShader_ImageBasedLightingSkinned();
}
else
{
renderProgManager.BindShader_ImageBasedLighting();
}
}
}
// bump matrix
SetVertexParm( RENDERPARM_BUMPMATRIX_S, din->bumpMatrix[0].ToFloatPtr() );
SetVertexParm( RENDERPARM_BUMPMATRIX_T, din->bumpMatrix[1].ToFloatPtr() );
@ -1836,7 +1865,7 @@ void idRenderBackend::RenderInteractions( const drawSurf_t* surfList, const view
// draw any previous interaction
if( inter.bumpImage != NULL )
{
DrawSingleInteraction( &inter );
DrawSingleInteraction( &inter, false );
}
inter.bumpImage = surfaceStage->texture.image;
inter.diffuseImage = NULL;
@ -1854,7 +1883,7 @@ void idRenderBackend::RenderInteractions( const drawSurf_t* surfList, const view
// draw any previous interaction
if( inter.diffuseImage != NULL )
{
DrawSingleInteraction( &inter );
DrawSingleInteraction( &inter, false );
}
inter.diffuseImage = surfaceStage->texture.image;
inter.vertexColor = surfaceStage->vertexColor;
@ -1872,7 +1901,7 @@ void idRenderBackend::RenderInteractions( const drawSurf_t* surfList, const view
// draw any previous interaction
if( inter.specularImage != NULL )
{
DrawSingleInteraction( &inter );
DrawSingleInteraction( &inter, false );
}
inter.specularImage = surfaceStage->texture.image;
inter.vertexColor = surfaceStage->vertexColor;
@ -1884,7 +1913,7 @@ void idRenderBackend::RenderInteractions( const drawSurf_t* surfList, const view
}
// draw the final interaction
DrawSingleInteraction( &inter );
DrawSingleInteraction( &inter, false );
renderLog.CloseBlock();
}
@ -2243,7 +2272,7 @@ void idRenderBackend::AmbientPass( const drawSurf_t* const* drawSurfs, int numDr
// draw any previous interaction
if( inter.bumpImage != NULL )
{
DrawSingleInteraction( &inter );
DrawSingleInteraction( &inter, !fillGbuffer );
}
inter.bumpImage = surfaceStage->texture.image;
inter.diffuseImage = NULL;
@ -2264,7 +2293,7 @@ void idRenderBackend::AmbientPass( const drawSurf_t* const* drawSurfs, int numDr
// draw any previous interaction
if( inter.diffuseImage != NULL )
{
DrawSingleInteraction( &inter );
DrawSingleInteraction( &inter, !fillGbuffer );
}
inter.diffuseImage = surfaceStage->texture.image;
@ -2284,7 +2313,7 @@ void idRenderBackend::AmbientPass( const drawSurf_t* const* drawSurfs, int numDr
// draw any previous interaction
if( inter.specularImage != NULL )
{
DrawSingleInteraction( &inter );
DrawSingleInteraction( &inter, !fillGbuffer );
}
inter.specularImage = surfaceStage->texture.image;
inter.vertexColor = surfaceStage->vertexColor;
@ -2296,7 +2325,7 @@ void idRenderBackend::AmbientPass( const drawSurf_t* const* drawSurfs, int numDr
}
// draw the final interaction
DrawSingleInteraction( &inter );
DrawSingleInteraction( &inter,!fillGbuffer );
renderLog.CloseBlock();
}

View file

@ -109,8 +109,8 @@ void idRenderProgManager::Init()
{ BUILTIN_VERTEX_COLOR, "vertex_color.vfp", "", 0, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
{ BUILTIN_AMBIENT_LIGHTING, "ambient_lighting", "", 0, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
{ BUILTIN_AMBIENT_LIGHTING_SKINNED, "ambient_lighting", "_skinned", BIT( USE_GPU_SKINNING ), true, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
{ BUILTIN_AMBIENT_LIGHTING_IBL, "ambient_lighting_IBL", "", 0, false false, SHADER_STAGE_DEFAULT, LAYOUT_DRA },
{ BUILTIN_AMBIENT_LIGHTING_IBL_SKINNED, "ambient_lighting_IBL", "_skinned", BIT( USE_GPU_SKINNING ), true false, SHADER_STAGE_DEFAULT, LAYOUT_DRA },
{ BUILTIN_AMBIENT_LIGHTING_IBL, "ambient_lighting_IBL", "", 0, false false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
{ BUILTIN_AMBIENT_LIGHTING_IBL_SKINNED, "ambient_lighting_IBL", "_skinned", BIT( USE_GPU_SKINNING ), true false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
{ BUILTIN_SMALL_GEOMETRY_BUFFER, "gbuffer", "", 0, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
{ BUILTIN_SMALL_GEOMETRY_BUFFER_SKINNED, "gbuffer", "_skinned", BIT( USE_GPU_SKINNING ), true, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
// RB end
@ -245,6 +245,7 @@ void idRenderProgManager::Init()
// RB begin
renderProgs[builtinShaders[BUILTIN_AMBIENT_LIGHTING_SKINNED]].usesJoints = true;
renderProgs[builtinShaders[BUILTIN_AMBIENT_LIGHTING_IBL_SKINNED]].usesJoints = true;
renderProgs[builtinShaders[BUILTIN_AMBIENT_LIGHTING_IBL_PBR_SKINNED]].usesJoints = true;
renderProgs[builtinShaders[BUILTIN_SMALL_GEOMETRY_BUFFER_SKINNED]].usesJoints = true;
renderProgs[builtinShaders[BUILTIN_INTERACTION_SHADOW_MAPPING_SPOT_SKINNED]].usesJoints = true;
renderProgs[builtinShaders[BUILTIN_INTERACTION_SHADOW_MAPPING_POINT_SKINNED]].usesJoints = true;

View file

@ -300,6 +300,16 @@ public:
BindShader_Builtin( BUILTIN_AMBIENT_LIGHTING_IBL_SKINNED );
}
void BindShader_ImageBasedLighting_PBR()
{
BindShader_Builtin( BUILTIN_AMBIENT_LIGHTING_IBL_PBR );
}
void BindShader_ImageBasedLightingSkinned_PBR()
{
BindShader_Builtin( BUILTIN_AMBIENT_LIGHTING_IBL_PBR_SKINNED );
}
void BindShader_SmallGeometryBuffer()
{
BindShader_Builtin( BUILTIN_SMALL_GEOMETRY_BUFFER );
@ -659,6 +669,8 @@ private:
BUILTIN_AMBIENT_LIGHTING_SKINNED,
BUILTIN_AMBIENT_LIGHTING_IBL,
BUILTIN_AMBIENT_LIGHTING_IBL_SKINNED,
BUILTIN_AMBIENT_LIGHTING_IBL_PBR,
BUILTIN_AMBIENT_LIGHTING_IBL_PBR_SKINNED,
BUILTIN_SMALL_GEOMETRY_BUFFER,
BUILTIN_SMALL_GEOMETRY_BUFFER_SKINNED,
// RB end
@ -744,6 +756,7 @@ private:
BRIGHTPASS,
HDR_DEBUG,
USE_SRGB,
USE_PBR,
MAX_SHADER_MACRO_NAMES,
};

View file

@ -314,7 +314,8 @@ const char* idRenderProgManager::GLSLMacroNames[MAX_SHADER_MACRO_NAMES] =
"LIGHT_PARALLEL",
"BRIGHTPASS",
"HDR_DEBUG",
"USE_SRGB"
"USE_SRGB",
"USE_PBR"
};
// RB end