diff --git a/base/renderprogs/BRDF.inc.hlsl b/base/renderprogs/BRDF.inc.hlsl index f4b15b3d..a87f9e71 100644 --- a/base/renderprogs/BRDF.inc.hlsl +++ b/base/renderprogs/BRDF.inc.hlsl @@ -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 ) diff --git a/base/renderprogs/ambient_lighting_IBL.pixel b/base/renderprogs/ambient_lighting_IBL.pixel index 8de07c84..ad314c7b 100644 --- a/base/renderprogs/ambient_lighting_IBL.pixel +++ b/base/renderprogs/ambient_lighting_IBL.pixel @@ -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; diff --git a/neo/renderer/Image.h b/neo/renderer/Image.h index a4879de7..eed7eabb 100644 --- a/neo/renderer/Image.h +++ b/neo/renderer/Image.h @@ -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 ); diff --git a/neo/renderer/Image_load.cpp b/neo/renderer/Image_load.cpp index 2f518a4e..94778b78 100644 --- a/neo/renderer/Image_load.cpp +++ b/neo/renderer/Image_load.cpp @@ -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; diff --git a/neo/renderer/Material.cpp b/neo/renderer/Material.cpp index c55f7615..3029df45 100644 --- a/neo/renderer/Material.cpp +++ b/neo/renderer/Material.cpp @@ -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 ); diff --git a/neo/renderer/RenderBackend.cpp b/neo/renderer/RenderBackend.cpp index 3a270d2c..53243a08 100644 --- a/neo/renderer/RenderBackend.cpp +++ b/neo/renderer/RenderBackend.cpp @@ -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(); } diff --git a/neo/renderer/RenderProgs.cpp b/neo/renderer/RenderProgs.cpp index c32a69e0..a754f0a8 100644 --- a/neo/renderer/RenderProgs.cpp +++ b/neo/renderer/RenderProgs.cpp @@ -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; diff --git a/neo/renderer/RenderProgs.h b/neo/renderer/RenderProgs.h index 680bc0a5..8e5b7e2e 100644 --- a/neo/renderer/RenderProgs.h +++ b/neo/renderer/RenderProgs.h @@ -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, }; diff --git a/neo/renderer/RenderProgs_GLSL.cpp b/neo/renderer/RenderProgs_GLSL.cpp index 6e73c18f..149d6786 100644 --- a/neo/renderer/RenderProgs_GLSL.cpp +++ b/neo/renderer/RenderProgs_GLSL.cpp @@ -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