From 86ff6cea8704c67b19647d05c6f239e4fd987123 Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Sun, 15 Nov 2020 14:01:14 +0100 Subject: [PATCH] Fixed a problem with octahedron irradiance sampling --- base/renderprogs/ambient_lighting_IBL.ps.hlsl | 18 ++--- neo/idlib/math/Vector.cpp | 13 ++-- neo/renderer/Image_intrinsic.cpp | 4 +- neo/renderer/RenderProgs_embedded.h | 65 +++++++++++++++++-- neo/renderer/RenderWorld_envprobes.cpp | 3 +- 5 files changed, 83 insertions(+), 20 deletions(-) diff --git a/base/renderprogs/ambient_lighting_IBL.ps.hlsl b/base/renderprogs/ambient_lighting_IBL.ps.hlsl index e390265f..fa19e218 100644 --- a/base/renderprogs/ambient_lighting_IBL.ps.hlsl +++ b/base/renderprogs/ambient_lighting_IBL.ps.hlsl @@ -69,27 +69,27 @@ struct PS_OUT Available online http://jcgt.org/published/0003/02/01/ */ -float signNotZero( in float k ) +float signNotZeroFloat( float k ) { return ( k >= 0.0 ) ? 1.0 : -1.0; } -float2 signNotZero( in float2 v ) +float2 signNotZero( float2 v ) { - return float2( signNotZero( v.x ), signNotZero( v.y ) ); + return float2( signNotZeroFloat( v.x ), signNotZeroFloat( v.y ) ); } /** Assumes that v is a unit vector. The result is an octahedral vector on the [-1, +1] square. */ -float2 octEncode( in float3 v ) +float2 octEncode( float3 v ) { float l1norm = abs( v.x ) + abs( v.y ) + abs( v.z ); - float2 result = v.xy * ( 1.0 / l1norm ); + float2 oct = v.xy * ( 1.0 / l1norm ); if( v.z < 0.0 ) { - result = ( 1.0 - abs( result.yx ) ) * signNotZero( result.xy ); + oct = ( 1.0 - abs( oct.yx ) ) * signNotZero( oct.xy ); } - return result; + return oct; } @@ -127,6 +127,7 @@ void main( PS_IN fragment, out PS_OUT result ) globalNormal.x = dot3( localNormal, fragment.texcoord4 ); globalNormal.y = dot3( localNormal, fragment.texcoord5 ); globalNormal.z = dot3( localNormal, fragment.texcoord6 ); + globalNormal = normalize( globalNormal ); float3 globalEye = normalize( fragment.texcoord3.xyz ); @@ -199,7 +200,7 @@ void main( PS_IN fragment, out PS_OUT result ) float2 normalizedOctCoord = octEncode( globalNormal ); float2 normalizedOctCoordZeroOne = ( normalizedOctCoord + float2( 1.0 ) ) * 0.5; - float3 irradiance = tex2D( samp7, normalizedOctCoord ).rgb; + float3 irradiance = tex2D( samp7, normalizedOctCoordZeroOne ).rgb; float3 diffuseLight = ( kD * irradiance * diffuseColor ) * ao * ( rpDiffuseModifier.xyz * 1.0 ); // evaluate specular IBL @@ -213,6 +214,7 @@ void main( PS_IN fragment, out PS_OUT result ) normalizedOctCoordZeroOne = ( normalizedOctCoord + float2( 1.0 ) ) * 0.5; float3 radiance = textureLod( samp8, normalizedOctCoordZeroOne, mip ).rgb; + //radiance = float3( 0.0 ); float2 envBRDF = texture( samp3, float2( max( vDotN, 0.0 ), roughness ) ).rg; diff --git a/neo/idlib/math/Vector.cpp b/neo/idlib/math/Vector.cpp index 592aab8c..3f0b03f5 100644 --- a/neo/idlib/math/Vector.cpp +++ b/neo/idlib/math/Vector.cpp @@ -358,6 +358,11 @@ void idVec3::ProjectSelfOntoSphere( const float radius ) // A Survey of Efficient Representations for Independent Unit Vectors, Journal of Computer Graphics Techniques (JCGT), vol. 3, no. 2, 1-30, 2014 // Available online http://jcgt.org/published/0003/02/01/ +inline float signNotZero( float k ) +{ + return ( k >= 0.0f ) ? 1.0f : -1.0f; +} + idVec2 idVec3::ToOctahedral() const { const float L1norm = idMath::Fabs( x ) + idMath::Fabs( x ) + idMath::Fabs( x ); @@ -365,8 +370,8 @@ idVec2 idVec3::ToOctahedral() const idVec2 result; if( z < 0.0f ) { - result.x = ( 1.0f - idMath::Fabs( y ) ) * ( x >= 0.0f ) ? 1.0f : -1.0f; - result.y = ( 1.0f - idMath::Fabs( x ) ) * ( y >= 0.0f ) ? 1.0f : -1.0f; + result.x = ( 1.0f - idMath::Fabs( y ) ) * signNotZero( x ); + result.y = ( 1.0f - idMath::Fabs( x ) ) * signNotZero( y ); } else { @@ -386,8 +391,8 @@ void idVec3::FromOctahedral( const idVec2& o ) if( z < 0.0f ) { float oldX = x; - x = ( 1.0f - idMath::Fabs( y ) ) * ( oldX >= 0.0f ) ? 1.0f : -1.0f; - y = ( 1.0f - idMath::Fabs( oldX ) ) * ( y >= 0.0f ) ? 1.0f : -1.0f; + x = ( 1.0f - idMath::Fabs( y ) ) * signNotZero( oldX ); + y = ( 1.0f - idMath::Fabs( oldX ) ) * signNotZero( y ); } Normalize(); diff --git a/neo/renderer/Image_intrinsic.cpp b/neo/renderer/Image_intrinsic.cpp index 4834bd40..aca556e9 100644 --- a/neo/renderer/Image_intrinsic.cpp +++ b/neo/renderer/Image_intrinsic.cpp @@ -1041,8 +1041,8 @@ void idImageManager::CreateIntrinsicImages() hellLoadingIconImage = ImageFromFile( "textures/loadingicon3", TF_DEFAULT, TR_CLAMP, TD_DEFAULT, CF_2D ); // RB begin - defaultUACIrradianceCube = ImageFromFile( "env/UAC1_amb", TF_DEFAULT, TR_CLAMP, TD_LOOKUP_TABLE_RGB1, CF_2D ); - defaultUACRadianceCube = ImageFromFile( "env/UAC1_spec", TF_DEFAULT, TR_CLAMP, TD_LOOKUP_TABLE_RGB1, CF_2D ); + defaultUACIrradianceCube = ImageFromFile( "env/UAC2_amb", TF_DEFAULT, TR_CLAMP, TD_LOOKUP_TABLE_RGB1, CF_2D ); + defaultUACRadianceCube = ImageFromFile( "env/UAC2_spec", TF_DEFAULT, TR_CLAMP, TD_LOOKUP_TABLE_RGB1, CF_2D ); // RB end release_assert( loadingIconImage->referencedOutsideLevelLoad ); diff --git a/neo/renderer/RenderProgs_embedded.h b/neo/renderer/RenderProgs_embedded.h index 7be966d0..da7d9ce4 100644 --- a/neo/renderer/RenderProgs_embedded.h +++ b/neo/renderer/RenderProgs_embedded.h @@ -2483,8 +2483,8 @@ static const cgShaderDef_t cg_renderprogs[] = "uniform sampler2D samp3 : register(s3); // texture 3 is the BRDF LUT\n" "uniform sampler2D samp4 : register(s4); // texture 4 is SSAO\n" "\n" - "uniform samplerCUBE samp7 : register(s7); // texture 7 is the irradiance cube map\n" - "uniform samplerCUBE samp8 : register(s8); // texture 8 is the radiance cube map\n" + "uniform sampler2D samp7 : register(s7); // texture 7 is the irradiance cube map\n" + "uniform sampler2D samp8 : register(s8); // texture 8 is the radiance cube map\n" "\n" "struct PS_IN \n" "{\n" @@ -2505,6 +2505,52 @@ static const cgShaderDef_t cg_renderprogs[] = "};\n" "// *INDENT-ON*\n" "\n" + "\n" + "\n" + "/** Efficient GPU implementation of the octahedral unit vector encoding from\n" + "\n" + " Cigolle, Donow, Evangelakos, Mara, McGuire, Meyer,\n" + " A Survey of Efficient Representations for Independent Unit Vectors, Journal of Computer Graphics Techniques (JCGT), vol. 3, no. 2, 1-30, 2014\n" + "\n" + " Available online http://jcgt.org/published/0003/02/01/\n" + "*/\n" + "\n" + "float signNotZeroFloat( float k )\n" + "{\n" + " return ( k >= 0.0 ) ? 1.0 : -1.0;\n" + "}\n" + "\n" + "\n" + "float2 signNotZero( float2 v )\n" + "{\n" + " return float2( signNotZeroFloat( v.x ), signNotZeroFloat( v.y ) );\n" + "}\n" + "\n" + "/** Assumes that v is a unit vector. The result is an octahedral vector on the [-1, +1] square. */\n" + "float2 octEncode( float3 v )\n" + "{\n" + " float l1norm = abs( v.x ) + abs( v.y ) + abs( v.z );\n" + " float2 oct = v.xy * ( 1.0 / l1norm );\n" + " if( v.z < 0.0 )\n" + " {\n" + " oct = ( 1.0 - abs( oct.yx ) ) * signNotZero( oct.xy );\n" + " }\n" + " return oct;\n" + "}\n" + "\n" + "\n" + "/** Returns a unit vector. Argument o is an octahedral vector packed via octEncode,\n" + " on the [-1, +1] square*/\n" + "float3 octDecode( float2 o )\n" + "{\n" + " float3 v = float3( o.x, o.y, 1.0 - abs( o.x ) - abs( o.y ) );\n" + " if( v.z < 0.0 )\n" + " {\n" + " v.xy = ( 1.0 - abs( v.yx ) ) * signNotZero( v.xy );\n" + " }\n" + " return normalize( v );\n" + "}\n" + "\n" "void main( PS_IN fragment, out PS_OUT result )\n" "{\n" " half4 bumpMap = tex2D( samp0, fragment.texcoord0.xy );\n" @@ -2527,11 +2573,12 @@ static const cgShaderDef_t cg_renderprogs[] = " globalNormal.x = dot3( localNormal, fragment.texcoord4 );\n" " globalNormal.y = dot3( localNormal, fragment.texcoord5 );\n" " globalNormal.z = dot3( localNormal, fragment.texcoord6 );\n" + " globalNormal = normalize( globalNormal );\n" "\n" " float3 globalEye = normalize( fragment.texcoord3.xyz );\n" "\n" " float3 reflectionVector = globalNormal * dot3( globalEye, globalNormal );\n" - " reflectionVector = ( reflectionVector * 2.0f ) - globalEye;\n" + " reflectionVector = normalize( ( reflectionVector * 2.0f ) - globalEye );\n" "\n" " half vDotN = saturate( dot3( globalEye, globalNormal ) );\n" "\n" @@ -2596,7 +2643,10 @@ static const cgShaderDef_t cg_renderprogs[] = "\n" " // evaluate diffuse IBL\n" "\n" - " float3 irradiance = texCUBE( samp7, globalNormal ).rgb;\n" + " float2 normalizedOctCoord = octEncode( globalNormal );\n" + " float2 normalizedOctCoordZeroOne = ( normalizedOctCoord + float2( 1.0 ) ) * 0.5;\n" + "\n" + " float3 irradiance = tex2D( samp7, normalizedOctCoordZeroOne ).rgb;\n" " float3 diffuseLight = ( kD * irradiance * diffuseColor ) * ao * ( rpDiffuseModifier.xyz * 1.0 );\n" "\n" " // evaluate specular IBL\n" @@ -2605,7 +2655,12 @@ static const cgShaderDef_t cg_renderprogs[] = " const float MAX_REFLECTION_LOD = 10.0;\n" " float mip = clamp( ( roughness * MAX_REFLECTION_LOD ), 0.0, MAX_REFLECTION_LOD );\n" " //float mip = 0.0;\n" - " float3 radiance = textureLod( samp8, reflectionVector, mip ).rgb;\n" + "\n" + " normalizedOctCoord = octEncode( reflectionVector );\n" + " normalizedOctCoordZeroOne = ( normalizedOctCoord + float2( 1.0 ) ) * 0.5;\n" + "\n" + " float3 radiance = textureLod( samp8, normalizedOctCoordZeroOne, mip ).rgb;\n" + " //radiance = float3( 0.0 );\n" "\n" " float2 envBRDF = texture( samp3, float2( max( vDotN, 0.0 ), roughness ) ).rg;\n" "\n" diff --git a/neo/renderer/RenderWorld_envprobes.cpp b/neo/renderer/RenderWorld_envprobes.cpp index e3d273e1..ace05fcd 100644 --- a/neo/renderer/RenderWorld_envprobes.cpp +++ b/neo/renderer/RenderWorld_envprobes.cpp @@ -938,7 +938,7 @@ CONSOLE_COMMAND( generateEnvironmentProbes, "Generate environment probes", NULL axis[5][2][1] = 1; //-------------------------------------------- - // CAPTURE SCENE LIGHTING + // CAPTURE SCENE LIGHTING TO CUBEMAPS //-------------------------------------------- // let's get the game window to a "size" resolution @@ -975,6 +975,7 @@ CONSOLE_COMMAND( generateEnvironmentProbes, "Generate environment probes", NULL ref.viewaxis = axis[j]; fullname.Format( "env/%s/envprobe%i%s", baseName.c_str(), i, extension ); + // TODO capture resolved HDR data without bloom aka _currentRender in 16bit float HDR RGB tr.TakeScreenshot( size, size, fullname, blends, &ref, PNG ); //tr.CaptureRenderToFile( fullname, false ); }