From 1b3378cb94b37951e7a1cb9d699939c457af51cb Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Mon, 11 May 2020 23:49:04 +0200 Subject: [PATCH] Added Vogel Disk Sampling by Panos Karabelas --- RELEASE-NOTES.md | 2 +- base/renderprogs/global.inc.hlsl | 10 ++++ base/renderprogs/interactionSM.ps.hlsl | 66 ++++++++++++++++++---- neo/renderer/RenderBackend.cpp | 2 +- neo/renderer/RenderProgs_embedded.h | 76 ++++++++++++++++++++++---- neo/renderer/RenderSystem_init.cpp | 4 +- 6 files changed, 134 insertions(+), 26 deletions(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 4bab25bc..9ecbf13a 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -77,7 +77,7 @@ The main goal is that the new content looks the same in RBDOOM-3-BFG as in Blend * Added Blue Noise based Filmic Dithering by Timothy Lottes and Chromatic Aberration -* Improved Shadow Mapping performance by reducing the number of taps from 12 to 6 and keeping a good quality using dithering the result with Blue Noise magic by Alan Wolfe +* Improved Shadow Mapping quality with Vogel Disk Sampling by Panos Karabelas and using dithering the result with Blue Noise magic by Alan Wolfe * Improved Screen Space Ambient Occlusion performance by enhancing the quality with Blue Noise and skipping the expensive extra bilateral filtering pass diff --git a/base/renderprogs/global.inc.hlsl b/base/renderprogs/global.inc.hlsl index 75b7231b..128972fb 100644 --- a/base/renderprogs/global.inc.hlsl +++ b/base/renderprogs/global.inc.hlsl @@ -278,6 +278,16 @@ float RemapNoiseTriErp( const float v ) // http://advances.realtimerendering.com/s2014/index.html float InterleavedGradientNoise( float2 uv ) { + const float3 magic = float3( 0.06711056, 0.00583715, 52.9829189 ); + float rnd = fract( magic.z * fract( dot( uv, magic.xy ) ) ); + + return rnd; +} + +// this noise, including the 5.58... scrolling constant are from Jorge Jimenez +float InterleavedGradientNoiseAnim( float2 uv, float frameIndex ) +{ + uv += ( frameIndex * 5.588238f ); const float3 magic = float3( 0.06711056, 0.00583715, 52.9829189 ); float rnd = fract( magic.z * fract( dot( uv, magic.xy ) ) ); diff --git a/base/renderprogs/interactionSM.ps.hlsl b/base/renderprogs/interactionSM.ps.hlsl index eb2e700f..2ed2c363 100644 --- a/base/renderprogs/interactionSM.ps.hlsl +++ b/base/renderprogs/interactionSM.ps.hlsl @@ -4,6 +4,7 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. Copyright (C) 2013-2020 Robert Beckebans +Copyright (C) 2020 Panos Karabelas This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -76,6 +77,19 @@ float BlueNoise( float2 n, float x ) return noise; } +float2 VogelDiskSample( float sampleIndex, float samplesCount, float phi ) +{ + float goldenAngle = 2.4f; + + float r = sqrt( sampleIndex + 0.5f ) / sqrt( samplesCount ); + float theta = sampleIndex * goldenAngle + phi; + + float sine = sin( theta ); + float cosine = cos( theta ); + + return float2( r * cosine, r * sine ); +} + void main( PS_IN fragment, out PS_OUT result ) { half4 bumpMap = tex2D( samp0, fragment.texcoord1.xy ); @@ -228,7 +242,7 @@ void main( PS_IN fragment, out PS_OUT result ) float numSamples = 16; float stepSize = 1.0 / numSamples; - float4 jitterTC = ( fragment.position * rpScreenCorrectionFactor ) + rpJitterTexOffset; + float2 jitterTC = ( fragment.position.xy * rpScreenCorrectionFactor.xy ) + rpJitterTexOffset.ww; for( float i = 0.0; i < numSamples; i += 1.0 ) { float4 jitter = base + tex2D( samp6, jitterTC.xy ) * rpJitterTexScale; @@ -240,8 +254,11 @@ void main( PS_IN fragment, out PS_OUT result ) shadow *= stepSize; + #elif 0 + // Poisson Disk with White Noise used for years int RBDOOM-3-BFG + const float2 poissonDisk[12] = float2[]( float2( 0.6111618, 0.1050905 ), float2( 0.1088336, 0.1127091 ), @@ -285,9 +302,12 @@ void main( PS_IN fragment, out PS_OUT result ) shadow *= stepSize; - #elif 1 +#if 0 + + // Poisson Disk with animated Blue Noise or Interleaved Gradient Noise + const float2 poissonDisk[12] = float2[]( float2( 0.6111618, 0.1050905 ), float2( 0.1088336, 0.1127091 ), @@ -305,15 +325,11 @@ void main( PS_IN fragment, out PS_OUT result ) float shadow = 0.0; // RB: casting a float to int and using it as index can really kill the performance ... - float numSamples = 6.0; + float numSamples = 12.0; float stepSize = 1.0 / numSamples; - //float4 jitterTC = ( fragment.position * rpScreenCorrectionFactor ) + rpJitterTexOffset; - //float random = tex2D( samp6, jitterTC.xy ).x; - - float random = BlueNoise( fragment.position.xy, 100.0 ); - - //float random = InterleavedGradientNoise( fragment.position.xy ); + float random = BlueNoise( fragment.position.xy, 1.0 ); + //float random = InterleavedGradientNoiseAnim( fragment.position.xy, rpJitterTexOffset.w ); random *= PI; @@ -322,7 +338,7 @@ void main( PS_IN fragment, out PS_OUT result ) rot.y = sin( random ); float shadowTexelSize = rpScreenCorrectionFactor.z * rpJitterTexScale.x; - for( int i = 0; i < 6; i++ ) + for( int i = 0; i < 12; i++ ) { float2 jitter = poissonDisk[i]; float2 jitterRotated; @@ -336,6 +352,34 @@ void main( PS_IN fragment, out PS_OUT result ) shadow *= stepSize; +#else + + // Vogel Disk Sampling + // https://twitter.com/panoskarabelas1/status/1222663889659355140 + + // this approach is more dynamic and can be controlled by r_shadowMapSamples + + float shadow = 0.0; + + float numSamples = rpJitterTexScale.w; + float stepSize = 1.0 / numSamples; + + float vogelPhi = BlueNoise( fragment.position.xy, 1.0 ); + //float vogelPhi = InterleavedGradientNoiseAnim( fragment.position.xy, rpJitterTexOffset.w ); + + float shadowTexelSize = rpScreenCorrectionFactor.z * rpJitterTexScale.x; + for( float i = 0; i < numSamples; i += 1.0 ) + { + float2 jitter = VogelDiskSample( i, numSamples, vogelPhi ); + + float4 shadowTexcoordJittered = float4( shadowTexcoord.xy + jitter * shadowTexelSize, shadowTexcoord.z, shadowTexcoord.w ); + + shadow += texture( samp5, shadowTexcoordJittered.xywz ); + } + + shadow *= stepSize; +#endif + #else float shadow = texture( samp5, shadowTexcoord.xywz ); @@ -374,7 +418,7 @@ void main( PS_IN fragment, out PS_OUT result ) half3 specularColor = specMapSRGB.rgb; // RB: should be linear but it looks too flat #endif - //diffuseColor = half3( 1.0 ); + diffuseColor = half3( 1.0 ); // RB: compensate r_lightScale 3 and the division of Pi //lambert *= 1.3; diff --git a/neo/renderer/RenderBackend.cpp b/neo/renderer/RenderBackend.cpp index 599504e4..11c1d160 100644 --- a/neo/renderer/RenderBackend.cpp +++ b/neo/renderer/RenderBackend.cpp @@ -1677,7 +1677,7 @@ void idRenderBackend::RenderInteractions( const drawSurf_t* surfList, const view jitterTexScale[0] = r_shadowMapJitterScale.GetFloat() * jitterSampleScale; // TODO shadow buffer size fraction shadowMapSize / maxShadowMapSize jitterTexScale[1] = r_shadowMapJitterScale.GetFloat() * jitterSampleScale; jitterTexScale[2] = -r_shadowMapBiasScale.GetFloat(); - jitterTexScale[3] = 0.0f; + jitterTexScale[3] = shadowMapSamples; SetFragmentParm( RENDERPARM_JITTERTEXSCALE, jitterTexScale ); // rpJitterTexScale float jitterTexOffset[4]; diff --git a/neo/renderer/RenderProgs_embedded.h b/neo/renderer/RenderProgs_embedded.h index e1dfad3e..74f1ddc4 100644 --- a/neo/renderer/RenderProgs_embedded.h +++ b/neo/renderer/RenderProgs_embedded.h @@ -292,6 +292,16 @@ static const cgShaderDef_t cg_renderprogs[] = "// http://advances.realtimerendering.com/s2014/index.html\n" "float InterleavedGradientNoise( float2 uv )\n" "{\n" + " const float3 magic = float3( 0.06711056, 0.00583715, 52.9829189 );\n" + " float rnd = fract( magic.z * fract( dot( uv, magic.xy ) ) );\n" + "\n" + " return rnd;\n" + "}\n" + "\n" + "// this noise, including the 5.58... scrolling constant are from Jorge Jimenez\n" + "float InterleavedGradientNoiseAnim( float2 uv, float frameIndex )\n" + "{\n" + " uv += ( frameIndex * 5.588238f );\n" "\n" " const float3 magic = float3( 0.06711056, 0.00583715, 52.9829189 );\n" " float rnd = fract( magic.z * fract( dot( uv, magic.xy ) ) );\n" @@ -9806,6 +9816,7 @@ static const cgShaderDef_t cg_renderprogs[] = "Doom 3 BFG Edition GPL Source Code\n" "Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.\n" "Copyright (C) 2013-2020 Robert Beckebans\n" + "Copyright (C) 2020 Panos Karabelas\n" "\n" "This file is part of the Doom 3 BFG Edition GPL Source Code (\"Doom 3 BFG Edition Source Code\").\n" "\n" @@ -9878,6 +9889,19 @@ static const cgShaderDef_t cg_renderprogs[] = " return noise;\n" "}\n" "\n" + "float2 VogelDiskSample( float sampleIndex, float samplesCount, float phi )\n" + "{\n" + " float goldenAngle = 2.4f;\n" + "\n" + " float r = sqrt( sampleIndex + 0.5f ) / sqrt( samplesCount );\n" + " float theta = sampleIndex * goldenAngle + phi;\n" + "\n" + " float sine = sin( theta );\n" + " float cosine = cos( theta );\n" + "\n" + " return float2( r * cosine, r * sine );\n" + "}\n" + "\n" "void main( PS_IN fragment, out PS_OUT result )\n" "{\n" " half4 bumpMap = tex2D( samp0, fragment.texcoord1.xy );\n" @@ -10030,7 +10054,7 @@ static const cgShaderDef_t cg_renderprogs[] = " float numSamples = 16;\n" " float stepSize = 1.0 / numSamples;\n" "\n" - " float4 jitterTC = ( fragment.position * rpScreenCorrectionFactor ) + rpJitterTexOffset;\n" + " float2 jitterTC = ( fragment.position.xy * rpScreenCorrectionFactor.xy ) + rpJitterTexOffset.ww;\n" " for( float i = 0.0; i < numSamples; i += 1.0 )\n" " {\n" " float4 jitter = base + tex2D( samp6, jitterTC.xy ) * rpJitterTexScale;\n" @@ -10042,8 +10066,11 @@ static const cgShaderDef_t cg_renderprogs[] = "\n" " shadow *= stepSize;\n" "\n" + "\n" "#elif 0\n" "\n" + " // Poisson Disk with White Noise used for years int RBDOOM-3-BFG\n" + "\n" " const float2 poissonDisk[12] = float2[](\n" " float2( 0.6111618, 0.1050905 ),\n" " float2( 0.1088336, 0.1127091 ),\n" @@ -10087,9 +10114,12 @@ static const cgShaderDef_t cg_renderprogs[] = "\n" " shadow *= stepSize;\n" "\n" - "\n" "#elif 1\n" "\n" + "#if 0\n" + "\n" + " // Poisson Disk with animated Blue Noise or Interleaved Gradient Noise\n" + "\n" " const float2 poissonDisk[12] = float2[](\n" " float2( 0.6111618, 0.1050905 ),\n" " float2( 0.1088336, 0.1127091 ),\n" @@ -10107,15 +10137,11 @@ static const cgShaderDef_t cg_renderprogs[] = " float shadow = 0.0;\n" "\n" " // RB: casting a float to int and using it as index can really kill the performance ...\n" - " float numSamples = 6.0;\n" + " float numSamples = 12.0;\n" " float stepSize = 1.0 / numSamples;\n" "\n" - " //float4 jitterTC = ( fragment.position * rpScreenCorrectionFactor ) + rpJitterTexOffset;\n" - " //float random = tex2D( samp6, jitterTC.xy ).x;\n" - "\n" - " float random = BlueNoise( fragment.position.xy, 100.0 );\n" - "\n" - " //float random = InterleavedGradientNoise( fragment.position.xy );\n" + " float random = BlueNoise( fragment.position.xy, 1.0 );\n" + " //float random = InterleavedGradientNoiseAnim( fragment.position.xy, rpJitterTexOffset.w );\n" "\n" " random *= PI;\n" "\n" @@ -10124,7 +10150,7 @@ static const cgShaderDef_t cg_renderprogs[] = " rot.y = sin( random );\n" "\n" " float shadowTexelSize = rpScreenCorrectionFactor.z * rpJitterTexScale.x;\n" - " for( int i = 0; i < 6; i++ )\n" + " for( int i = 0; i < 12; i++ )\n" " {\n" " float2 jitter = poissonDisk[i];\n" " float2 jitterRotated;\n" @@ -10140,6 +10166,34 @@ static const cgShaderDef_t cg_renderprogs[] = "\n" "#else\n" "\n" + " // Vogel Disk Sampling\n" + " // https://twitter.com/panoskarabelas1/status/1222663889659355140\n" + "\n" + " // this approach is more dynamic and can be controlled by r_shadowMapSamples\n" + "\n" + " float shadow = 0.0;\n" + "\n" + " float numSamples = rpJitterTexScale.w;\n" + " float stepSize = 1.0 / numSamples;\n" + "\n" + " float vogelPhi = BlueNoise( fragment.position.xy, 1.0 );\n" + " //float vogelPhi = InterleavedGradientNoiseAnim( fragment.position.xy, rpJitterTexOffset.w );\n" + "\n" + " float shadowTexelSize = rpScreenCorrectionFactor.z * rpJitterTexScale.x;\n" + " for( float i = 0; i < numSamples; i += 1.0 )\n" + " {\n" + " float2 jitter = VogelDiskSample( i, numSamples, vogelPhi );\n" + "\n" + " float4 shadowTexcoordJittered = float4( shadowTexcoord.xy + jitter * shadowTexelSize, shadowTexcoord.z, shadowTexcoord.w );\n" + "\n" + " shadow += texture( samp5, shadowTexcoordJittered.xywz );\n" + " }\n" + "\n" + " shadow *= stepSize;\n" + "#endif\n" + "\n" + "#else\n" + "\n" " float shadow = texture( samp5, shadowTexcoord.xywz );\n" "#endif\n" "\n" @@ -10176,7 +10230,7 @@ static const cgShaderDef_t cg_renderprogs[] = " half3 specularColor = specMapSRGB.rgb; // RB: should be linear but it looks too flat\n" "#endif\n" "\n" - " //diffuseColor = half3( 1.0 );\n" + " diffuseColor = half3( 1.0 );\n" "\n" " // RB: compensate r_lightScale 3 and the division of Pi\n" " //lambert *= 1.3;\n" diff --git a/neo/renderer/RenderSystem_init.cpp b/neo/renderer/RenderSystem_init.cpp index 07e6f230..6f65bffe 100644 --- a/neo/renderer/RenderSystem_init.cpp +++ b/neo/renderer/RenderSystem_init.cpp @@ -245,10 +245,10 @@ idCVar r_useVirtualScreenResolution( "r_useVirtualScreenResolution", "1", CVAR_R idCVar r_shadowMapFrustumFOV( "r_shadowMapFrustumFOV", "92", CVAR_RENDERER | CVAR_FLOAT, "oversize FOV for point light side matching" ); idCVar r_shadowMapSingleSide( "r_shadowMapSingleSide", "-1", CVAR_RENDERER | CVAR_INTEGER, "only draw a single side (0-5) of point lights" ); idCVar r_shadowMapImageSize( "r_shadowMapImageSize", "1024", CVAR_RENDERER | CVAR_INTEGER, "", 128, 2048 ); -idCVar r_shadowMapJitterScale( "r_shadowMapJitterScale", "3", CVAR_RENDERER | CVAR_FLOAT, "scale factor for jitter offset" ); +idCVar r_shadowMapJitterScale( "r_shadowMapJitterScale", "2.5", CVAR_RENDERER | CVAR_FLOAT, "scale factor for jitter offset" ); idCVar r_shadowMapBiasScale( "r_shadowMapBiasScale", "0.0001", CVAR_RENDERER | CVAR_FLOAT, "scale factor for jitter bias" ); idCVar r_shadowMapRandomizeJitter( "r_shadowMapRandomizeJitter", "1", CVAR_RENDERER | CVAR_BOOL, "randomly offset jitter texture each draw" ); -idCVar r_shadowMapSamples( "r_shadowMapSamples", "1", CVAR_RENDERER | CVAR_INTEGER, "0, 1, 4, or 16" ); +idCVar r_shadowMapSamples( "r_shadowMapSamples", "16", CVAR_RENDERER | CVAR_INTEGER, "1, 4, 12 or 16", 1, 64 ); idCVar r_shadowMapSplits( "r_shadowMapSplits", "3", CVAR_RENDERER | CVAR_INTEGER, "number of splits for cascaded shadow mapping with parallel lights", 0, 4 ); idCVar r_shadowMapSplitWeight( "r_shadowMapSplitWeight", "0.9", CVAR_RENDERER | CVAR_FLOAT, "" ); idCVar r_shadowMapLodScale( "r_shadowMapLodScale", "1.4", CVAR_RENDERER | CVAR_FLOAT, "" );