From 5dedbc70a67a6da07886247111f3f0912f672cd9 Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Tue, 12 May 2020 23:38:32 +0200 Subject: [PATCH] Improved filmic post processing --- base/renderprogs/AmbientOcclusion_AO.ps.hlsl | 7 +- base/renderprogs/global.inc.hlsl | 59 +++ base/renderprogs/interactionSM.ps.hlsl | 16 +- base/renderprogs/postprocess.ps.hlsl | 283 +++----------- neo/renderer/RenderProgs_embedded.h | 365 ++++++------------- 5 files changed, 240 insertions(+), 490 deletions(-) diff --git a/base/renderprogs/AmbientOcclusion_AO.ps.hlsl b/base/renderprogs/AmbientOcclusion_AO.ps.hlsl index 47ff72ec..389176a8 100644 --- a/base/renderprogs/AmbientOcclusion_AO.ps.hlsl +++ b/base/renderprogs/AmbientOcclusion_AO.ps.hlsl @@ -112,7 +112,7 @@ struct PS_OUT float BlueNoise( float2 n, float x ) { - float noise = tex2D( samp2, ( n.xy / 256.0 ) ).r; + float noise = tex2D( samp2, n.xy * rpJitterTexOffset.xy ).r; #if TEMPORALLY_VARY_TAPS noise = fract( noise + 0.61803398875 * rpJitterTexOffset.z * x ); @@ -120,9 +120,8 @@ float BlueNoise( float2 n, float x ) noise = fract( noise ); #endif - noise = RemapNoiseTriErp( noise ); - - //noise = noise * 2.0 - 1.0; + //noise = RemapNoiseTriErp( noise ); + //noise = noise * 2.0 - 0.5; return noise; } diff --git a/base/renderprogs/global.inc.hlsl b/base/renderprogs/global.inc.hlsl index 128972fb..a1ad7eb4 100644 --- a/base/renderprogs/global.inc.hlsl +++ b/base/renderprogs/global.inc.hlsl @@ -172,6 +172,32 @@ half4 LinearRGBToSRGB( half4 rgba ) } +float Linear1( float c ) +{ + return ( c <= 0.04045 ) ? c / 12.92 : pow( ( c + 0.055 ) / 1.055, 2.4 ); +} + +float3 Linear3( float3 c ) +{ + return float3( Linear1( c.r ), Linear1( c.g ), Linear1( c.b ) ); +} + +float Srgb1( float c ) +{ + return ( c < 0.0031308 ? c * 12.92 : 1.055 * pow( c, 0.41666 ) - 0.055 ); +} + +float3 Srgb3( float3 c ) +{ + return float3( Srgb1( c.r ), Srgb1( c.g ), Srgb1( c.b ) ); +} + +static const float3 photoLuma = float3( 0.2126, 0.7152, 0.0722 ); +float PhotoLuma( float3 c ) +{ + return dot( c, photoLuma ); +} + // RB end // ---------------------- @@ -309,3 +335,36 @@ float3 Hash33( float3 p3 ) return fract( ( p3.xxy + p3.yxx ) * p3.zyx ); } +static float3 ditherRGB( float3 c, float2 uvSeed ) +{ + // uniform noise + //float3 whiteNoise = Hash33( float3( uvSeed, rpJitterTexOffset.w ) ); + + //float3 whiteNoise = float3( InterleavedGradientNoise( uvSeed ) ); + float3 whiteNoise = float3( InterleavedGradientNoiseAnim( uvSeed, rpJitterTexOffset.w ) ); + + + // triangular noise [-0.5;1.5[ + +#if 1 + whiteNoise.x = RemapNoiseTriErp( whiteNoise.x ); + whiteNoise = whiteNoise * 2.0 - 0.5; +#endif + + whiteNoise = float3( whiteNoise.x ); + + + // quantize/truncate color and dither the result + //float scale = exp2( float( TARGET_BITS ) ) - 1.0; + + // lets assume 2^3 bits = 8 + float scale = 255.0; + + float3 color = floor( c * scale + whiteNoise ) / scale; + +#if defined( USE_LINEAR_RGB ) + +#endif + + return color; +} diff --git a/base/renderprogs/interactionSM.ps.hlsl b/base/renderprogs/interactionSM.ps.hlsl index 2ed2c363..102df2df 100644 --- a/base/renderprogs/interactionSM.ps.hlsl +++ b/base/renderprogs/interactionSM.ps.hlsl @@ -71,8 +71,7 @@ float BlueNoise( float2 n, float x ) noise = fract( noise + c_goldenRatioConjugate * rpJitterTexOffset.w * x ); //noise = RemapNoiseTriErp( noise ); - - //noise = noise * 2.0 - 1.0; + //noise = noise * 2.0 - 0.5; return noise; } @@ -94,7 +93,7 @@ void main( PS_IN fragment, out PS_OUT result ) { half4 bumpMap = tex2D( samp0, fragment.texcoord1.xy ); half4 lightFalloff = ( idtex2Dproj( samp3, fragment.texcoord2 ) ); - half4 lightProj = ( idtex2Dproj( samp4, fragment.texcoord3 ) ); + half4 lightProj = ( idtex2Dproj( samp4, fragment.texcoord3 ) ); half4 YCoCG = tex2D( samp2, fragment.texcoord4.xy ); half4 specMapSRGB = tex2D( samp1, fragment.texcoord5.xy ); half4 specMap = sRGBAToLinearRGBA( specMapSRGB ); @@ -418,13 +417,20 @@ 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; // rpDiffuseModifier contains light color multiplier - half3 lightColor = sRGBToLinearRGB( lightProj.xyz * lightFalloff.xyz );// * rpDiffuseModifier.xyz; + half3 lightColor = sRGBToLinearRGB( lightProj.xyz * lightFalloff.xyz ); + //lightColor = ditherRGB( lightColor, fragment.position.xy ); + //lightColor *= sRGBToLinearRGB( lightFalloff.xyz );// * rpDiffuseModifier.xyz; + + //lightColor = ditherRGB( lightColor, fragment.position.xy ); + + + //lightColor = ditherRGB( lightColor, fragment.position.xy * rpWindowCoord.xy ); half vdotN = clamp( dot3( viewVector, localNormal ), 0.0, 1.0 ); half vdotH = clamp( dot3( viewVector, halfAngleVector ), 0.0, 1.0 ); diff --git a/base/renderprogs/postprocess.ps.hlsl b/base/renderprogs/postprocess.ps.hlsl index 605cf75c..a84f64e7 100644 --- a/base/renderprogs/postprocess.ps.hlsl +++ b/base/renderprogs/postprocess.ps.hlsl @@ -46,7 +46,7 @@ struct PS_OUT }; // *INDENT-ON* -#define USE_CHROMATIC_ABERRATION 0 +#define USE_CHROMATIC_ABERRATION 1 #define Chromatic_Amount 0.075 #define USE_TECHNICOLOR 0 // [0 or 1] @@ -62,6 +62,11 @@ struct PS_OUT #define Vibrance_RGB_Balance float3( 1.0, 1.0, 1.0 ) #define USE_DITHERING 1 +#define Dithering_QuantizationSteps 8.0 // 8.0 = 2 ^ 3 quantization bits +#define Dithering_NoiseBoost 1.0 +#define Dithering_Wide 1.0 +#define DITHER_IN_LINEAR_SPACE 0 +#define DITHER_GENERATE_NOISE 0 float3 overlay( float3 a, float3 b ) { @@ -208,31 +213,7 @@ void ChromaticAberrationPass( inout float4 color ) //----------------------------------------------------------------------- -float Linear1( float c ) -{ - return ( c <= 0.04045 ) ? c / 12.92 : pow( ( c + 0.055 ) / 1.055, 2.4 ); -} -float3 Linear3( float3 c ) -{ - return float3( Linear1( c.r ), Linear1( c.g ), Linear1( c.b ) ); -} - -float Srgb1( float c ) -{ - return ( c < 0.0031308 ? c * 12.92 : 1.055 * pow( c, 0.41666 ) - 0.055 ); -} - -float3 Srgb3( float3 c ) -{ - return float3( Srgb1( c.r ), Srgb1( c.g ), Srgb1( c.b ) ); -} - -float3 photoLuma = float3( 0.2126, 0.7152, 0.0722 ); -float PhotoLuma( float3 c ) -{ - return dot( c, photoLuma ); -} @@ -306,7 +287,11 @@ float Step2( float2 uv, float n ) { float a = 1.0, b = 2.0, c = -2.0, t = 1.0; +#if DITHER_IN_LINEAR_SPACE return ( 1.0 / ( a * 4.0 + b * 4.0 - c ) ) * ( +#else + return ( 4.0 / ( a * 4.0 + b * 4.0 - c ) ) * ( +#endif Step1( uv + float2( -1.0, -1.0 ) * t, n ) * a + Step1( uv + float2( 0.0, -1.0 ) * t, n ) * b + Step1( uv + float2( 1.0, -1.0 ) * t, n ) * a + @@ -322,7 +307,7 @@ float Step2( float2 uv, float n ) // Used for stills. float3 Step3( float2 uv ) { -#if 0 +#if DITHER_GENERATE_NOISE float a = Step2( uv, 0.07 ); float b = Step2( uv, 0.11 ); float c = Step2( uv, 0.13 ); @@ -331,11 +316,13 @@ float3 Step3( float2 uv ) #else float3 noise = BlueNoise3( uv, 0.0 ); +#if 1 noise.x = RemapNoiseTriErp( noise.x ); noise.y = RemapNoiseTriErp( noise.y ); noise.z = RemapNoiseTriErp( noise.z ); noise = noise * 2.0 - 1.0; +#endif return noise; #endif @@ -344,7 +331,7 @@ float3 Step3( float2 uv ) // Used for temporal dither. float3 Step3T( float2 uv ) { -#if 0 +#if DITHER_GENERATE_NOISE float a = Step2( uv, 0.07 * fract( rpJitterTexOffset.z ) ); float b = Step2( uv, 0.11 * fract( rpJitterTexOffset.z ) ); float c = Step2( uv, 0.13 * fract( rpJitterTexOffset.z ) ); @@ -353,19 +340,19 @@ float3 Step3T( float2 uv ) #else float3 noise = BlueNoise3( uv, 1.0 ); +#if 1 noise.x = RemapNoiseTriErp( noise.x ); noise.y = RemapNoiseTriErp( noise.y ); noise.z = RemapNoiseTriErp( noise.z ); noise = noise * 2.0 - 1.0; +#endif return noise; #endif } -#define STEPS 12.0 - void DitheringPass( inout float4 fragColor ) { float2 uv = fragment.position.xy * 1.0; @@ -373,31 +360,39 @@ void DitheringPass( inout float4 fragColor ) float3 color = fragColor.rgb; -#if 1 +#if 0 // BOTTOM: Show bands. if( uv2.y >= 0.975 ) { + // quantized signal color = float3( uv2.x ); - color = floor( color * STEPS + Step3( uv ) * 4.0 ) * ( 1.0 / ( STEPS - 1.0 ) ); + + // dithered still + //color = floor( color * Dithering_QuantizationSteps + Step3( uv ) * Dithering_NoiseBoost ) * ( 1.0 / ( Dithering_QuantizationSteps - 1.0 ) ); } else if( uv2.y >= 0.95 ) { + // quantized signal color = float3( uv2.x ); - color = floor( color * STEPS ) * ( 1.0 / ( STEPS - 1.0 ) ); + color = floor( color * Dithering_QuantizationSteps ) * ( 1.0 / ( Dithering_QuantizationSteps - 1.0 ) ); } else if( uv2.y >= 0.925 ) { + // quantized signal dithered temporally color = float3( uv2.x ); - color = floor( color * STEPS + Step3T( uv ) * 4.0 ) * ( 1.0 / ( STEPS - 1.0 ) ); + color = floor( color * Dithering_QuantizationSteps + Step3( uv ) * Dithering_NoiseBoost ) * ( 1.0 / ( Dithering_QuantizationSteps - 1.0 ) ); } // TOP: Show dither texture. else if( uv2.y >= 0.9 ) { - color = Step3( uv ) * 1.0 + 0.5; + color = Step3( uv ) * ( 0.25 * Dithering_NoiseBoost ) + 0.5; } else #endif { + +#if DITHER_IN_LINEAR_SPACE + color = Linear3( color ); // Add grain in linear space. @@ -406,13 +401,13 @@ void DitheringPass( inout float4 fragColor ) #if 1 // Too expensive. // Helps understand the fast solutions. - float3 amount = Linear3( Srgb3( color ) + ( 4.0 / STEPS ) ) - color; + float3 amount = Linear3( Srgb3( color ) + ( Dithering_NoiseBoost / Dithering_QuantizationSteps ) ) - color; #else // Less too expensive. float luma = PhotoLuma( color ); // Implement this as a texture lookup table. - float amount = Linear1( Srgb1( luma ) + ( 4.0 / STEPS ) ) - luma; + float amount = Linear1( Srgb1( luma ) + ( Dithering_NoiseBoost / Dithering_QuantizationSteps ) ) - luma; #endif #else @@ -422,227 +417,43 @@ void DitheringPass( inout float4 fragColor ) // For HDR need saturate() around luma. float luma = PhotoLuma( color ); float amount = mix( - Linear1( 4.0 / STEPS ), - Linear1( ( 4.0 / STEPS ) + 1.0 ) - 1.0, + Linear1( Dithering_NoiseBoost / Dithering_QuantizationSteps ), + Linear1( ( Dithering_NoiseBoost / Dithering_QuantizationSteps ) + 1.0 ) - 1.0, luma ); #else // Hack 2 (slower?). // For HDR need saturate() around color in mix(). float3 amount = mix( - float3( Linear1( 4.0 / STEPS ) ), - float3( Linear1( ( 4.0 / STEPS ) + 1.0 ) - 1.0 ), + float3( Linear1( Dithering_NoiseBoost / Dithering_QuantizationSteps ) ), + float3( Linear1( ( Dithering_NoiseBoost / Dithering_QuantizationSteps ) + 1.0 ) - 1.0 ), color ); #endif #endif - color += Step3T( uv ) * amount; + color += Step3T( uv ) * amount;// * Dithering_NoiseBoost; // The following represents hardware linear->sRGB xform // which happens on sRGB formatted render targets, // except using a lot less bits/pixel. color = max( float3( 0.0 ), color ); color = Srgb3( color ); - color = floor( color * STEPS ) * ( 1.0 / ( STEPS - 1.0 ) ); - } + color = floor( color * Dithering_QuantizationSteps ) * ( 1.0 / ( Dithering_QuantizationSteps - 1.0 ) ); - fragColor.rgb = color; -} - - - -#define ANIMATE_NOISE 1 -#define TARGET_BITS 4 // 2^3 = 8 dithered to this many bits -#define DITHER_IN_LINEAR_SPACE 0 - -//---------------------------------------------------------------------------------------- - -/* -Items of note! - -* The blue noise texture sampling should be set to "nearest" (not mip map!) and repeat - -* you should calculate the uv to use based on the pixel coordinate and the size of the blue noise texture. - * aka you should tile the blue noise texture across the screen. - * blue noise actually tiles really well unlike white noise. - -* A blue noise texture is "low discrepancy over space" which means there are fewer visible patterns than white noise - * it also gives more even coverage vs white noise. no clumps or voids. - -* In an attempt to make it also blue noise over time, you can add the golden ratio and frac it. - * that makes it lower discrepancy over time, but makes it less good over space. - * thanks to r4unit for that tip! https://twitter.com/R4_Unit - -* Animating the noise in this demo makes the noise basically disappear imo, it's really nice! - -For more information: - -What the heck is blue nois: -https://blog.demofox.org/2018/01/30/what-the-heck-is-blue-noise/ - -Low discrepancy sequences: -https://blog.demofox.org/2017/05/29/when-random-numbers-are-too-random-low-discrepancy-sequences/ - -You can get your own blue noise textures here: -http://momentsingraphics.de/?p=127 - -*/ -void DitheringPassDemoFox( inout float4 fragColor ) -{ - // texture color - float2 uv = fragment.position.xy; - float2 uv2 = fragment.texcoord0; - float3 fg = fragColor.rgb; - - float3 color = fg; - -#if 1 - // TOP: show bands - if( uv2.y >= 0.975 ) - { - color = float3( uv2.x ); - color = floor( color * STEPS + Step3( uv ) * 4.0 ) * ( 1.0 / ( STEPS - 1.0 ) ); - } - else if( uv2.y >= 0.95 ) - { - color = float3( uv2.x ); - color = floor( color * STEPS ) * ( 1.0 / ( STEPS - 1.0 ) ); - } - else if( uv2.y >= 0.925 ) - { - color = float3( uv2.x ); - color = floor( color * STEPS + Step3T( uv ) * 4.0 ) * ( 1.0 / ( STEPS - 1.0 ) ); - } - // BOTTOM: show dither texture - else if( uv2.y >= 0.9 ) - { - color = Step3( uv ).rgb; - } - else -#endif - { - // right of the screen is dithered using white noise to a fewer number of bits per color channel - if( uv2.x > 3.0 / 4.0 ) - { - // get white noise "random" number -#if ANIMATE_NOISE - float3 whiteNoise = Hash33( float3( uv, rpJitterTexOffset.w / 256.0 ) ); #else - float3 whiteNoise = Hash33( float3( uv, 0.0 ) ); -#endif - - // dither to the specified number of bits, using sRGB conversions if desired -#if DITHER_IN_LINEAR_SPACE - fg = pow( fg, float3( 2.2 ) ); -#endif - - float scale = exp2( float( TARGET_BITS ) ) - 1.0; - color = floor( fg * scale + whiteNoise ) / scale; - -#if DITHER_IN_LINEAR_SPACE - color = pow( col, 1.0 / float3( 2.2 ) ); -#endif - } - // middle right of the screen is dithered using blue noise to a fewer number of bits per color channel - else if( uv2.x > 2.0 / 4.0 ) - { - float3 blueNoise = BlueNoise3( uv, 1.0 ); - - // dither to the specified number of bits, using sRGB conversions if desired -#if DITHER_IN_LINEAR_SPACE - fg = pow( fg, float3( 2.2 ) ); -#endif - - float scale = exp2( float( TARGET_BITS ) ) - 1.0; - color = floor( fg * scale + blueNoise ) / scale; - -#if DITHER_IN_LINEAR_SPACE - color = pow( color, 1.0 / float3( 2.2 ) ); -#endif - } - // middle left of the screen is quantized but not dithered - else if( uv2.x > 1.0 / 4.0 ) - { - // dither to the specified number of bits, using sRGB conversions if desired -#if DITHER_IN_LINEAR_SPACE - fg = pow( fg, float3( 2.2 ) ); -#endif - - float scale = exp2( float( TARGET_BITS ) ) - 1.0; - color = floor( fg * scale + 0.5f ) / scale; - -#if DITHER_IN_LINEAR_SPACE - color = pow( color, 1.0 / float3( 2.2 ) ); -#endif - } - // left side of screen is left alone for comparison - else - { - color = fg; - } - - if( abs( uv2.x - 1.0 / 4.0 ) < 0.001 || abs( uv2.x - 2.0 / 4.0 ) < 0.001 || abs( uv2.x - 3.0 / 4.0 ) < 0.001 ) - { - color = float3( 0.0, 1.0, 0.0 ); - } - } - - fragColor.rgb = color; -} - -void DitheringPassSlim( inout float4 fragColor ) -{ - // texture color - float2 uv = fragment.position.xy; - float2 uv2 = fragment.texcoord0; - float3 fg = fragColor.rgb; - - float3 color = fg; #if 0 - if( uv2.y >= 0.975 ) - { - // source signal - color = float3( uv2.x ); - } - else if( uv2.y >= 0.95 ) - { - color = float3( uv2.x ); - - // quantized signal - float scale = exp2( float( TARGET_BITS ) ) - 1.0; - color = floor( color * scale + 0.0f ) / scale; - } - else if( uv2.y >= 0.925 ) - { - // dithered quantized signal - color = float3( uv2.x ); - color = floor( color * STEPS + Step3T( uv ) * 4.0 ) * ( 1.0 / ( STEPS - 1.0 ) ); - } - else if( uv2.y >= 0.9 ) - { - // dither texture - color = Step3( uv ).rgb; - } - else + if( uv2.x <= 0.5 ) + { + // quantized but not dithered + color = floor( 0.5 + color * ( Dithering_QuantizationSteps + Dithering_Wide - 1.0 ) + ( -Dithering_Wide * 0.5 ) ) * ( 1.0 / ( Dithering_QuantizationSteps - 1.0 ) ); + } + else #endif - { - float3 noise = BlueNoise3( uv, 1.0 ); - - //noise.x = RemapNoiseTriErp( noise.x ); - //noise.y = RemapNoiseTriErp( noise.y ); - //noise.z = RemapNoiseTriErp( noise.z ); - - // dither to the specified number of bits, using sRGB conversions if desired -#if DITHER_IN_LINEAR_SPACE - fg = pow( fg, float3( 2.2 ) ); + { + color = floor( 0.5 + color * ( Dithering_QuantizationSteps + Dithering_Wide - 1.0 ) + ( -Dithering_Wide * 0.5 ) + Step3T( uv ) * ( Dithering_Wide ) ) * ( 1.0 / ( Dithering_QuantizationSteps - 1.0 ) ); + } #endif - float scale = exp2( float( TARGET_BITS ) ) - 1.0; - color = floor( fg * scale + noise ) / scale; - -#if DITHER_IN_LINEAR_SPACE - color = pow( color, 1.0 / float3( 2.2 ) ); -#endif } fragColor.rgb = color; @@ -669,7 +480,7 @@ void main( PS_IN fragment, out PS_OUT result ) #endif #if USE_DITHERING - DitheringPassSlim( color ); + DitheringPass( color ); #endif result.color = color; diff --git a/neo/renderer/RenderProgs_embedded.h b/neo/renderer/RenderProgs_embedded.h index 74f1ddc4..86cd0d94 100644 --- a/neo/renderer/RenderProgs_embedded.h +++ b/neo/renderer/RenderProgs_embedded.h @@ -186,6 +186,32 @@ static const cgShaderDef_t cg_renderprogs[] = "}\n" "\n" "\n" + "float Linear1( float c )\n" + "{\n" + " return ( c <= 0.04045 ) ? c / 12.92 : pow( ( c + 0.055 ) / 1.055, 2.4 );\n" + "}\n" + "\n" + "float3 Linear3( float3 c )\n" + "{\n" + " return float3( Linear1( c.r ), Linear1( c.g ), Linear1( c.b ) );\n" + "}\n" + "\n" + "float Srgb1( float c )\n" + "{\n" + " return ( c < 0.0031308 ? c * 12.92 : 1.055 * pow( c, 0.41666 ) - 0.055 );\n" + "}\n" + "\n" + "float3 Srgb3( float3 c )\n" + "{\n" + " return float3( Srgb1( c.r ), Srgb1( c.g ), Srgb1( c.b ) );\n" + "}\n" + "\n" + "static const float3 photoLuma = float3( 0.2126, 0.7152, 0.0722 );\n" + "float PhotoLuma( float3 c )\n" + "{\n" + " return dot( c, photoLuma );\n" + "}\n" + "\n" "// RB end\n" "\n" "// ----------------------\n" @@ -323,6 +349,39 @@ static const cgShaderDef_t cg_renderprogs[] = " return fract( ( p3.xxy + p3.yxx ) * p3.zyx );\n" "}\n" "\n" + "static float3 ditherRGB( float3 c, float2 uvSeed )\n" + "{\n" + " // uniform noise\n" + " //float3 whiteNoise = Hash33( float3( uvSeed, rpJitterTexOffset.w ) );\n" + "\n" + " //float3 whiteNoise = float3( InterleavedGradientNoise( uvSeed ) );\n" + " float3 whiteNoise = float3( InterleavedGradientNoiseAnim( uvSeed, rpJitterTexOffset.w ) );\n" + "\n" + "\n" + " // triangular noise [-0.5;1.5[\n" + "\n" + "#if 1\n" + " whiteNoise.x = RemapNoiseTriErp( whiteNoise.x );\n" + " whiteNoise = whiteNoise * 2.0 - 0.5;\n" + "#endif\n" + "\n" + " whiteNoise = float3( whiteNoise.x );\n" + "\n" + "\n" + " // quantize/truncate color and dither the result\n" + " //float scale = exp2( float( TARGET_BITS ) ) - 1.0;\n" + "\n" + " // lets assume 2^3 bits = 8\n" + " float scale = 255.0;\n" + "\n" + " float3 color = floor( c * scale + whiteNoise ) / scale;\n" + "\n" + "#if defined( USE_LINEAR_RGB )\n" + "\n" + "#endif\n" + "\n" + " return color;\n" + "}\n" "\n" }, @@ -2840,7 +2899,7 @@ static const cgShaderDef_t cg_renderprogs[] = "\n" "float BlueNoise( float2 n, float x )\n" "{\n" - " float noise = tex2D( samp2, ( n.xy / 256.0 ) ).r;\n" + " float noise = tex2D( samp2, n.xy * rpJitterTexOffset.xy ).r;\n" "\n" "#if TEMPORALLY_VARY_TAPS\n" " noise = fract( noise + 0.61803398875 * rpJitterTexOffset.z * x );\n" @@ -2848,9 +2907,8 @@ static const cgShaderDef_t cg_renderprogs[] = " noise = fract( noise );\n" "#endif\n" "\n" - " noise = RemapNoiseTriErp( noise );\n" - "\n" - " //noise = noise * 2.0 - 1.0;\n" + " //noise = RemapNoiseTriErp( noise );\n" + " //noise = noise * 2.0 - 0.5;\n" "\n" " return noise;\n" "}\n" @@ -9883,8 +9941,7 @@ static const cgShaderDef_t cg_renderprogs[] = " noise = fract( noise + c_goldenRatioConjugate * rpJitterTexOffset.w * x );\n" "\n" " //noise = RemapNoiseTriErp( noise );\n" - "\n" - " //noise = noise * 2.0 - 1.0;\n" + " //noise = noise * 2.0 - 0.5;\n" "\n" " return noise;\n" "}\n" @@ -9906,7 +9963,7 @@ static const cgShaderDef_t cg_renderprogs[] = "{\n" " half4 bumpMap = tex2D( samp0, fragment.texcoord1.xy );\n" " half4 lightFalloff = ( idtex2Dproj( samp3, fragment.texcoord2 ) );\n" - " half4 lightProj = ( idtex2Dproj( samp4, fragment.texcoord3 ) );\n" + " half4 lightProj = ( idtex2Dproj( samp4, fragment.texcoord3 ) );\n" " half4 YCoCG = tex2D( samp2, fragment.texcoord4.xy );\n" " half4 specMapSRGB = tex2D( samp1, fragment.texcoord5.xy );\n" " half4 specMap = sRGBAToLinearRGBA( specMapSRGB );\n" @@ -10230,13 +10287,20 @@ 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" "\n" " // rpDiffuseModifier contains light color multiplier\n" - " half3 lightColor = sRGBToLinearRGB( lightProj.xyz * lightFalloff.xyz );// * rpDiffuseModifier.xyz;\n" + " half3 lightColor = sRGBToLinearRGB( lightProj.xyz * lightFalloff.xyz );\n" + " //lightColor = ditherRGB( lightColor, fragment.position.xy );\n" + " //lightColor *= sRGBToLinearRGB( lightFalloff.xyz );// * rpDiffuseModifier.xyz;\n" + "\n" + " //lightColor = ditherRGB( lightColor, fragment.position.xy );\n" + "\n" + "\n" + " //lightColor = ditherRGB( lightColor, fragment.position.xy * rpWindowCoord.xy );\n" "\n" " half vdotN = clamp( dot3( viewVector, localNormal ), 0.0, 1.0 );\n" " half vdotH = clamp( dot3( viewVector, halfAngleVector ), 0.0, 1.0 );\n" @@ -10709,7 +10773,7 @@ static const cgShaderDef_t cg_renderprogs[] = "};\n" "// *INDENT-ON*\n" "\n" - "#define USE_CHROMATIC_ABERRATION 0\n" + "#define USE_CHROMATIC_ABERRATION 1\n" "#define Chromatic_Amount 0.075\n" "\n" "#define USE_TECHNICOLOR 0 // [0 or 1]\n" @@ -10725,6 +10789,11 @@ static const cgShaderDef_t cg_renderprogs[] = "#define Vibrance_RGB_Balance float3( 1.0, 1.0, 1.0 )\n" "\n" "#define USE_DITHERING 1\n" + "#define Dithering_QuantizationSteps 8.0 // 8.0 = 2 ^ 3 quantization bits\n" + "#define Dithering_NoiseBoost 1.0\n" + "#define Dithering_Wide 1.0\n" + "#define DITHER_IN_LINEAR_SPACE 0\n" + "#define DITHER_GENERATE_NOISE 0\n" "\n" "float3 overlay( float3 a, float3 b )\n" "{\n" @@ -10871,31 +10940,7 @@ static const cgShaderDef_t cg_renderprogs[] = "\n" "//-----------------------------------------------------------------------\n" "\n" - "float Linear1( float c )\n" - "{\n" - " return ( c <= 0.04045 ) ? c / 12.92 : pow( ( c + 0.055 ) / 1.055, 2.4 );\n" - "}\n" "\n" - "float3 Linear3( float3 c )\n" - "{\n" - " return float3( Linear1( c.r ), Linear1( c.g ), Linear1( c.b ) );\n" - "}\n" - "\n" - "float Srgb1( float c )\n" - "{\n" - " return ( c < 0.0031308 ? c * 12.92 : 1.055 * pow( c, 0.41666 ) - 0.055 );\n" - "}\n" - "\n" - "float3 Srgb3( float3 c )\n" - "{\n" - " return float3( Srgb1( c.r ), Srgb1( c.g ), Srgb1( c.b ) );\n" - "}\n" - "\n" - "float3 photoLuma = float3( 0.2126, 0.7152, 0.0722 );\n" - "float PhotoLuma( float3 c )\n" - "{\n" - " return dot( c, photoLuma );\n" - "}\n" "\n" "\n" "\n" @@ -10969,7 +11014,11 @@ static const cgShaderDef_t cg_renderprogs[] = "{\n" " float a = 1.0, b = 2.0, c = -2.0, t = 1.0;\n" "\n" + "#if DITHER_IN_LINEAR_SPACE\n" " return ( 1.0 / ( a * 4.0 + b * 4.0 - c ) ) * (\n" + "#else\n" + " return ( 4.0 / ( a * 4.0 + b * 4.0 - c ) ) * (\n" + "#endif\n" " Step1( uv + float2( -1.0, -1.0 ) * t, n ) * a +\n" " Step1( uv + float2( 0.0, -1.0 ) * t, n ) * b +\n" " Step1( uv + float2( 1.0, -1.0 ) * t, n ) * a +\n" @@ -10985,7 +11034,7 @@ static const cgShaderDef_t cg_renderprogs[] = "// Used for stills.\n" "float3 Step3( float2 uv )\n" "{\n" - "#if 0\n" + "#if DITHER_GENERATE_NOISE\n" " float a = Step2( uv, 0.07 );\n" " float b = Step2( uv, 0.11 );\n" " float c = Step2( uv, 0.13 );\n" @@ -10994,11 +11043,13 @@ static const cgShaderDef_t cg_renderprogs[] = "#else\n" " float3 noise = BlueNoise3( uv, 0.0 );\n" "\n" + "#if 1\n" " noise.x = RemapNoiseTriErp( noise.x );\n" " noise.y = RemapNoiseTriErp( noise.y );\n" " noise.z = RemapNoiseTriErp( noise.z );\n" "\n" " noise = noise * 2.0 - 1.0;\n" + "#endif\n" "\n" " return noise;\n" "#endif\n" @@ -11007,7 +11058,7 @@ static const cgShaderDef_t cg_renderprogs[] = "// Used for temporal dither.\n" "float3 Step3T( float2 uv )\n" "{\n" - "#if 0\n" + "#if DITHER_GENERATE_NOISE\n" " float a = Step2( uv, 0.07 * fract( rpJitterTexOffset.z ) );\n" " float b = Step2( uv, 0.11 * fract( rpJitterTexOffset.z ) );\n" " float c = Step2( uv, 0.13 * fract( rpJitterTexOffset.z ) );\n" @@ -11016,19 +11067,19 @@ static const cgShaderDef_t cg_renderprogs[] = "#else\n" " float3 noise = BlueNoise3( uv, 1.0 );\n" "\n" + "#if 1\n" " noise.x = RemapNoiseTriErp( noise.x );\n" " noise.y = RemapNoiseTriErp( noise.y );\n" " noise.z = RemapNoiseTriErp( noise.z );\n" "\n" " noise = noise * 2.0 - 1.0;\n" + "#endif\n" "\n" " return noise;\n" "#endif\n" "}\n" "\n" "\n" - "#define STEPS 12.0\n" - "\n" "void DitheringPass( inout float4 fragColor )\n" "{\n" " float2 uv = fragment.position.xy * 1.0;\n" @@ -11036,31 +11087,39 @@ static const cgShaderDef_t cg_renderprogs[] = "\n" " float3 color = fragColor.rgb;\n" "\n" - "#if 1\n" + "#if 0\n" "// BOTTOM: Show bands.\n" " if( uv2.y >= 0.975 )\n" " {\n" + " // quantized signal\n" " color = float3( uv2.x );\n" - " color = floor( color * STEPS + Step3( uv ) * 4.0 ) * ( 1.0 / ( STEPS - 1.0 ) );\n" + "\n" + " // dithered still\n" + " //color = floor( color * Dithering_QuantizationSteps + Step3( uv ) * Dithering_NoiseBoost ) * ( 1.0 / ( Dithering_QuantizationSteps - 1.0 ) );\n" " }\n" " else if( uv2.y >= 0.95 )\n" " {\n" + " // quantized signal\n" " color = float3( uv2.x );\n" - " color = floor( color * STEPS ) * ( 1.0 / ( STEPS - 1.0 ) );\n" + " color = floor( color * Dithering_QuantizationSteps ) * ( 1.0 / ( Dithering_QuantizationSteps - 1.0 ) );\n" " }\n" " else if( uv2.y >= 0.925 )\n" " {\n" + " // quantized signal dithered temporally\n" " color = float3( uv2.x );\n" - " color = floor( color * STEPS + Step3T( uv ) * 4.0 ) * ( 1.0 / ( STEPS - 1.0 ) );\n" + " color = floor( color * Dithering_QuantizationSteps + Step3( uv ) * Dithering_NoiseBoost ) * ( 1.0 / ( Dithering_QuantizationSteps - 1.0 ) );\n" " }\n" " // TOP: Show dither texture.\n" " else if( uv2.y >= 0.9 )\n" " {\n" - " color = Step3( uv ) * 1.0 + 0.5;\n" + " color = Step3( uv ) * ( 0.25 * Dithering_NoiseBoost ) + 0.5;\n" " }\n" " else\n" "#endif\n" " {\n" + "\n" + "#if DITHER_IN_LINEAR_SPACE\n" + "\n" " color = Linear3( color );\n" "\n" " // Add grain in linear space.\n" @@ -11069,13 +11128,13 @@ static const cgShaderDef_t cg_renderprogs[] = "#if 1\n" " // Too expensive.\n" " // Helps understand the fast solutions.\n" - " float3 amount = Linear3( Srgb3( color ) + ( 4.0 / STEPS ) ) - color;\n" + " float3 amount = Linear3( Srgb3( color ) + ( Dithering_NoiseBoost / Dithering_QuantizationSteps ) ) - color;\n" "#else\n" " // Less too expensive.\n" " float luma = PhotoLuma( color );\n" "\n" " // Implement this as a texture lookup table.\n" - " float amount = Linear1( Srgb1( luma ) + ( 4.0 / STEPS ) ) - luma;\n" + " float amount = Linear1( Srgb1( luma ) + ( Dithering_NoiseBoost / Dithering_QuantizationSteps ) ) - luma;\n" "#endif\n" "\n" "#else\n" @@ -11085,227 +11144,43 @@ static const cgShaderDef_t cg_renderprogs[] = " // For HDR need saturate() around luma.\n" " float luma = PhotoLuma( color );\n" " float amount = mix(\n" - " Linear1( 4.0 / STEPS ),\n" - " Linear1( ( 4.0 / STEPS ) + 1.0 ) - 1.0,\n" + " Linear1( Dithering_NoiseBoost / Dithering_QuantizationSteps ),\n" + " Linear1( ( Dithering_NoiseBoost / Dithering_QuantizationSteps ) + 1.0 ) - 1.0,\n" " luma );\n" "#else\n" " // Hack 2 (slower?).\n" " // For HDR need saturate() around color in mix().\n" " float3 amount = mix(\n" - " float3( Linear1( 4.0 / STEPS ) ),\n" - " float3( Linear1( ( 4.0 / STEPS ) + 1.0 ) - 1.0 ),\n" + " float3( Linear1( Dithering_NoiseBoost / Dithering_QuantizationSteps ) ),\n" + " float3( Linear1( ( Dithering_NoiseBoost / Dithering_QuantizationSteps ) + 1.0 ) - 1.0 ),\n" " color );\n" "#endif\n" "\n" "#endif\n" - " color += Step3T( uv ) * amount;\n" + " color += Step3T( uv ) * amount;// * Dithering_NoiseBoost;\n" "\n" " // The following represents hardware linear->sRGB xform\n" " // which happens on sRGB formatted render targets,\n" " // except using a lot less bits/pixel.\n" " color = max( float3( 0.0 ), color );\n" " color = Srgb3( color );\n" - " color = floor( color * STEPS ) * ( 1.0 / ( STEPS - 1.0 ) );\n" - " }\n" + " color = floor( color * Dithering_QuantizationSteps ) * ( 1.0 / ( Dithering_QuantizationSteps - 1.0 ) );\n" "\n" - " fragColor.rgb = color;\n" - "}\n" - "\n" - "\n" - "\n" - "#define ANIMATE_NOISE 1\n" - "#define TARGET_BITS 4 // 2^3 = 8 dithered to this many bits\n" - "#define DITHER_IN_LINEAR_SPACE 0\n" - "\n" - "//----------------------------------------------------------------------------------------\n" - "\n" - "/*\n" - "Items of note!\n" - "\n" - "* The blue noise texture sampling should be set to \"nearest\" (not mip map!) and repeat\n" - "\n" - "* you should calculate the uv to use based on the pixel coordinate and the size of the blue noise texture.\n" - " * aka you should tile the blue noise texture across the screen.\n" - " * blue noise actually tiles really well unlike white noise.\n" - "\n" - "* A blue noise texture is \"low discrepancy over space\" which means there are fewer visible patterns than white noise\n" - " * it also gives more even coverage vs white noise. no clumps or voids.\n" - "\n" - "* In an attempt to make it also blue noise over time, you can add the golden ratio and frac it.\n" - " * that makes it lower discrepancy over time, but makes it less good over space.\n" - " * thanks to r4unit for that tip! https://twitter.com/R4_Unit\n" - "\n" - "* Animating the noise in this demo makes the noise basically disappear imo, it's really nice!\n" - "\n" - "For more information:\n" - "\n" - "What the heck is blue nois:\n" - "https://blog.demofox.org/2018/01/30/what-the-heck-is-blue-noise/\n" - "\n" - "Low discrepancy sequences:\n" - "https://blog.demofox.org/2017/05/29/when-random-numbers-are-too-random-low-discrepancy-sequences/\n" - "\n" - "You can get your own blue noise textures here:\n" - "http://momentsingraphics.de/?p=127\n" - "\n" - "*/\n" - "void DitheringPassDemoFox( inout float4 fragColor )\n" - "{\n" - " // texture color\n" - " float2 uv = fragment.position.xy;\n" - " float2 uv2 = fragment.texcoord0;\n" - " float3 fg = fragColor.rgb;\n" - "\n" - " float3 color = fg;\n" - "\n" - "#if 1\n" - " // TOP: show bands\n" - " if( uv2.y >= 0.975 )\n" - " {\n" - " color = float3( uv2.x );\n" - " color = floor( color * STEPS + Step3( uv ) * 4.0 ) * ( 1.0 / ( STEPS - 1.0 ) );\n" - " }\n" - " else if( uv2.y >= 0.95 )\n" - " {\n" - " color = float3( uv2.x );\n" - " color = floor( color * STEPS ) * ( 1.0 / ( STEPS - 1.0 ) );\n" - " }\n" - " else if( uv2.y >= 0.925 )\n" - " {\n" - " color = float3( uv2.x );\n" - " color = floor( color * STEPS + Step3T( uv ) * 4.0 ) * ( 1.0 / ( STEPS - 1.0 ) );\n" - " }\n" - " // BOTTOM: show dither texture\n" - " else if( uv2.y >= 0.9 )\n" - " {\n" - " color = Step3( uv ).rgb;\n" - " }\n" - " else\n" - "#endif\n" - " {\n" - " // right of the screen is dithered using white noise to a fewer number of bits per color channel\n" - " if( uv2.x > 3.0 / 4.0 )\n" - " {\n" - " // get white noise \"random\" number\n" - "#if ANIMATE_NOISE\n" - " float3 whiteNoise = Hash33( float3( uv, rpJitterTexOffset.w / 256.0 ) );\n" "#else\n" - " float3 whiteNoise = Hash33( float3( uv, 0.0 ) );\n" - "#endif\n" - "\n" - " // dither to the specified number of bits, using sRGB conversions if desired\n" - "#if DITHER_IN_LINEAR_SPACE\n" - " fg = pow( fg, float3( 2.2 ) );\n" - "#endif\n" - "\n" - " float scale = exp2( float( TARGET_BITS ) ) - 1.0;\n" - " color = floor( fg * scale + whiteNoise ) / scale;\n" - "\n" - "#if DITHER_IN_LINEAR_SPACE\n" - " color = pow( col, 1.0 / float3( 2.2 ) );\n" - "#endif\n" - " }\n" - " // middle right of the screen is dithered using blue noise to a fewer number of bits per color channel\n" - " else if( uv2.x > 2.0 / 4.0 )\n" - " {\n" - " float3 blueNoise = BlueNoise3( uv, 1.0 );\n" - "\n" - " // dither to the specified number of bits, using sRGB conversions if desired\n" - "#if DITHER_IN_LINEAR_SPACE\n" - " fg = pow( fg, float3( 2.2 ) );\n" - "#endif\n" - "\n" - " float scale = exp2( float( TARGET_BITS ) ) - 1.0;\n" - " color = floor( fg * scale + blueNoise ) / scale;\n" - "\n" - "#if DITHER_IN_LINEAR_SPACE\n" - " color = pow( color, 1.0 / float3( 2.2 ) );\n" - "#endif\n" - " }\n" - " // middle left of the screen is quantized but not dithered\n" - " else if( uv2.x > 1.0 / 4.0 )\n" - " {\n" - " // dither to the specified number of bits, using sRGB conversions if desired\n" - "#if DITHER_IN_LINEAR_SPACE\n" - " fg = pow( fg, float3( 2.2 ) );\n" - "#endif\n" - "\n" - " float scale = exp2( float( TARGET_BITS ) ) - 1.0;\n" - " color = floor( fg * scale + 0.5f ) / scale;\n" - "\n" - "#if DITHER_IN_LINEAR_SPACE\n" - " color = pow( color, 1.0 / float3( 2.2 ) );\n" - "#endif\n" - " }\n" - " // left side of screen is left alone for comparison\n" - " else\n" - " {\n" - " color = fg;\n" - " }\n" - "\n" - " if( abs( uv2.x - 1.0 / 4.0 ) < 0.001 || abs( uv2.x - 2.0 / 4.0 ) < 0.001 || abs( uv2.x - 3.0 / 4.0 ) < 0.001 )\n" - " {\n" - " color = float3( 0.0, 1.0, 0.0 );\n" - " }\n" - " }\n" - "\n" - " fragColor.rgb = color;\n" - "}\n" - "\n" - "void DitheringPassSlim( inout float4 fragColor )\n" - "{\n" - " // texture color\n" - " float2 uv = fragment.position.xy;\n" - " float2 uv2 = fragment.texcoord0;\n" - " float3 fg = fragColor.rgb;\n" - "\n" - " float3 color = fg;\n" "\n" "#if 0\n" - " if( uv2.y >= 0.975 )\n" - " {\n" - " // source signal\n" - " color = float3( uv2.x );\n" - " }\n" - " else if( uv2.y >= 0.95 )\n" - " {\n" - " color = float3( uv2.x );\n" - "\n" - " // quantized signal\n" - " float scale = exp2( float( TARGET_BITS ) ) - 1.0;\n" - " color = floor( color * scale + 0.0f ) / scale;\n" - " }\n" - " else if( uv2.y >= 0.925 )\n" - " {\n" - " // dithered quantized signal\n" - " color = float3( uv2.x );\n" - " color = floor( color * STEPS + Step3T( uv ) * 4.0 ) * ( 1.0 / ( STEPS - 1.0 ) );\n" - " }\n" - " else if( uv2.y >= 0.9 )\n" - " {\n" - " // dither texture\n" - " color = Step3( uv ).rgb;\n" - " }\n" - " else\n" + " if( uv2.x <= 0.5 )\n" + " {\n" + " // quantized but not dithered\n" + " color = floor( 0.5 + color * ( Dithering_QuantizationSteps + Dithering_Wide - 1.0 ) + ( -Dithering_Wide * 0.5 ) ) * ( 1.0 / ( Dithering_QuantizationSteps - 1.0 ) );\n" + " }\n" + " else\n" "#endif\n" - " {\n" - " float3 noise = BlueNoise3( uv, 1.0 );\n" - "\n" - " //noise.x = RemapNoiseTriErp( noise.x );\n" - " //noise.y = RemapNoiseTriErp( noise.y );\n" - " //noise.z = RemapNoiseTriErp( noise.z );\n" - "\n" - " // dither to the specified number of bits, using sRGB conversions if desired\n" - "#if DITHER_IN_LINEAR_SPACE\n" - " fg = pow( fg, float3( 2.2 ) );\n" + " {\n" + " color = floor( 0.5 + color * ( Dithering_QuantizationSteps + Dithering_Wide - 1.0 ) + ( -Dithering_Wide * 0.5 ) + Step3T( uv ) * ( Dithering_Wide ) ) * ( 1.0 / ( Dithering_QuantizationSteps - 1.0 ) );\n" + " }\n" "#endif\n" "\n" - " float scale = exp2( float( TARGET_BITS ) ) - 1.0;\n" - " color = floor( fg * scale + noise ) / scale;\n" - "\n" - "#if DITHER_IN_LINEAR_SPACE\n" - " color = pow( color, 1.0 / float3( 2.2 ) );\n" - "#endif\n" " }\n" "\n" " fragColor.rgb = color;\n" @@ -11332,7 +11207,7 @@ static const cgShaderDef_t cg_renderprogs[] = "#endif\n" "\n" "#if USE_DITHERING\n" - " DitheringPassSlim( color );\n" + " DitheringPass( color );\n" "#endif\n" "\n" " result.color = color;\n"