From b96b085d0438509f87fbabca3f6fe84e2789257e Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Tue, 27 Apr 2021 14:15:11 +0200 Subject: [PATCH] Don't use 1 pixel border for non-lightgrid octahedrons --- .../builtin/debug/octahedron.ps.hlsl | 2 + .../lighting/ambient_lighting_IBL.ps.hlsl | 26 ++++++- neo/renderer/Framebuffer.h | 2 +- neo/renderer/RenderBackend.cpp | 2 +- neo/renderer/RenderCommon.h | 4 +- neo/renderer/RenderProgs_embedded.h | 28 ++++++- neo/renderer/RenderSystem_init.cpp | 2 + neo/renderer/RenderWorld_defs.cpp | 4 +- neo/renderer/RenderWorld_envprobes.cpp | 21 +++-- neo/renderer/RenderWorld_lightgrid.cpp | 76 +++---------------- 10 files changed, 83 insertions(+), 84 deletions(-) diff --git a/base/renderprogs/builtin/debug/octahedron.ps.hlsl b/base/renderprogs/builtin/debug/octahedron.ps.hlsl index 7f8170ab..0ad3c269 100644 --- a/base/renderprogs/builtin/debug/octahedron.ps.hlsl +++ b/base/renderprogs/builtin/debug/octahedron.ps.hlsl @@ -59,12 +59,14 @@ void main( PS_IN fragment, out PS_OUT result ) float2 normalizedOctCoordZeroOne = ( normalizedOctCoord + float2( 1.0 ) ) * 0.5; // offset by one pixel border bleed size for linear filtering +#if 0 float2 octCoordNormalizedToTextureDimensions = ( normalizedOctCoordZeroOne * ( rpCascadeDistances.x - float( 2.0 ) ) ) / rpCascadeDistances.xy; float2 probeTopLeftPosition = float2( 1.0, 1.0 ); float2 normalizedProbeTopLeftPosition = probeTopLeftPosition * rpCascadeDistances.zw; normalizedOctCoordZeroOne.xy = normalizedProbeTopLeftPosition + octCoordNormalizedToTextureDimensions; +#endif //normalizedOctCoordZeroOne = TextureCoordFromDirection( reflectionVector, 0, int( rpCascadeDistances.x ), int( rpCascadeDistances.y ), int( rpCascadeDistances.x ) - 2 ); diff --git a/base/renderprogs/builtin/lighting/ambient_lighting_IBL.ps.hlsl b/base/renderprogs/builtin/lighting/ambient_lighting_IBL.ps.hlsl index 582324ec..7593007c 100644 --- a/base/renderprogs/builtin/lighting/ambient_lighting_IBL.ps.hlsl +++ b/base/renderprogs/builtin/lighting/ambient_lighting_IBL.ps.hlsl @@ -119,6 +119,26 @@ bool AABBRayIntersection( float3 b[2], float3 start, float3 dir, out float scale hit[ax2] >= b[0][ax2] && hit[ax2] <= b[1][ax2] ); } + +float2 OctTexCoord( float3 worldDir ) +{ + float2 normalizedOctCoord = octEncode( worldDir ); + float2 normalizedOctCoordZeroOne = ( normalizedOctCoord + float2( 1.0 ) ) * 0.5; + + // offset by one pixel border bleed size for linear filtering +#if 0 + // texcoord sizes in rpCascadeDistances are not valid + float2 octCoordNormalizedToTextureDimensions = ( normalizedOctCoordZeroOne * ( rpCascadeDistances.x - float( 2.0 ) ) ) / rpCascadeDistances.xy; + + float2 probeTopLeftPosition = float2( 1.0, 1.0 ); + float2 normalizedProbeTopLeftPosition = probeTopLeftPosition * rpCascadeDistances.zw; + + normalizedOctCoordZeroOne.xy = normalizedProbeTopLeftPosition + octCoordNormalizedToTextureDimensions; +#endif + + return normalizedOctCoordZeroOne; +} + void main( PS_IN fragment, out PS_OUT result ) { half4 bumpMap = tex2D( samp0, fragment.texcoord0.xy ); @@ -236,8 +256,7 @@ void main( PS_IN fragment, out PS_OUT result ) // evaluate diffuse IBL - float2 normalizedOctCoord = octEncode( globalNormal ); - float2 normalizedOctCoordZeroOne = ( normalizedOctCoord + float2( 1.0 ) ) * 0.5; + float2 normalizedOctCoordZeroOne = OctTexCoord( globalNormal ); float3 irradiance = tex2D( samp7, normalizedOctCoordZeroOne ).rgb; float3 diffuseLight = ( kD * irradiance * diffuseColor ) * ao * ( rpDiffuseModifier.xyz * 1.0 ); @@ -249,8 +268,7 @@ void main( PS_IN fragment, out PS_OUT result ) float mip = clamp( ( roughness * MAX_REFLECTION_LOD ), 0.0, MAX_REFLECTION_LOD ); //float mip = 0.0; - normalizedOctCoord = octEncode( reflectionVector ); - normalizedOctCoordZeroOne = ( normalizedOctCoord + float2( 1.0 ) ) * 0.5; + normalizedOctCoordZeroOne = OctTexCoord( reflectionVector ); float3 radiance = textureLod( samp8, normalizedOctCoordZeroOne, mip ).rgb; //radiance = float3( 0.0 ); diff --git a/neo/renderer/Framebuffer.h b/neo/renderer/Framebuffer.h index 3a455ff4..2fc7e8cf 100644 --- a/neo/renderer/Framebuffer.h +++ b/neo/renderer/Framebuffer.h @@ -35,7 +35,7 @@ static const int MAX_SSAO_BUFFERS = 2; static const int MAX_HIERARCHICAL_ZBUFFERS = 6; // native resolution + 5 MIP LEVELS static const int RADIANCE_CUBEMAP_SIZE = 256; -static const int IRRADIANCE_CUBEMAP_SIZE = 34; +static const int IRRADIANCE_CUBEMAP_SIZE = 30 + 2; #if 1 static int shadowMapResolutions[MAX_SHADOWMAP_RESOLUTIONS] = { 2048, 1024, 512, 512, 256 }; diff --git a/neo/renderer/RenderBackend.cpp b/neo/renderer/RenderBackend.cpp index 3ddb88af..3beb3125 100644 --- a/neo/renderer/RenderBackend.cpp +++ b/neo/renderer/RenderBackend.cpp @@ -1323,7 +1323,7 @@ void idRenderBackend::DrawSingleInteraction( drawInteraction_t* din, bool useFas const textureUsage_t specUsage = din->specularImage->GetUsage(); // RB begin - if( useIBL && currentSpace->useLightGrid ) + if( useIBL && currentSpace->useLightGrid && r_useLightGrid.GetBool() ) { idVec4 probeMins, probeMaxs, probeCenter; diff --git a/neo/renderer/RenderCommon.h b/neo/renderer/RenderCommon.h index 90413b40..248d4102 100644 --- a/neo/renderer/RenderCommon.h +++ b/neo/renderer/RenderCommon.h @@ -516,7 +516,7 @@ struct calcEnvprobeParms_t static const int LIGHTGRID_IRRADIANCE_BORDER_SIZE = 2; // one pixel border all around the octahedron so 2 on each side -static const int LIGHTGRID_IRRADIANCE_SIZE = ( 16 * 1 ) + LIGHTGRID_IRRADIANCE_BORDER_SIZE; +static const int LIGHTGRID_IRRADIANCE_SIZE = 30 + LIGHTGRID_IRRADIANCE_BORDER_SIZE; struct calcLightGridPointParms_t { @@ -1208,6 +1208,8 @@ extern idCVar r_pbrDebug; extern idCVar r_showViewEnvprobes; extern idCVar r_showLightGrid; // show Quake 3 style light grid points +extern idCVar r_useLightGrid; + extern idCVar r_exposure; // RB end diff --git a/neo/renderer/RenderProgs_embedded.h b/neo/renderer/RenderProgs_embedded.h index 402ecae2..c56b7ce6 100644 --- a/neo/renderer/RenderProgs_embedded.h +++ b/neo/renderer/RenderProgs_embedded.h @@ -2628,12 +2628,14 @@ static const cgShaderDef_t cg_renderprogs[] = " float2 normalizedOctCoordZeroOne = ( normalizedOctCoord + float2( 1.0 ) ) * 0.5;\n" "\n" " // offset by one pixel border bleed size for linear filtering\n" + "#if 0\n" " float2 octCoordNormalizedToTextureDimensions = ( normalizedOctCoordZeroOne * ( rpCascadeDistances.x - float( 2.0 ) ) ) / rpCascadeDistances.xy;\n" "\n" " float2 probeTopLeftPosition = float2( 1.0, 1.0 );\n" " float2 normalizedProbeTopLeftPosition = probeTopLeftPosition * rpCascadeDistances.zw;\n" "\n" " normalizedOctCoordZeroOne.xy = normalizedProbeTopLeftPosition + octCoordNormalizedToTextureDimensions;\n" + "#endif\n" "\n" " //normalizedOctCoordZeroOne = TextureCoordFromDirection( reflectionVector, 0, int( rpCascadeDistances.x ), int( rpCascadeDistances.y ), int( rpCascadeDistances.x ) - 2 );\n" "\n" @@ -4884,6 +4886,26 @@ static const cgShaderDef_t cg_renderprogs[] = " hit[ax2] >= b[0][ax2] && hit[ax2] <= b[1][ax2] );\n" "}\n" "\n" + "\n" + "float2 OctTexCoord( float3 worldDir )\n" + "{\n" + " float2 normalizedOctCoord = octEncode( worldDir );\n" + " float2 normalizedOctCoordZeroOne = ( normalizedOctCoord + float2( 1.0 ) ) * 0.5;\n" + "\n" + " // offset by one pixel border bleed size for linear filtering\n" + "#if 0\n" + " // texcoord sizes in rpCascadeDistances are not valid\n" + " float2 octCoordNormalizedToTextureDimensions = ( normalizedOctCoordZeroOne * ( rpCascadeDistances.x - float( 2.0 ) ) ) / rpCascadeDistances.xy;\n" + "\n" + " float2 probeTopLeftPosition = float2( 1.0, 1.0 );\n" + " float2 normalizedProbeTopLeftPosition = probeTopLeftPosition * rpCascadeDistances.zw;\n" + "\n" + " normalizedOctCoordZeroOne.xy = normalizedProbeTopLeftPosition + octCoordNormalizedToTextureDimensions;\n" + "#endif\n" + "\n" + " return normalizedOctCoordZeroOne;\n" + "}\n" + "\n" "void main( PS_IN fragment, out PS_OUT result )\n" "{\n" " half4 bumpMap = tex2D( samp0, fragment.texcoord0.xy );\n" @@ -5001,8 +5023,7 @@ static const cgShaderDef_t cg_renderprogs[] = "\n" " // evaluate diffuse IBL\n" "\n" - " float2 normalizedOctCoord = octEncode( globalNormal );\n" - " float2 normalizedOctCoordZeroOne = ( normalizedOctCoord + float2( 1.0 ) ) * 0.5;\n" + " float2 normalizedOctCoordZeroOne = OctTexCoord( globalNormal );\n" "\n" " float3 irradiance = tex2D( samp7, normalizedOctCoordZeroOne ).rgb;\n" " float3 diffuseLight = ( kD * irradiance * diffuseColor ) * ao * ( rpDiffuseModifier.xyz * 1.0 );\n" @@ -5014,8 +5035,7 @@ static const cgShaderDef_t cg_renderprogs[] = " float mip = clamp( ( roughness * MAX_REFLECTION_LOD ), 0.0, MAX_REFLECTION_LOD );\n" " //float mip = 0.0;\n" "\n" - " normalizedOctCoord = octEncode( reflectionVector );\n" - " normalizedOctCoordZeroOne = ( normalizedOctCoord + float2( 1.0 ) ) * 0.5;\n" + " normalizedOctCoordZeroOne = OctTexCoord( reflectionVector );\n" "\n" " float3 radiance = textureLod( samp8, normalizedOctCoordZeroOne, mip ).rgb;\n" " //radiance = float3( 0.0 );\n" diff --git a/neo/renderer/RenderSystem_init.cpp b/neo/renderer/RenderSystem_init.cpp index 4b5ac273..63e97e76 100644 --- a/neo/renderer/RenderSystem_init.cpp +++ b/neo/renderer/RenderSystem_init.cpp @@ -301,6 +301,8 @@ idCVar r_pbrDebug( "r_pbrDebug", "0", CVAR_RENDERER | CVAR_INTEGER, "show which idCVar r_showViewEnvprobes( "r_showViewEnvprobes", "0", CVAR_RENDERER | CVAR_INTEGER, "1 = displays the bounding boxes of all view environment probes, 2 = show irradiance" ); idCVar r_showLightGrid( "r_showLightGrid", "0", CVAR_RENDERER | CVAR_INTEGER, "show Quake 3 style light grid points" ); +idCVar r_useLightGrid( "r_useLightGrid", "1", CVAR_RENDERER | CVAR_BOOL, "" ); + idCVar r_exposure( "r_exposure", "0.5", CVAR_ARCHIVE | CVAR_RENDERER | CVAR_FLOAT, "HDR exposure or LDR brightness [0.0 .. 1.0]", 0.0f, 1.0f ); // RB end diff --git a/neo/renderer/RenderWorld_defs.cpp b/neo/renderer/RenderWorld_defs.cpp index e8df5681..bbac89d2 100644 --- a/neo/renderer/RenderWorld_defs.cpp +++ b/neo/renderer/RenderWorld_defs.cpp @@ -769,10 +769,10 @@ void R_DeriveEnvprobeData( RenderEnvprobeLocal* probe ) // TODO get preconvolved cubemaps fullname.Format( "env/%s/envprobe%i_amb", basename.c_str(), probeIndex ); - probe->irradianceImage = globalImages->ImageFromFile( fullname, TF_LINEAR, TR_REPEAT, TD_R11G11B10F, CF_2D_PACKED_MIPCHAIN ); + probe->irradianceImage = globalImages->ImageFromFile( fullname, TF_LINEAR, TR_CLAMP, TD_R11G11B10F, CF_2D_PACKED_MIPCHAIN ); fullname.Format( "env/%s/envprobe%i_spec", basename.c_str(), probeIndex ); - probe->radianceImage = globalImages->ImageFromFile( fullname, TF_DEFAULT, TR_REPEAT, TD_R11G11B10F, CF_2D_PACKED_MIPCHAIN ); + probe->radianceImage = globalImages->ImageFromFile( fullname, TF_DEFAULT, TR_CLAMP, TD_R11G11B10F, CF_2D_PACKED_MIPCHAIN ); // ------------------------------------ // compute the probe projection matrix diff --git a/neo/renderer/RenderWorld_envprobes.cpp b/neo/renderer/RenderWorld_envprobes.cpp index c05ce36d..0ada648b 100644 --- a/neo/renderer/RenderWorld_envprobes.cpp +++ b/neo/renderer/RenderWorld_envprobes.cpp @@ -467,6 +467,7 @@ idVec2 NormalizedOctCoord( int x, int y, const int probeWithBorderSide ) const int margin = 2; // RB: FIXME - margin * 2 is wrong but looks better + // figure out why int probeSideLength = Max( 2, probeWithBorderSide - ( margin * 2 ) ); idVec2 octFragCoord = idVec2( ( x - margin ) % probeWithBorderSide, ( y - margin ) % probeWithBorderSide ); @@ -477,6 +478,16 @@ idVec2 NormalizedOctCoord( int x, int y, const int probeWithBorderSide ) #endif } +static inline idVec2 NormalizedOctCoordNoBorder( int x, int y, const int probeWithBorderSide ) +{ + int probeSideLength = probeWithBorderSide; + + idVec2 octFragCoord = idVec2( x % probeWithBorderSide, y % probeWithBorderSide ); + + // Add back the half pixel to get pixel center normalized coordinates + return ( idVec2( octFragCoord ) + idVec2( 0.5f, 0.5f ) ) * ( 2.0f / float( probeSideLength ) ) - idVec2( 1.0f, 1.0f ); +} + /* static inline float LatLongTexelArea( const idVec2i& pos, const idVec2i& imageSize ) { @@ -615,7 +626,7 @@ void CalculateIrradianceJob( calcEnvprobeParms_t* parms ) { for( int y = dstRect.y; y < ( dstRect.y + dstRect.w ); y++ ) { - idVec2 octCoord = NormalizedOctCoord( x, y, dstRect.z ); + idVec2 octCoord = NormalizedOctCoordNoBorder( x, y, dstRect.z ); // convert UV coord to 3D direction idVec3 dir; @@ -727,11 +738,11 @@ void CalculateIrradianceJob( calcEnvprobeParms_t* parms ) if( mip > 0 ) { // move back to [0, 1] coords - octCoord = NormalizedOctCoord( x - dstRect.x, y - dstRect.y, dstRect.z ); + octCoord = NormalizedOctCoordNoBorder( x - dstRect.x, y - dstRect.y, dstRect.z ); } else { - octCoord = NormalizedOctCoord( x, y, dstRect.z ); + octCoord = NormalizedOctCoordNoBorder( x, y, dstRect.z ); } // convert UV coord to 3D direction @@ -838,11 +849,11 @@ void CalculateRadianceJob( calcEnvprobeParms_t* parms ) if( mip > 0 ) { // move back to [0, 1] coords - octCoord = NormalizedOctCoord( x - dstRect.x, y - dstRect.y, dstRect.z ); + octCoord = NormalizedOctCoordNoBorder( x - dstRect.x, y - dstRect.y, dstRect.z ); } else { - octCoord = NormalizedOctCoord( x, y, dstRect.z ); + octCoord = NormalizedOctCoordNoBorder( x, y, dstRect.z ); } // convert UV coord to 3D direction diff --git a/neo/renderer/RenderWorld_lightgrid.cpp b/neo/renderer/RenderWorld_lightgrid.cpp index 47961442..d19e0c1b 100644 --- a/neo/renderer/RenderWorld_lightgrid.cpp +++ b/neo/renderer/RenderWorld_lightgrid.cpp @@ -38,7 +38,7 @@ If you have questions concerning this license or the applicable additional terms #define STORE_LIGHTGRID_SHDATA 0 -static const byte BLGRID_VERSION = 3; +static const byte BLGRID_VERSION = 4; static const unsigned int BLGRID_MAGIC = ( 'P' << 24 ) | ( 'R' << 16 ) | ( 'O' << 8 ) | BLGRID_VERSION; @@ -513,7 +513,7 @@ bool idRenderWorldLocal::LoadLightGridFile( const char* name ) // if we are reloading the same map, check the timestamp // and try to skip all the work - ID_TIME_T currentTimeStamp = fileSystem->GetTimestamp( fileName ); + ID_TIME_T sourceTimeStamp = fileSystem->GetTimestamp( fileName ); // see if we have a generated version of this bool loaded = false; @@ -524,11 +524,14 @@ bool idRenderWorldLocal::LoadLightGridFile( const char* name ) { int numEntries = 0; int magic = 0; + ID_TIME_T storedTimeStamp; file->ReadBig( magic ); + file->ReadBig( storedTimeStamp ); file->ReadBig( numEntries ); - if( magic == BLGRID_MAGIC && numEntries > 0 ) + if( ( magic == BLGRID_MAGIC ) && ( sourceTimeStamp == storedTimeStamp ) && ( numEntries > 0 ) ) { loaded = true; + for( int i = 0; i < numEntries; i++ ) { idStrStatic< MAX_OSPATH > type; @@ -564,6 +567,7 @@ bool idRenderWorldLocal::LoadLightGridFile( const char* name ) { int magic = BLGRID_MAGIC; outputFile->WriteBig( magic ); + outputFile->WriteBig( sourceTimeStamp ); outputFile->WriteBig( numEntries ); } @@ -646,8 +650,10 @@ bool idRenderWorldLocal::LoadLightGridFile( const char* name ) if( outputFile != NULL ) { outputFile->Seek( 0, FS_SEEK_SET ); + int magic = BLGRID_MAGIC; outputFile->WriteBig( magic ); + outputFile->WriteBig( sourceTimeStamp ); outputFile->WriteBig( numEntries ); } } @@ -913,58 +919,6 @@ void CalculateLightGridPointJob( calcLightGridPointParms_t* parms ) shRadiance[i].Zero(); } -#if 0 - - // build SH by only iterating over the octahedron - // RB: not used because I don't know the texel area of an octahedron pixel and the cubemap texel area is too small - // however it would be nice to use this because it would be 6 times faster - - idVec4 dstRect = R_CalculateMipRect( parms->outHeight, 0 ); - - for( int x = dstRect.x; x < ( dstRect.x + dstRect.z ); x++ ) - { - for( int y = dstRect.y; y < ( dstRect.y + dstRect.w ); y++ ) - { - idVec2 octCoord = NormalizedOctCoord( x, y, dstRect.z ); - - // convert UV coord to 3D direction - idVec3 dir; - - dir.FromOctahedral( octCoord ); - - float u, v; - idVec3 radiance; - R_SampleCubeMapHDR( dir, parms->outHeight, buffers, &radiance[0], u, v ); - - //radiance = dir * 0.5 + idVec3( 0.5f, 0.5f, 0.5f ); - - // convert from [0 .. size-1] to [-1.0 + invSize .. 1.0 - invSize] - const float uu = 2.0f * ( u * invDstSize ) - 1.0f; - const float vv = 2.0f * ( v * invDstSize ) - 1.0f; - - float texelArea = CubemapTexelSolidAngle( uu, vv, invDstSize ); - - const SphericalHarmonicsT& sh = shEvaluate<4>( dir ); - - bool shValid = true; - for( int i = 0; i < 25; i++ ) - { - if( IsNAN( sh[i] ) ) - { - shValid = false; - break; - } - } - - if( shValid ) - { - shAddWeighted( shRadiance, sh, radiance * texelArea ); - } - } - } - -#else - // build SH by iterating over all cubemap pixels for( int side = 0; side < 6; side++ ) @@ -1008,8 +962,6 @@ void CalculateLightGridPointJob( calcLightGridPointParms_t* parms ) } } -#endif - for( int i = 0; i < shSize( 3 ); i++ ) { parms->shRadiance[i] = shRadiance[i]; @@ -1259,7 +1211,7 @@ CONSOLE_COMMAND( bakeLightGrids, "Bake irradiance/vis light grid data", NULL ) lightGridPoint_t* gridPoint = &area->lightGrid.lightGridPoints[ gridCoord[0] * gridStep[0] + gridCoord[1] * gridStep[1] + gridCoord[2] * gridStep[2] ]; if( !gridPoint->valid ) { - progressBar.Increment(); + //progressBar.Increment(); continue; } @@ -1284,14 +1236,6 @@ CONSOLE_COMMAND( bakeLightGrids, "Bake irradiance/vis light grid data", NULL ) byte* float16FRGB = tr.CaptureRenderToBuffer( captureSize, captureSize, &ref ); jobParms->radiance[ side ] = float16FRGB; - -#if 0 - if( i < 3 ) - { - filename.Format( "env/%s/area%i_lightgridpoint%i%s.exr", baseName.c_str(), a, i, extension ); - R_WriteEXR( filename, float16FRGB, 3, size, size, "fs_basepath" ); - } -#endif } tr.lightGridJobs.Append( jobParms );