From cef666f2fd5c9a2731644b63b9dff2eda813f72c Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Thu, 1 Aug 2024 17:23:07 +0200 Subject: [PATCH] Easymode CRT shader works correctly --- neo/renderer/RenderBackend.cpp | 2 +- neo/renderer/RenderProgs.cpp | 2 +- neo/renderer/RenderProgs.h | 6 +- ...curvature.ps.hlsl => crt_aperture.ps.hlsl} | 29 +- ...curvature.vs.hlsl => crt_aperture.vs.hlsl} | 0 neo/shaders/builtin/post/crt_easymode.ps.hlsl | 261 ++++++++++++++++++ neo/shaders/builtin/post/crt_easymode.vs.hlsl | 55 ++++ neo/shaders/shaders.cfg | 4 +- 8 files changed, 349 insertions(+), 10 deletions(-) rename neo/shaders/builtin/post/{crt_zfast_curvature.ps.hlsl => crt_aperture.ps.hlsl} (90%) rename neo/shaders/builtin/post/{crt_zfast_curvature.vs.hlsl => crt_aperture.vs.hlsl} (100%) create mode 100644 neo/shaders/builtin/post/crt_easymode.ps.hlsl create mode 100644 neo/shaders/builtin/post/crt_easymode.vs.hlsl diff --git a/neo/renderer/RenderBackend.cpp b/neo/renderer/RenderBackend.cpp index 64fbd3e7..bdaa001b 100644 --- a/neo/renderer/RenderBackend.cpp +++ b/neo/renderer/RenderBackend.cpp @@ -6509,7 +6509,7 @@ void idRenderBackend::CRTPostProcess() } else { - renderProgManager.BindShader_CrtZFast(); + renderProgManager.BindShader_CrtEasyMode(); } float windowCoordParm[4]; diff --git a/neo/renderer/RenderProgs.cpp b/neo/renderer/RenderProgs.cpp index 0bf867ee..c27f75c9 100644 --- a/neo/renderer/RenderProgs.cpp +++ b/neo/renderer/RenderProgs.cpp @@ -539,7 +539,7 @@ void idRenderProgManager::Init( nvrhi::IDevice* device ) { BUILTIN_POSTPROCESS_RETRO_PSX, "builtin/post/retro_ps1", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, { BUILTIN_CRT_MATTIAS, "builtin/post/crt_mattias", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, { BUILTIN_CRT_NUPIXIE, "builtin/post/crt_newpixie", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, - { BUILTIN_CRT_ZFAST, "builtin/post/crt_zfast_curvature", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, + { BUILTIN_CRT_EASYMODE, "builtin/post/crt_easymode", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, { BUILTIN_SCREEN, "builtin/post/screen", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_DEFAULT }, { BUILTIN_TONEMAP, "builtin/post/tonemap", "", { { "BRIGHTPASS", "0" }, { "HDR_DEBUG", "0"} }, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_DEFAULT }, diff --git a/neo/renderer/RenderProgs.h b/neo/renderer/RenderProgs.h index 5916341a..e196ecbc 100644 --- a/neo/renderer/RenderProgs.h +++ b/neo/renderer/RenderProgs.h @@ -377,7 +377,7 @@ enum BUILTIN_POSTPROCESS_RETRO_PSX, // Sony Playstation 1 BUILTIN_CRT_MATTIAS, BUILTIN_CRT_NUPIXIE, - BUILTIN_CRT_ZFAST, + BUILTIN_CRT_EASYMODE, BUILTIN_SCREEN, BUILTIN_TONEMAP, BUILTIN_BRIGHTPASS, @@ -869,9 +869,9 @@ public: BindShader_Builtin( BUILTIN_CRT_NUPIXIE ); } - void BindShader_CrtZFast() + void BindShader_CrtEasyMode() { - BindShader_Builtin( BUILTIN_CRT_ZFAST ); + BindShader_Builtin( BUILTIN_CRT_EASYMODE ); } void BindShader_Screen() diff --git a/neo/shaders/builtin/post/crt_zfast_curvature.ps.hlsl b/neo/shaders/builtin/post/crt_aperture.ps.hlsl similarity index 90% rename from neo/shaders/builtin/post/crt_zfast_curvature.ps.hlsl rename to neo/shaders/builtin/post/crt_aperture.ps.hlsl index fdedb597..aee8b8a4 100644 --- a/neo/shaders/builtin/post/crt_zfast_curvature.ps.hlsl +++ b/neo/shaders/builtin/post/crt_aperture.ps.hlsl @@ -57,7 +57,7 @@ struct PS_OUT // Set to 0 to use linear filter and gain speed #define ENABLE_LANCZOS 1 -#define RESOLUTION_DIVISOR 2.0 +#define RESOLUTION_DIVISOR 4.0 float4 dilate( float4 col ) { @@ -110,6 +110,17 @@ float mod( float x, float y ) return x - y * floor( x / y ); } +float2 curve( float2 uv, float curvature ) +{ + uv = ( uv - 0.5 ) * curvature; + uv *= 1.1; + uv.x *= 1.0 + pow( ( abs( uv.y ) / 5.0 ), 2.0 ); + uv.y *= 1.0 + pow( ( abs( uv.x ) / 4.0 ), 2.0 ); + uv = ( uv / curvature ) + 0.5; + uv = uv * 0.92 + 0.04; + return uv; +} + void main( PS_IN fragment, out PS_OUT result ) { // revised version from RetroArch @@ -163,11 +174,19 @@ void main( PS_IN fragment, out PS_OUT result ) float4 sourceSize = outputSize; #else float4 sourceSize; - sourceSize.xy = rpWindowCoord.zw / RESOLUTION_DIVISOR; + sourceSize.xy = rpWindowCoord.zw / float2( 4, 4.4 ); //RESOLUTION_DIVISOR; sourceSize.zw = float2( 1.0, 1.0 ) / sourceSize.xy; #endif float2 vTexCoord = fragment.texcoord0.xy; + +#if 0 + if( rpWindowCoord.x > 0.0 ) + { + vTexCoord = curve( vTexCoord, 2.0 ); + } +#endif + float2 dx = float2( sourceSize.z, 0.0 ); float2 dy = float2( 0.0, sourceSize.w ); float2 pix_co = vTexCoord * sourceSize.xy - float2( 0.5, 0.5 ); @@ -201,7 +220,7 @@ void main( PS_IN fragment, out PS_OUT result ) float bright = ( max( col.r, max( col.g, col.b ) ) + luma ) * 0.5; float scan_bright = clamp( bright, params.SCANLINE_BRIGHT_MIN, params.SCANLINE_BRIGHT_MAX ); float scan_beam = clamp( bright * params.SCANLINE_BEAM_WIDTH_MAX, params.SCANLINE_BEAM_WIDTH_MIN, params.SCANLINE_BEAM_WIDTH_MAX ); - float scan_weight = 1.0 - pow( cos( vTexCoord.y * 2.0 * PI * sourceSize.y ) * 0.5 + 0.5, scan_beam ) * params.SCANLINE_STRENGTH; + float scan_weight = 1.0 - pow( cos( vTexCoord.y * 2.0 * PI * sourceSize.y / RESOLUTION_DIVISOR ) * 0.5 + 0.5, scan_beam ) * params.SCANLINE_STRENGTH; float mask = 1.0 - params.MASK_STRENGTH; float2 mod_fac = floor( vTexCoord * outputSize.xy * sourceSize.xy / ( sourceSize.xy * float2( params.MASK_SIZE, params.MASK_DOT_HEIGHT * params.MASK_SIZE ) ) ); @@ -234,5 +253,9 @@ void main( PS_IN fragment, out PS_OUT result ) col *= mask_weight; col = pow( col, _float3( 1.0 / params.GAMMA_OUTPUT ) ); + //col = col2; + //col = _float3( scan_weight ); + //col = float3( scan_bright, scan_beam, scan_weight ); + result.color = float4( col * params.BRIGHT_BOOST, 1.0 ); } diff --git a/neo/shaders/builtin/post/crt_zfast_curvature.vs.hlsl b/neo/shaders/builtin/post/crt_aperture.vs.hlsl similarity index 100% rename from neo/shaders/builtin/post/crt_zfast_curvature.vs.hlsl rename to neo/shaders/builtin/post/crt_aperture.vs.hlsl diff --git a/neo/shaders/builtin/post/crt_easymode.ps.hlsl b/neo/shaders/builtin/post/crt_easymode.ps.hlsl new file mode 100644 index 00000000..aee8b8a4 --- /dev/null +++ b/neo/shaders/builtin/post/crt_easymode.ps.hlsl @@ -0,0 +1,261 @@ +/* + CRT Shader by EasyMode + License: GPL + + A flat CRT shader ideally for 1080p or higher displays. + + Recommended Settings: + + Video + - Aspect Ratio: 4:3 + - Integer Scale: Off + + Shader + - Filter: Nearest + - Scale: Don't Care + + Example RGB Mask Parameter Settings: + + Aperture Grille (Default) + - Dot Width: 1 + - Dot Height: 1 + - Stagger: 0 + + Lottes' Shadow Mask + - Dot Width: 2 + - Dot Height: 1 + - Stagger: 3 +*/ + + +#include + + +// *INDENT-OFF* +Texture2D t_CurrentRender : register( t0 VK_DESCRIPTOR_SET( 0 ) ); +Texture2D t_BlueNoise : register( t1 VK_DESCRIPTOR_SET( 0 ) ); + +SamplerState s_LinearClamp : register(s0 VK_DESCRIPTOR_SET( 1 ) ); +SamplerState s_LinearWrap : register(s1 VK_DESCRIPTOR_SET( 1 ) ); // blue noise 256 + + +struct PS_IN +{ + float4 position : SV_Position; + float2 texcoord0 : TEXCOORD0_centroid; +}; + +struct PS_OUT +{ + float4 color : SV_Target0; +}; +// *INDENT-ON* + +#define TEX2D(c) dilate(t_CurrentRender.Sample( s_LinearClamp, c ).rgba) +#define FIX(c) max(abs(c), 1e-5) + +// Set to 0 to use linear filter and gain speed +#define ENABLE_LANCZOS 1 + +#define RESOLUTION_DIVISOR 4.0 + +float4 dilate( float4 col ) +{ +#if 1 + // FIXME + //float4 x = lerp( _float4( 1.0 ), col, params.DILATION ); + float4 x = lerp( _float4( 1.0 ), col, 1.0 ); + return col * x; +#else + return col; +#endif +} + +float curve_distance( float x, float sharp ) +{ + /* + apply half-circle s-curve to distance for sharper (more pixelated) interpolation + single line formula for Graph Toy: + 0.5 - sqrt(0.25 - (x - step(0.5, x)) * (x - step(0.5, x))) * sign(0.5 - x) + */ + + float x_step = step( 0.5, x ); + float curve = 0.5 - sqrt( 0.25 - ( x - x_step ) * ( x - x_step ) ) * sign( 0.5 - x ); + + return lerp( x, curve, sharp ); +} + +float4x4 get_color_matrix( float2 co, float2 dx ) +{ + return float4x4( TEX2D( co - dx ), TEX2D( co ), TEX2D( co + dx ), TEX2D( co + 2.0 * dx ) ); + + // transpose for HLSL + //m = transpose(m); + //return m; +} + +float3 filter_lanczos( float4 coeffs, float4x4 color_matrix ) +{ + float4 col = mul( color_matrix, coeffs ); + float4 sample_min = min( color_matrix[1], color_matrix[2] ); + float4 sample_max = max( color_matrix[1], color_matrix[2] ); + + col = clamp( col, sample_min, sample_max ); + + return col.rgb; +} + +float mod( float x, float y ) +{ + return x - y * floor( x / y ); +} + +float2 curve( float2 uv, float curvature ) +{ + uv = ( uv - 0.5 ) * curvature; + uv *= 1.1; + uv.x *= 1.0 + pow( ( abs( uv.y ) / 5.0 ), 2.0 ); + uv.y *= 1.0 + pow( ( abs( uv.x ) / 4.0 ), 2.0 ); + uv = ( uv / curvature ) + 0.5; + uv = uv * 0.92 + 0.04; + return uv; +} + +void main( PS_IN fragment, out PS_OUT result ) +{ + // revised version from RetroArch + + struct Params + { + float BRIGHT_BOOST; + float DILATION; + float GAMMA_INPUT; + float GAMMA_OUTPUT; + float MASK_SIZE; + float MASK_STAGGER; + float MASK_STRENGTH; + float MASK_DOT_HEIGHT; + float MASK_DOT_WIDTH; + float SCANLINE_CUTOFF; + float SCANLINE_BEAM_WIDTH_MAX; + float SCANLINE_BEAM_WIDTH_MIN; + float SCANLINE_BRIGHT_MAX; + float SCANLINE_BRIGHT_MIN; + float SCANLINE_STRENGTH; + float SHARPNESS_H; + float SHARPNESS_V; + }; + + Params params; + params.BRIGHT_BOOST = 1.2; + params.DILATION = 1.0; + params.GAMMA_INPUT = 2.0; + params.GAMMA_OUTPUT = 1.8; + params.MASK_SIZE = 1.0; + params.MASK_STAGGER = 0.0; + params.MASK_STRENGTH = 0.8; + params.MASK_DOT_HEIGHT = 1.0; + params.MASK_DOT_WIDTH = 1.0; + params.SCANLINE_CUTOFF = 400.0; + params.SCANLINE_BEAM_WIDTH_MAX = 1.5; + params.SCANLINE_BEAM_WIDTH_MIN = 1.5; + params.SCANLINE_BRIGHT_MAX = 0.65; + params.SCANLINE_BRIGHT_MIN = 0.35; + params.SCANLINE_STRENGTH = 1.0; + params.SHARPNESS_H = 0.5; + params.SHARPNESS_V = 1.0; + + + float4 outputSize; + outputSize.xy = rpWindowCoord.zw; + outputSize.zw = float2( 1.0, 1.0 ) / rpWindowCoord.zw; + +#if 1 + float4 sourceSize = outputSize; +#else + float4 sourceSize; + sourceSize.xy = rpWindowCoord.zw / float2( 4, 4.4 ); //RESOLUTION_DIVISOR; + sourceSize.zw = float2( 1.0, 1.0 ) / sourceSize.xy; +#endif + + float2 vTexCoord = fragment.texcoord0.xy; + +#if 0 + if( rpWindowCoord.x > 0.0 ) + { + vTexCoord = curve( vTexCoord, 2.0 ); + } +#endif + + float2 dx = float2( sourceSize.z, 0.0 ); + float2 dy = float2( 0.0, sourceSize.w ); + float2 pix_co = vTexCoord * sourceSize.xy - float2( 0.5, 0.5 ); + float2 tex_co = ( floor( pix_co ) + float2( 0.5, 0.5 ) ) * sourceSize.zw; + float2 dist = frac( pix_co ); + float curve_x; + float3 col, col2; + +#if ENABLE_LANCZOS + curve_x = curve_distance( dist.x, params.SHARPNESS_H * params.SHARPNESS_H ); + + float4 coeffs = PI * float4( 1.0 + curve_x, curve_x, 1.0 - curve_x, 2.0 - curve_x ); + + coeffs = FIX( coeffs ); + coeffs = 2.0 * sin( coeffs ) * sin( coeffs * 0.5 ) / ( coeffs * coeffs ); + coeffs /= dot( coeffs, _float4( 1.0 ) ); + + col = filter_lanczos( coeffs, get_color_matrix( tex_co, dx ) ); + col2 = filter_lanczos( coeffs, get_color_matrix( tex_co + dy, dx ) ); +#else + curve_x = curve_distance( dist.x, params.SHARPNESS_H ); + + col = lerp( TEX2D( tex_co ).rgb, TEX2D( tex_co + dx ).rgb, curve_x ); + col2 = lerp( TEX2D( tex_co + dy ).rgb, TEX2D( tex_co + dx + dy ).rgb, curve_x ); +#endif + + col = lerp( col, col2, curve_distance( dist.y, params.SHARPNESS_V ) ); + col = pow( col, _float3( params.GAMMA_INPUT / ( params.DILATION + 1.0 ) ) ); + + float luma = dot( float3( 0.2126, 0.7152, 0.0722 ), col ); + float bright = ( max( col.r, max( col.g, col.b ) ) + luma ) * 0.5; + float scan_bright = clamp( bright, params.SCANLINE_BRIGHT_MIN, params.SCANLINE_BRIGHT_MAX ); + float scan_beam = clamp( bright * params.SCANLINE_BEAM_WIDTH_MAX, params.SCANLINE_BEAM_WIDTH_MIN, params.SCANLINE_BEAM_WIDTH_MAX ); + float scan_weight = 1.0 - pow( cos( vTexCoord.y * 2.0 * PI * sourceSize.y / RESOLUTION_DIVISOR ) * 0.5 + 0.5, scan_beam ) * params.SCANLINE_STRENGTH; + + float mask = 1.0 - params.MASK_STRENGTH; + float2 mod_fac = floor( vTexCoord * outputSize.xy * sourceSize.xy / ( sourceSize.xy * float2( params.MASK_SIZE, params.MASK_DOT_HEIGHT * params.MASK_SIZE ) ) ); + int dot_no = int( mod( ( mod_fac.x + mod( mod_fac.y, 2.0 ) * params.MASK_STAGGER ) / params.MASK_DOT_WIDTH, 3.0 ) ); + float3 mask_weight; + + if( dot_no == 0 ) + { + mask_weight = float3( 1.0, mask, mask ); + } + else if( dot_no == 1 ) + { + mask_weight = float3( mask, 1.0, mask ); + } + else + { + mask_weight = float3( mask, mask, 1.0 ); + } + +#if 0 + if( sourceSize.y >= params.SCANLINE_CUTOFF ) + { + scan_weight = 1.0; + } +#endif + + col2 = col.rgb; + col *= _float3( scan_weight ); + col = lerp( col, col2, scan_bright ); + col *= mask_weight; + col = pow( col, _float3( 1.0 / params.GAMMA_OUTPUT ) ); + + //col = col2; + //col = _float3( scan_weight ); + //col = float3( scan_bright, scan_beam, scan_weight ); + + result.color = float4( col * params.BRIGHT_BOOST, 1.0 ); +} diff --git a/neo/shaders/builtin/post/crt_easymode.vs.hlsl b/neo/shaders/builtin/post/crt_easymode.vs.hlsl new file mode 100644 index 00000000..75519fe8 --- /dev/null +++ b/neo/shaders/builtin/post/crt_easymode.vs.hlsl @@ -0,0 +1,55 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). + +Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 BFG Edition Source Code. If not, see . + +In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "global_inc.hlsl" + + +// *INDENT-OFF* +struct VS_IN +{ + float4 position : POSITION; + float2 texcoord : TEXCOORD0; + float4 normal : NORMAL; + float4 tangent : TANGENT; + float4 color : COLOR0; + float4 color2 : COLOR1; +}; + +struct VS_OUT { + float4 position : SV_Position; + float2 texcoord0 : TEXCOORD0_centroid; +}; +// *INDENT-ON* + +void main( VS_IN vertex, out VS_OUT result ) +{ + result.position = vertex.position; + result.position.y = -result.position.y; + + result.texcoord0 = vertex.texcoord; +} \ No newline at end of file diff --git a/neo/shaders/shaders.cfg b/neo/shaders/shaders.cfg index 85eb9198..de3e1843 100644 --- a/neo/shaders/shaders.cfg +++ b/neo/shaders/shaders.cfg @@ -68,8 +68,8 @@ builtin/post/crt_mattias.vs.hlsl -T vs builtin/post/crt_mattias.ps.hlsl -T ps builtin/post/crt_newpixie.vs.hlsl -T vs builtin/post/crt_newpixie.ps.hlsl -T ps -builtin/post/crt_zfast_curvature.vs.hlsl -T vs -builtin/post/crt_zfast_curvature.ps.hlsl -T ps +builtin/post/crt_easymode.vs.hlsl -T vs +builtin/post/crt_easymode.ps.hlsl -T ps builtin/post/screen.vs.hlsl -T vs builtin/post/screen.ps.hlsl -T ps builtin/post/tonemap.vs.hlsl -T vs -D BRIGHTPASS={0,1} -D HDR_DEBUG={0,1}