Improved filmic post processing

This commit is contained in:
Robert Beckebans 2020-05-12 23:38:32 +02:00
parent 1b3378cb94
commit 5dedbc70a6
5 changed files with 240 additions and 490 deletions

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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 );

View file

@ -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;

View file

@ -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"