Skip to content

Commit

Permalink
VHS / Dither post effects
Browse files Browse the repository at this point in the history
  • Loading branch information
sultim-t committed Sep 18, 2023
1 parent 84ac3b2 commit b71b189
Show file tree
Hide file tree
Showing 9 changed files with 358 additions and 0 deletions.
18 changes: 18 additions & 0 deletions Include/RTGL1/RTGL1.h
Original file line number Diff line number Diff line change
Expand Up @@ -943,6 +943,22 @@ typedef struct RgPostEffectCRT
RgBool32 isActive;
} RgPostEffectCRT;

typedef struct RgPostEffectVHS
{
RgBool32 isActive;
float transitionDurationIn;
float transitionDurationOut;
float intensity;
} RgPostEffectVHS;

typedef struct RgPostEffectDither
{
RgBool32 isActive;
float transitionDurationIn;
float transitionDurationOut;
float intensity;
} RgPostEffectDither;

typedef struct RgPostEffectTeleport
{
RgBool32 isActive;
Expand All @@ -966,6 +982,8 @@ typedef struct RgDrawFramePostEffectsParams
const RgPostEffectColorTint* pColorTint;
const RgPostEffectTeleport* pTeleport;
const RgPostEffectCRT* pCRT;
const RgPostEffectVHS* pVHS;
const RgPostEffectDither* pDither;
} RgDrawFramePostEffectsParams;

typedef enum RgMediaType
Expand Down
2 changes: 2 additions & 0 deletions Source/DrawFrameInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,8 @@ namespace detail
.pColorTint = nullptr,
.pTeleport = nullptr,
.pCRT = nullptr,
.pVHS = nullptr,
.pDither = nullptr,
};
};

Expand Down
52 changes: 52 additions & 0 deletions Source/EffectSimple_Instances.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,4 +245,56 @@ struct EffectWaves final : public EffectSimple<EffectWaves_PushConst>
}
};


// ------------------ //


struct EffectVHS_PushConst
{
float intensity;
};

struct EffectVHS final : public EffectSimple< EffectVHS_PushConst >
{
RTGL1_EFFECT_SIMPLE_INHERIT_CONSTRUCTOR( EffectVHS, "EffectVHS" )

bool Setup( const CommonnlyUsedEffectArguments& args, const RgPostEffectVHS* params )
{
if( params == nullptr || params->intensity <= 0.0f )
{
return SetupNull();
}

GetPush().intensity = params->intensity;
return EffectSimple::Setup(
args, params->isActive, params->transitionDurationIn, params->transitionDurationOut );
}
};


// ------------------ //


struct EffectDither_PushConst
{
float intensity;
};

struct EffectDither final : public EffectSimple< EffectDither_PushConst >
{
RTGL1_EFFECT_SIMPLE_INHERIT_CONSTRUCTOR( EffectDither, "EffectDither" )

bool Setup( const CommonnlyUsedEffectArguments& args, const RgPostEffectDither* params )
{
if( params == nullptr || params->intensity <= 0.0f )
{
return SetupNull();
}

GetPush().intensity = params->intensity;
return EffectSimple::Setup(
args, params->isActive, params->transitionDurationIn, params->transitionDurationOut );
}
};

}
2 changes: 2 additions & 0 deletions Source/ShaderManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ static ShaderModuleDefinition G_SHADERS[] =
{ "EffectHueShift", "EfHueShift.comp.spv" },
{ "EffectCrtDemodulateEncode", "EfCrtDemodulateEncode.comp.spv" },
{ "EffectCrtDecode", "EfCrtDecode.comp.spv" },
{ "EffectVHS", "EfVHS.comp.spv" },
{ "EffectDither", "EfDither.comp.spv" },
};

// clang-format on
Expand Down
147 changes: 147 additions & 0 deletions Source/Shaders/EfDither.comp
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// Copyright (c) 2022 Sultim Tsyrendashiev
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#version 460

struct EffectDither_PushConst
{
float intensity;
};

#define EFFECT_PUSH_CONST_T EffectDither_PushConst
#include "EfSimple.inl"


#define DITHER_TEXTURE_SIZE_X 36
#define DITHER_TEXTURE_SIZE_Y 4

float getPattern( vec2 uv )
{
ivec2 at = ivec2( uv.x * DITHER_TEXTURE_SIZE_X, uv.y * DITHER_TEXTURE_SIZE_Y );

// pattern texture is 36x4
at = ivec2( at.x % DITHER_TEXTURE_SIZE_X, at.y % DITHER_TEXTURE_SIZE_Y );

// the pixels with x=32..36 are 1.0
if( at.x >= 32 )
{
return 1.0;
}

// the pixels with x=0..31 are encoded in bit mask; each line is y
const uint pattern[] = {
0x8aaaeff,
0x4555d,
0x2aaabff,
0x15557,
};

// test bit at.x, at.y
return ( pattern[ at.y ] & ( 1 << ( 31 - at.x ) ) ) != 0 ? 1.0 : 0.0;
}


// from https://github.com/jmickle66666666/PSX-Dither-Shader/blob/master/PSX%20Dither.shader (Created by https://github.com/jmickle66666666)
// and https://www.shadertoy.com/view/tlc3DM (Created by BitOfGold in 2019-12-16)


// ported to shaderToy by László Matuska / @BitOfGold
// from here: https://github.com/jmickle66666666/PSX-Dither-Shader/blob/master/PSX%20Dither.shader
// uses Shadertoy's 8x8 bayer dithering pattern instead of the original pattern

// Number of colors. 32 (5 bits) per channel
const vec3 _Colors = vec3(32.0);

float channelError(float col, float colMin, float colMax)
{
float range = abs(colMin - colMax);
float aRange = abs(col - colMin);
return aRange /range;
}

float ditheredChannel(float error, vec2 ditherBlockUV, float ditherSteps)
{
error = floor(error * ditherSteps) / ditherSteps;
vec2 ditherUV = vec2(error + ditherBlockUV.x, ditherBlockUV.y);
return getPattern(ditherUV);
}

/// YUV/RGB color space calculations

vec3 RGBtoYUV(vec3 rgb) {
vec3 yuv;
yuv.r = rgb.r * 0.2126 + 0.7152 * rgb.g + 0.0722 * rgb.b;
yuv.g = (rgb.b - yuv.r) / 1.8556;
yuv.b = (rgb.r - yuv.r) / 1.5748;

// Adjust to work on GPU
yuv.gb += 0.5;

return yuv;
}

vec3 YUVtoRGB(vec3 yuv) {
yuv.gb -= 0.5;
return vec3(
yuv.r * 1.0 + yuv.g * 0.0 + yuv.b * 1.5748,
yuv.r * 1.0 + yuv.g * -0.187324 + yuv.b * -0.468124,
yuv.r * 1.0 + yuv.g * 1.8556 + yuv.b * 0.0);
}

vec3 ditherColor(vec3 col, vec2 uv, ivec2 windowsize) {
vec3 yuv = RGBtoYUV(col);

vec3 col1 = floor(yuv * _Colors) / _Colors;
vec3 col2 = ceil(yuv * _Colors) / _Colors;

// Calculate dither texture UV based on the input texture
float ditherSize = DITHER_TEXTURE_SIZE_Y;
float ditherSteps = DITHER_TEXTURE_SIZE_X / ditherSize;

vec2 ditherBlockUV;
ditherBlockUV.x = mod(uv.x, (ditherSize / windowsize.x));
ditherBlockUV.x /= (ditherSize / windowsize.x);
ditherBlockUV.y = mod(uv.y, (ditherSize / windowsize.y));
ditherBlockUV.y /= (ditherSize / windowsize.y);
ditherBlockUV.x /= ditherSteps;

yuv.x = mix(col1.x, col2.x, ditheredChannel(channelError(yuv.x, col1.x, col2.x), ditherBlockUV, ditherSteps));
yuv.y = mix(col1.y, col2.y, ditheredChannel(channelError(yuv.y, col1.y, col2.y), ditherBlockUV, ditherSteps));
yuv.z = mix(col1.z, col2.z, ditheredChannel(channelError(yuv.z, col1.z, col2.z), ditherBlockUV, ditherSteps));

return(YUVtoRGB(yuv));
}


void main()
{
const ivec2 pix = ivec2( gl_GlobalInvocationID.x, gl_GlobalInvocationID.y );
if( !effect_isPixValid( pix ) )
{
return;
}

vec3 original = effect_loadFromSource( pix );

vec3 c = mix( original,
ditherColor( original, effect_getFramebufUV( pix ), effect_getFramebufSize() ),
getProgress() * push.custom.intensity );
effect_storeToTarget( c, pix );
}
121 changes: 121 additions & 0 deletions Source/Shaders/EfVHS.comp
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// Copyright (c) 2022 Sultim Tsyrendashiev
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#version 460

struct EffectVHS_PushConst
{
float intensity;
};

#define EFFECT_PUSH_CONST_T EffectVHS_PushConst
#include "EfSimple.inl"


// from https://www.shadertoy.com/view/XtBXDt (Created by FMS_Cat in 2015-11-10)


vec3 tex2D( vec2 _p ){
ivec2 pix = ivec2( _p * effect_getFramebufSize());
vec3 col = effect_loadFromSource( pix ).xyz;

if ( 0.5 < abs( _p.x - 0.5 ) ) {
col = vec3( 0.1 );
}
return col;
}

float hash( vec2 _v ){
return fract( sin( dot( _v, vec2( 89.44, 19.36 ) ) ) * 22189.22 );
}

float iHash( vec2 _v, vec2 _r ){
float h00 = hash( vec2( floor( _v * _r + vec2( 0.0, 0.0 ) ) / _r ) );
float h10 = hash( vec2( floor( _v * _r + vec2( 1.0, 0.0 ) ) / _r ) );
float h01 = hash( vec2( floor( _v * _r + vec2( 0.0, 1.0 ) ) / _r ) );
float h11 = hash( vec2( floor( _v * _r + vec2( 1.0, 1.0 ) ) / _r ) );
vec2 ip = vec2( smoothstep( vec2( 0.0, 0.0 ), vec2( 1.0, 1.0 ), mod( _v*_r, 1. ) ) );
return ( h00 * ( 1. - ip.x ) + h10 * ip.x ) * ( 1. - ip.y ) + ( h01 * ( 1. - ip.x ) + h11 * ip.x ) * ip.y;
}

float noise( vec2 _v ){
float sum = 0.;
for( int i=1; i<9; i++ )
{
sum += iHash( _v + vec2( i ), vec2( 2. * pow( 2., float( i ) ) ) ) / pow( 2., float( i ) );
}
return sum;
}

vec3 vhs( vec2 uv, float time ){
vec2 uvn = uv;
vec3 col = vec3( 0.0 );

// tape wave
uvn.x += ( noise( vec2( uvn.y, time ) ) - 0.5 )* 0.005;
uvn.x += ( noise( vec2( uvn.y * 100.0, time * 10.0 ) ) - 0.5 ) * 0.01;

// tape crease
float tcPhase = clamp( ( sin( uvn.y * 8.0 - time * M_PI * 1.2 ) - 0.92 ) * noise( vec2( time ) ), 0.0, 0.01 ) * 10.0;
float tcNoise = max( noise( vec2( uvn.y * 100.0, time * 10.0 ) ) - 0.5, 0.0 );
uvn.x = uvn.x - 0.1 * tcNoise * tcPhase;

// switching noise
float snPhase = smoothstep( 0.03, 0.0, uvn.y );
uvn.y += snPhase * 0.3;
uvn.x += snPhase * ( ( noise( vec2( uv.y * 100.0, time * 10.0 ) ) - 0.5 ) * 0.2 );

col = tex2D( uvn );
col *= 1.0 - tcPhase * 0.5;
col = mix(
col,
col.yzx,
snPhase
);

// bloom
for( float x = -2.0; x < 3.5; x += 1.0 ){
col.xyz += vec3(
tex2D( uvn + vec2( x - 2.0, 0.0 ) * 0.0035 ).x,
tex2D( uvn + vec2( x - 0.0, 0.0 ) * 0.0035 ).y,
tex2D( uvn + vec2( x + 2.0, 0.0 ) * 0.0035 ).z
) * 0.1;
}
col *= 0.6;

// ac beat
col *= 1.0 + clamp( noise( vec2( 0.0, uv.y + time * 0.2 ) ) * 0.6 - 0.25, 0.0, 0.1 );

return col;
}

void main()
{
const ivec2 pix = ivec2( gl_GlobalInvocationID.x, gl_GlobalInvocationID.y );
if( !effect_isPixValid( pix ) )
{
return;
}

vec3 c = mix( effect_loadFromSource( pix ),
vhs( effect_getFramebufUV( pix ), globalUniform.time ),
getProgress() * push.custom.intensity * 0.5 );
effect_storeToTarget( c, pix );
}
8 changes: 8 additions & 0 deletions Source/VulkanDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,14 @@ void RTGL1::VulkanDevice::Render( VkCommandBuffer cmd, const RgDrawFrameInfo& dr
{
accum = effectRadialBlur->Apply( args, accum );
}
if( effectVHS->Setup( args, postef.pVHS ) )
{
accum = effectVHS->Apply( args, accum );
}
if( effectDither->Setup( args, postef.pDither ) )
{
accum = effectDither->Apply( args, accum );
}
}

// draw geometry such as HUD into an upscaled framebuf
Expand Down
Loading

0 comments on commit b71b189

Please sign in to comment.