From befdb8ff730ebb36e28da0235e6bc7e1e83c68cb Mon Sep 17 00:00:00 2001 From: Alex Gunter Date: Mon, 20 Dec 2021 19:33:30 -0600 Subject: [PATCH 01/16] Refactored scanlines and started creating deinterpolation logic --- reshade-shaders/Shaders/crt-royale.fx | 54 ++++----- .../crt-royale/lib/bind-shader-params.fxh | 9 ++ .../lib/helper-functions-and-macros.fxh | 1 + .../crt-royale/lib/scanline-functions.fxh | 59 +++++++-- .../Shaders/crt-royale/lib/tex2Dantialias.fxh | 13 +- .../shaders/blur9fast-horizontal.fxh | 10 +- .../crt-royale/shaders/blur9fast-vertical.fxh | 10 +- .../shaders/crt-royale-blend-frames.fxh | 49 +++++++- .../shaders/crt-royale-bloom-approx.fxh | 10 +- ...t-royale-bloom-horizontal-reconstitute.fxh | 8 +- .../shaders/crt-royale-bloom-vertical.fxh | 8 +- .../shaders/crt-royale-brightpass.fxh | 8 +- ...elds.fxh => crt-royale-electron-beams.fxh} | 77 +++++++++++- .../crt-royale-geometry-aa-last-pass.fxh | 8 +- .../crt-royale-mask-resize-horizontal.fxh | 99 --------------- .../crt-royale-mask-resize-vertical.fxh | 113 ------------------ .../shaders/crt-royale-mask-resize.fxh | 4 +- ...royale-scanlines-horizontal-apply-mask.fxh | 9 +- ...-royale-scanlines-vertical-interlacing.fxh | 101 ---------------- 19 files changed, 238 insertions(+), 412 deletions(-) rename reshade-shaders/Shaders/crt-royale/shaders/{crt-royale-first-pass-linearize-crt-gamma-bob-fields.fxh => crt-royale-electron-beams.fxh} (57%) delete mode 100644 reshade-shaders/Shaders/crt-royale/shaders/crt-royale-mask-resize-horizontal.fxh delete mode 100644 reshade-shaders/Shaders/crt-royale/shaders/crt-royale-mask-resize-vertical.fxh delete mode 100644 reshade-shaders/Shaders/crt-royale/shaders/crt-royale-scanlines-vertical-interlacing.fxh diff --git a/reshade-shaders/Shaders/crt-royale.fx b/reshade-shaders/Shaders/crt-royale.fx index 54b34a1..b254551 100644 --- a/reshade-shaders/Shaders/crt-royale.fx +++ b/reshade-shaders/Shaders/crt-royale.fx @@ -25,9 +25,9 @@ #if !CONTENT_BOX_VISIBLE #include "crt-royale/shaders/content-crop.fxh" - #include "crt-royale/shaders/crt-royale-first-pass-linearize-crt-gamma-bob-fields.fxh" - #include "crt-royale/shaders/crt-royale-scanlines-vertical-interlacing.fxh" - // #include "crt-royale/shaders/crt-royale-scanlines-vertical-interlacing-new.fxh" + // #include "crt-royale/shaders/crt-royale-first-pass-linearize-crt-gamma-bob-fields.fxh" + // #include "crt-royale/shaders/crt-royale-scanlines-vertical-interlacing.fxh" + #include "crt-royale/shaders/crt-royale-electron-beams.fxh" #include "crt-royale/shaders/crt-royale-bloom-approx.fxh" #include "crt-royale/shaders/blur9fast-vertical.fxh" #include "crt-royale/shaders/blur9fast-horizontal.fxh" @@ -62,25 +62,24 @@ technique CRT_Royale RenderTarget = texCrop; } - // crt-royale-first-pass-linearize-crt-gamma-bob-fields.fx + // crt-royale-electron-beams.fx pass linearizeAndBobPass { - VertexShader = vertexShader0; - PixelShader = pixelShader0; + VertexShader = linearizeAndBobVS; + PixelShader = linearizeAndBobPS; RenderTarget = texOrigLinearized; } - // crt-royale-scanlines-vertical-interlacing.fxh - pass verticalBeamPass + pass electronBeamPass { VertexShader = PostProcessVS; - PixelShader = pixelShader1; + PixelShader = scanWithElectronBeams; RenderTarget = texVerticalScanlines; } - pass verticalOffsetPass { + pass beamMisaslignmentPass { VertexShader = PostProcessVS; - PixelShader = verticalOffsetPS; + PixelShader = beamMisaslignmentPS; RenderTarget = texVerticalOffset; } @@ -88,23 +87,23 @@ technique CRT_Royale pass bloomApproxPass { VertexShader = PostProcessVS; - PixelShader = pixelShader2; + PixelShader = approximateBloomPS; RenderTarget = texBloomApprox; } // blur9fast-vertical.fxh pass blurVerticalPass { - VertexShader = vertexShader3; - PixelShader = pixelShader3; + VertexShader = blurVerticalVS; + PixelShader = blurVerticalPS; RenderTarget = texBlurVertical; } // blur9fast-horizontal.fxh pass blurHorizontalPass { - VertexShader = vertexShader4; - PixelShader = pixelShader4; + VertexShader = blurHorizontalVS; + PixelShader = blurHorizontalPS; RenderTarget = texBlurHorizontal; } @@ -116,7 +115,6 @@ technique CRT_Royale RenderTarget = texMaskResizeVertical; } - // crt-royale-mask-resize.fxh pass phosphorMaskResizeHorizontalPass { VertexShader = maskResizeHorizVS; @@ -124,42 +122,42 @@ technique CRT_Royale RenderTarget = texMaskResizeHorizontal; } - // crt-royale-scanlines-horizontal-apply-mask-new.fxh + // crt-royale-scanlines-horizontal-apply-mask.fxh pass phosphorMaskPass { VertexShader = PostProcessVS; - PixelShader = newPixelShader7; + PixelShader = applyPhosphorMaskPS; RenderTarget = texMaskedScanlines; } // crt-royale-brightpass.fxh pass brightpassPass { - VertexShader = vertexShader8; - PixelShader = pixelShader8; + VertexShader = brightpassVS; + PixelShader = brightpassPS; RenderTarget = texBrightpass; } // crt-royale-bloom-vertical.fxh pass bloomVerticalPass { - VertexShader = vertexShader9; - PixelShader = pixelShader9; + VertexShader = bloomVerticalVS; + PixelShader = bloomVerticalPS; RenderTarget = texBloomVertical; } // crt-royale-bloom-horizontal-reconstitute.fxh pass bloomHorizontalPass { - VertexShader = vertexShader10; - PixelShader = pixelShader10; + VertexShader = bloomHorizontalVS; + PixelShader = bloomHorizontalPS; RenderTarget = texBloomHorizontal; } // crt-royale-blend-frames.fxh pass scanlineBlendPass { - VertexShader = PostProcessVS; + VertexShader = lerpScanlinesVS; PixelShader = lerpScanlinesPS; RenderTarget = texBlendScanline; @@ -179,8 +177,8 @@ technique CRT_Royale // crt-royale-geometry-aa-last-pass.fxh pass geometryPass { - VertexShader = vertexShader11; - PixelShader = pixelShader11; + VertexShader = geometryVS; + PixelShader = geometryPS; RenderTarget = texGeometry; } diff --git a/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh b/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh index 98d8494..693ddf0 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh @@ -172,6 +172,15 @@ uniform bool enable_interlacing < // ui_type = "combo"; // ui_items = "No\0Yes\0"; > = true; +uniform int scanline_deinterlacing_mode < + ui_label = "Deinterlacing Mode"; + ui_tooltip = "Selects the deinterlacing algorithm. Choose the setting that looks the best to you. For crt-royale's original appearance, choose None or Weaving."; + ui_type = "combo"; + ui_items = "None\0" + "Weaving\0" + "Bobbed Blending\0" + "Static\0"; +> = 0; uniform float scanline_num_pixels < ui_label = "Scanline Thickness"; ui_type = "slider"; diff --git a/reshade-shaders/Shaders/crt-royale/lib/helper-functions-and-macros.fxh b/reshade-shaders/Shaders/crt-royale/lib/helper-functions-and-macros.fxh index 792f9ff..124ad7c 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/helper-functions-and-macros.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/helper-functions-and-macros.fxh @@ -47,6 +47,7 @@ #define root_cond(c, a, b) float(c) * a + float(!c) * b + //////////////////////// COMMON MATHEMATICAL CONSTANTS /////////////////////// static const float pi = 3.141592653589; diff --git a/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh b/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh index aa33e99..0400ca7 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh @@ -306,6 +306,36 @@ float3 sample_single_scanline_horizontal(const sampler2D tex, tex, prev_texel_hor_uv, uv_step_x, final_weights); } +float3 sample_rgb_scanline( + const sampler2D tex, + const float2 tex_uv, const float2 tex_size, + const float2 texture_size_inv +) { + if (beam_misconvergence) { + const float3 convergence_offsets_rgb_x = get_convergence_offsets_x_vector(); + const float3 convergence_offsets_rgb_y = get_convergence_offsets_y_vector(); + + const float3 offset_u_rgb = convergence_offsets_rgb_x * texture_size_inv.x; + const float3 offset_v_rgb = convergence_offsets_rgb_y * texture_size_inv.y; + + const float2 scanline_uv_r = tex_uv - float2(offset_u_rgb.r, offset_v_rgb.r); + const float2 scanline_uv_g = tex_uv - float2(offset_u_rgb.g, offset_v_rgb.g); + const float2 scanline_uv_b = tex_uv - float2(offset_u_rgb.b, offset_v_rgb.b); + + const float3 sample_r = sample_single_scanline_horizontal( + tex, scanline_uv_r, tex_size, texture_size_inv); + const float3 sample_g = sample_single_scanline_horizontal( + tex, scanline_uv_g, tex_size, texture_size_inv); + const float3 sample_b = sample_single_scanline_horizontal( + tex, scanline_uv_b, tex_size, texture_size_inv); + + return float3(sample_r.r, sample_g.g, sample_b.b); + } + else { + return sample_single_scanline_horizontal(tex, tex_uv, tex_size, texture_size_inv); + } +} + float3 sample_rgb_scanline_horizontal(const sampler2D tex, const float2 tex_uv, const float2 tex_size, const float2 texture_size_inv) @@ -339,7 +369,7 @@ float3 get_bobbed_scanline_sample( const float input_gamma ) { // Sample `scanline_num_pixels` vertically-contiguous pixels and average them. - float3 interpolated_line; + float3 interpolated_line = 0.0; for (int i = 0; i < scanline_num_pixels; i++) { float4 coord = float4(texcoord.x, scanline_start_y + i * v_step_y, 0, 0); interpolated_line += tex2Dlod_linearize(tex, coord, input_gamma).rgb; @@ -359,7 +389,7 @@ float get_curr_scanline_idx( // thickness `scanline_num_pixels` belonging to a single field. const float curr_line_texel_y = floor(texcoord_y * tex_size_y + under_half); - return floor(curr_line_texel_y / scanline_num_pixels); + return floor(curr_line_texel_y / scanline_num_pixels + FIX_ZERO(0.0)); } float2 get_frame_and_line_field_idx(const float curr_scanline_idx) @@ -368,30 +398,33 @@ float2 get_frame_and_line_field_idx(const float curr_scanline_idx) // Also determine which field is being drawn this frame. const float modulus = enable_interlacing + 1.0; - const float frame_field_idx = fmod(frame_count + interlace_bff, modulus); + + const float alternate_between_frames = float(scanline_deinterlacing_mode != 3); + const float frame_field_idx = alternate_between_frames * fmod(frame_count + interlace_bff, modulus); const float line_field_idx = fmod(curr_scanline_idx, modulus); return float2(frame_field_idx, line_field_idx); } -float curr_line_is_wrong_field( - const float texcoord_y, - const float tex_size_y -) { +float curr_line_is_wrong_field(float2 frame_and_line_field_idx) +{ // Return 1.0 if the current scanline is in the current field. // 0.0 otherwise - const float curr_scanline_idx = get_curr_scanline_idx(texcoord_y, tex_size_y); - const float2 frame_and_line_field_idx = get_frame_and_line_field_idx(curr_scanline_idx); return float(frame_and_line_field_idx.x != frame_and_line_field_idx.y); } float curr_line_is_wrong_field(float curr_scanline_idx) { - // Return 1.0 if the current scanline is in the current field. - // 0.0 otherwise - const float2 frame_and_line_field_idx = get_frame_and_line_field_idx(curr_scanline_idx); - return float(frame_and_line_field_idx.x != frame_and_line_field_idx.y); + return curr_line_is_wrong_field(frame_and_line_field_idx); +} + +float curr_line_is_wrong_field( + const float texcoord_y, + const float tex_size_y +) { + const float curr_scanline_idx = get_curr_scanline_idx(texcoord_y, tex_size_y); + return curr_line_is_wrong_field(curr_scanline_idx); } float3 get_beam_strength(float3 dist, float3 color, diff --git a/reshade-shaders/Shaders/crt-royale/lib/tex2Dantialias.fxh b/reshade-shaders/Shaders/crt-royale/lib/tex2Dantialias.fxh index 9881d0d..507c3cc 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/tex2Dantialias.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/tex2Dantialias.fxh @@ -550,8 +550,8 @@ float3 tex2Daa4x(const sampler2D tex, const float2 tex_uv, const float4 ssd_fai = get_subpixel_support_diam_and_final_axis_importance(); const float2 subpixel_support_diameter = ssd_fai.xy; const float2 final_axis_importance = ssd_fai.zw; - const float2 xy_step = float2(1.0,1.0)/grid_size * subpixel_support_diameter; - const float2 xy_start_offset = float2(0.5 - grid_size*0.5,0.5 - grid_size*0.5) * xy_step; + const float2 xy_step = 1.0/grid_size * subpixel_support_diameter; + const float2 xy_start_offset = (0.5 - grid_size*0.5) * xy_step; // Get the xy offset of each sample. Exploit diagonal symmetry: const float2 xy_offset0 = xy_start_offset + float2(2.0, 0.0) * xy_step; const float2 xy_offset1 = xy_start_offset + float2(0.0, 1.0) * xy_step; @@ -563,10 +563,9 @@ float3 tex2Daa4x(const sampler2D tex, const float2 tex_uv, // Get the weight sum to normalize the total to 1.0 later: const float3 half_sum = w0 + w1; const float3 w_sum = half_sum + half_sum.bgr; - const float3 w_sum_inv = float3(1.0,1.0,1.0)/(w_sum); + const float3 w_sum_inv = 1.0/w_sum; // Scale the pixel-space to texture offset matrix by the pixel diameter. - const float2x2 true_pixel_to_tex_uv = - float2x2((pixel_to_tex_uv * aa_pixel_diameter)); + const float2x2 true_pixel_to_tex_uv = pixel_to_tex_uv * aa_pixel_diameter; // Get uv sample offsets, mirror on odd frames if directed, and exploit // diagonal symmetry: const float2 frame_sign = get_frame_sign(frame); @@ -628,6 +627,8 @@ float3 tex2Daa5x(const sampler2D tex, const float2 tex_uv, // Sum weighted samples (weight sum must equal 1.0 for each channel): return w_sum_inv * (w0 * sample0 + w1 * sample1 + w2 * sample2 + w3 * sample3 + w4 * sample4); + + // return (w0 + w1 + w2 + w3 + w4); } float3 tex2Daa6x(const sampler2D tex, const float2 tex_uv, @@ -1375,7 +1376,7 @@ float3 tex2Daa(const sampler2D tex, const float2 tex_uv, return (antialias_level < 0.5) ? tex2D_linearize(tex, tex_uv, input_gamma).rgb : (antialias_level < 3.5) ? tex2Daa_subpixel_weights_only( tex, tex_uv, pixel_to_tex_uv, input_gamma) : - // (antialias_level < 4.5) ? tex2Daa4x(tex, tex_uv, pixel_to_tex_uv, frame, input_gamma) : + (antialias_level < 4.5) ? tex2Daa4x(tex, tex_uv, pixel_to_tex_uv, frame, input_gamma) : (antialias_level < 5.5) ? tex2Daa5x(tex, tex_uv, pixel_to_tex_uv, frame, input_gamma) : (antialias_level < 6.5) ? tex2Daa6x(tex, tex_uv, pixel_to_tex_uv, frame, input_gamma) : (antialias_level < 7.5) ? tex2Daa7x(tex, tex_uv, pixel_to_tex_uv, frame, input_gamma) : diff --git a/reshade-shaders/Shaders/crt-royale/shaders/blur9fast-horizontal.fxh b/reshade-shaders/Shaders/crt-royale/shaders/blur9fast-horizontal.fxh index 22cc568..b99a5e6 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/blur9fast-horizontal.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/blur9fast-horizontal.fxh @@ -49,17 +49,15 @@ #include "shared-objects.fxh" -void vertexShader4( +void blurHorizontalVS( in const uint id : SV_VertexID, out float4 position : SV_Position, out float2 texcoord : TEXCOORD0, out float2 blur_dxdy : TEXCOORD1 ) { - texcoord.x = (id == 2) ? 2.0 : 0.0; - texcoord.y = (id == 1) ? 2.0 : 0.0; - position = float4(texcoord * float2(2, -2) + float2(-1, 1), 0, 1); - + PostProcessVS(id, position, texcoord); + // Get the uv sample distance between output pixels. Blurs are not generic // Gaussian resizers, and correct blurs require: // 1.) OutputSize == InputSize * 2^m, where m is an integer <= 0. @@ -75,7 +73,7 @@ void vertexShader4( blur_dxdy = float2(dxdy.x, 0.0); } -void pixelShader4( +void blurHorizontalPS( in const float4 pos : SV_Position, in const float2 texcoord : TEXCOORD0, in const float2 blur_dxdy : TEXCOORD1, diff --git a/reshade-shaders/Shaders/crt-royale/shaders/blur9fast-vertical.fxh b/reshade-shaders/Shaders/crt-royale/shaders/blur9fast-vertical.fxh index 94bda10..8c7ebab 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/blur9fast-vertical.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/blur9fast-vertical.fxh @@ -49,17 +49,15 @@ #include "shared-objects.fxh" -void vertexShader3( +void blurVerticalVS( in const uint id : SV_VertexID, out float4 position : SV_Position, out float2 texcoord : TEXCOORD0, out float2 blur_dxdy : TEXCOORD1 ) { - texcoord.x = (id == 2) ? 2.0 : 0.0; - texcoord.y = (id == 1) ? 2.0 : 0.0; - position = float4(texcoord * float2(2, -2) + float2(-1, 1), 0, 1); - + PostProcessVS(id, position, texcoord); + // Get the uv sample distance between output pixels. Blurs are not generic // Gaussian resizers, and correct blurs require: // 1.) OutputSize == InputSize * 2^m, where m is an integer <= 0. @@ -75,7 +73,7 @@ void vertexShader3( blur_dxdy = float2(0.0, dxdy.y); } -void pixelShader3( +void blurVerticalPS( in const float4 pos : SV_Position, in const float2 texcoord : TEXCOORD0, in const float2 blur_dxdy : TEXCOORD1, diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-blend-frames.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-blend-frames.fxh index 8a136c6..272a61b 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-blend-frames.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-blend-frames.fxh @@ -4,23 +4,62 @@ #include "../lib/gamma-management.fxh" #include "../lib/scanline-functions.fxh" + +void lerpScanlinesVS( + in const uint id : SV_VertexID, + + out float4 position : SV_Position, + out float2 texcoord : TEXCOORD0, + out float2 v_step : TEXCOORD1 +) { + PostProcessVS(id, position, texcoord); + + v_step = float2(0.0, scanline_num_pixels / TEX_FREEZEFRAME_HEIGHT); +} + + void lerpScanlinesPS( in const float4 pos : SV_Position, in const float2 texcoord : TEXCOORD0, - + in const float2 v_step : TEXCOORD1, + out float4 color : SV_Target ) { - if (enable_interlacing) { + if (enable_interlacing && scanline_deinterlacing_mode == 1) { const float cur_scanline_idx = get_curr_scanline_idx(texcoord.y, CONTENT_HEIGHT_INTERNAL); const float wrong_field = curr_line_is_wrong_field(cur_scanline_idx); - + const float4 cur_line_color = tex2D(samplerBloomHorizontal, texcoord); const float4 cur_line_prev_color = tex2D(samplerFreezeFrame, texcoord); const float4 avg_color = (cur_line_color + cur_line_prev_color) / 2.0; - const float4 raw_out_color = lerp(cur_line_color, avg_color, scanline_blend_strength); - color = encode_output(raw_out_color, lerp(1.0, scanline_blend_gamma, scanline_blend_strength)); + const float use_blend_params = float( + scanline_deinterlacing_mode > 0 && scanline_deinterlacing_mode < 4 + ); + const float blend_strength = scanline_blend_strength * use_blend_params; + const float base_blend_gamma = lerp(1.0, scanline_blend_gamma, use_blend_params); + + const float4 raw_out_color = lerp(cur_line_color, avg_color, blend_strength); + color = encode_output(raw_out_color, lerp(1.0, base_blend_gamma, scanline_blend_strength)); + } + else if (enable_interlacing && scanline_deinterlacing_mode == 2) { + const float cur_scanline_idx = get_curr_scanline_idx(texcoord.y, CONTENT_HEIGHT_INTERNAL); + const float2 frame_and_line_field_idx = get_frame_and_line_field_idx(cur_scanline_idx); + const float wrong_field = curr_line_is_wrong_field(frame_and_line_field_idx); + const float field_is_odd = fmod(cur_scanline_idx, 2); + + const float use_negative_offset = field_is_odd; + const float2 raw_offset = lerp(1, -1, use_negative_offset) * v_step; + const float2 curr_offset = lerp(0, raw_offset, wrong_field); + const float2 prev_offset = lerp(raw_offset, 0, wrong_field); + + const float4 cur_line_color = tex2D(samplerBloomHorizontal, texcoord + curr_offset); + const float4 cur_line_prev_color = tex2D(samplerFreezeFrame, texcoord + prev_offset); + + const float4 avg_color = (cur_line_color + cur_line_prev_color) / 2.0; + const float4 raw_out_color = lerp(cur_line_color, avg_color, wrong_field); + color = encode_output(raw_out_color, scanline_blend_gamma); } else { color = tex2D(samplerBloomHorizontal, texcoord); diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-bloom-approx.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-bloom-approx.fxh index ee96c9c..2c94d78 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-bloom-approx.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-bloom-approx.fxh @@ -147,7 +147,7 @@ float3 tex2Dresize_gaussian4x4(sampler2D tex, float2 tex_uv, float2 dxdy, float2 } -void pixelShader2( +void approximateBloomPS( in const float4 pos : SV_Position, in const float2 texcoord : TEXCOORD0, @@ -329,8 +329,12 @@ void pixelShader2( out_color = float3(out_color_r.r, out_color_g.g, out_color_b.b); } */ - // Encode and output the blurred image: - color = encode_output(float4(tex2D_linearize(samplerOrigLinearized, tex_uv, get_intermediate_gamma())), get_intermediate_gamma()); + + + // Encode and output the blurred image + // Currently the bloom-approx logic is completely disabled + const float4 input_color = tex2D_linearize(samplerOrigLinearized, tex_uv, get_intermediate_gamma()); + color = encode_output(input_color, get_intermediate_gamma()); // color = tex2D(samplerOrigLinearized, tex_uv); // color = encode_output(float4(out_color, 1.0), get_intermediate_gamma()); diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-bloom-horizontal-reconstitute.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-bloom-horizontal-reconstitute.fxh index a53b1d5..bf3f0ae 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-bloom-horizontal-reconstitute.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-bloom-horizontal-reconstitute.fxh @@ -27,7 +27,7 @@ #include "../lib/scanline-functions.fxh" #include "../lib/bloom-functions.fxh" -void vertexShader10( +void bloomHorizontalVS( in const uint id : SV_VertexID, out float4 position : SV_Position, @@ -35,9 +35,7 @@ void vertexShader10( out float2 bloom_dxdy : TEXCOORD1, out float bloom_sigma_runtime : TEXCOORD2 ) { - texcoord.x = (id == 2) ? 2.0 : 0.0; - texcoord.y = (id == 1) ? 2.0 : 0.0; - position = float4(texcoord * float2(2, -2) + float2(-1, 1), 0, 1); + PostProcessVS(id, position, texcoord); const float2 input_size = tex2Dsize(samplerBloomVertical); @@ -56,7 +54,7 @@ void vertexShader10( mask_tile_size_x / mask_triads_per_tile, bloom_diff_thresh_); } -void pixelShader10( +void bloomHorizontalPS( in const float4 pos : SV_Position, in const float2 texcoord : TEXCOORD0, in const float2 bloom_dxdy : TEXCOORD1, diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-bloom-vertical.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-bloom-vertical.fxh index 93e2f77..1e36f88 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-bloom-vertical.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-bloom-vertical.fxh @@ -26,7 +26,7 @@ #include "../lib/phosphor-mask-resizing.fxh" #include "../lib/bloom-functions.fxh" -void vertexShader9( +void bloomVerticalVS( in const uint id : SV_VertexID, out float4 position : SV_Position, @@ -34,9 +34,7 @@ void vertexShader9( out float2 bloom_dxdy : TEXCOORD1, out float bloom_sigma_runtime : TEXCOORD2 ) { - texcoord.x = (id == 2) ? 2.0 : 0.0; - texcoord.y = (id == 1) ? 2.0 : 0.0; - position = float4(texcoord * float2(2, -2) + float2(-1, 1), 0, 1); + PostProcessVS(id, position, texcoord); float2 input_size = tex2Dsize(samplerBrightpass); float2 output_size = TEX_BLOOMVERTICAL_SIZE; @@ -57,7 +55,7 @@ void vertexShader9( } -void pixelShader9( +void bloomVerticalPS( in const float4 pos : SV_Position, in const float2 texcoord : TEXCOORD0, in const float2 bloom_dxdy : TEXCOORD1, diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-brightpass.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-brightpass.fxh index 5262cd5..f8dd5d2 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-brightpass.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-brightpass.fxh @@ -30,16 +30,14 @@ #include "../lib/blur-functions.fxh" -void vertexShader8( +void brightpassVS( in const uint id : SV_VertexID, out float4 position : SV_Position, out float2 texcoord : TEXCOORD0, out float bloom_sigma_runtime : TEXCOORD1 ) { - texcoord.x = (id == 2) ? 2.0 : 0.0; - texcoord.y = (id == 1) ? 2.0 : 0.0; - position = float4(texcoord * float2(2, -2) + float2(-1, 1), 0, 1); + PostProcessVS(id, position, texcoord); float2 output_size = TEX_BRIGHTPASS_SIZE; // Calculate a runtime bloom_sigma in case it's needed: @@ -50,7 +48,7 @@ void vertexShader8( mask_tile_size_x / mask_triads_per_tile, bloom_diff_thresh_); } -void pixelShader8( +void brightpassPS( in const float4 pos : SV_Position, in const float2 texcoord : TEXCOORD0, in const float bloom_sigma_runtime : TEXCOORD1, diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-first-pass-linearize-crt-gamma-bob-fields.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh similarity index 57% rename from reshade-shaders/Shaders/crt-royale/shaders/crt-royale-first-pass-linearize-crt-gamma-bob-fields.fxh rename to reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh index e354868..7f7280f 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-first-pass-linearize-crt-gamma-bob-fields.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh @@ -26,7 +26,7 @@ #include "shared-objects.fxh" -void vertexShader0( +void linearizeAndBobVS( in const uint id : SV_VertexID, out float4 position : SV_Position, @@ -34,9 +34,10 @@ void vertexShader0( out float interlaced : TEXCOORD1, out float2 v_step : TEXCOORD2 ) { - texcoord.x = (id == 2) ? 2.0 : 0.0; - texcoord.y = (id == 1) ? 2.0 : 0.0; - position = float4(texcoord * float2(2, -2) + float2(-1, 1), 0, 1); + PostProcessVS(id, position, texcoord); + // texcoord.x = (id == 2) ? 2.0 : 0.0; + // texcoord.y = (id == 1) ? 2.0 : 0.0; + // position = float4(texcoord * float2(2, -2) + float2(-1, 1), 0, 1); const float2 input_video_size = content_size; @@ -45,7 +46,7 @@ void vertexShader0( v_step = float2(0.0, 1.0 / input_video_size.y); } -void pixelShader0( +void linearizeAndBobPS( in const float4 pos : SV_Position, in const float2 texcoord : TEXCOORD0, in const float interlaced : TEXCOORD1, @@ -101,4 +102,70 @@ void pixelShader0( color = encode_output(float4(in_field_interpolated_line, 1.0), get_intermediate_gamma()); } +} + +void scanWithElectronBeams( + in const float4 position : SV_Position, + in const float2 texcoord : TEXCOORD0, + + out float4 color : SV_Target +) { + const float2 orig_linearized_size = tex2Dsize(samplerOrigLinearized); + + // Calculate {sigma, shape}_range outside of scanline_contrib so it's only + // done once per pixel (not 6 times) with runtime params. Don't reuse the + // vertex shader calculations, so static versions can be constant-folded. + // const float sigma_range = max(beam_max_sigma, beam_min_sigma) - beam_min_sigma; + // const float shape_range = max(beam_max_shape, beam_min_shape) - beam_min_shape; + + const float wrong_field = curr_line_is_wrong_field(texcoord.y, orig_linearized_size.y); + + + // If we're in the current field, draw the beam + // wrong_field is always 0 when we aren't interlacing + if (!wrong_field) { + // Double the intensity when interlacing to maintain the same apparent brightness + const float contrib_factor = enable_interlacing + 1.0; + + + // float beam_center_0 = get_beam_center(texel_0, scanline_idx_0); + // const float2 beam_coord_0 = float2(texcoord.x, beam_center_0 / orig_linearized_size.y); + const float3 scanline_color_0 = tex2D_linearize(samplerOrigLinearized, texcoord, get_intermediate_gamma()).rgb; + + /* + const float3 beam_dist_0 = 0; + const float3 scanline_contrib_0 = get_beam_strength( + beam_dist_0, + scanline_color_0, sigma_range, shape_range + ); + */ + + float3 scanline_intensity = contrib_factor * scanline_color_0; + + // Temporarily auto-dim the output to avoid clipping. + color = encode_output(float4(scanline_intensity * levels_autodim_temp, 1.0), get_intermediate_gamma()); + } + // If we're not in the current field, don't draw the beam + // It's tempting to add a gaussian here to account for bleeding, but it usually ends up + // either doing nothing or making the colors wrong. + else { + color = float4(0, 0, 0, 1); + } +} + +void beamMisaslignmentPS( + in const float4 position : SV_Position, + in const float2 texcoord : TEXCOORD0, + + out float4 color : SV_TARGET +) { + const float2 scanline_texture_size = tex2Dsize(samplerVerticalScanlines); + const float2 scanline_texture_size_inv = 1.0 / scanline_texture_size; + + const float3 offset_sample = sample_rgb_scanline( + samplerVerticalScanlines, texcoord, + scanline_texture_size, scanline_texture_size_inv + ); + + color = float4(offset_sample, 1); } \ No newline at end of file diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-geometry-aa-last-pass.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-geometry-aa-last-pass.fxh index 8c2c714..7f91c4e 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-geometry-aa-last-pass.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-geometry-aa-last-pass.fxh @@ -53,7 +53,7 @@ float2x2 mul_scale(float2 scale, float2x2 mtrx) } -void vertexShader11( +void geometryVS( in const uint id : SV_VertexID, out float4 position : SV_Position, @@ -65,9 +65,7 @@ void vertexShader11( out float3 global_to_local_row1 : TEXCOORD5, out float3 global_to_local_row2 : TEXCOORD6 ) { - texcoord.x = (id == 2) ? 2.0 : 0.0; - texcoord.y = (id == 1) ? 2.0 : 0.0; - position = float4(texcoord * float2(2, -2) + float2(-1, 1), 0, 1); + PostProcessVS(id, position, texcoord); output_size_inv = 1.0 / content_size; @@ -128,7 +126,7 @@ void vertexShader11( eye_pos_local = mul(global_to_local, eye_pos_global); } -void pixelShader11( +void geometryPS( in const float4 position : SV_Position, in const float2 texcoord : TEXCOORD0, in const float2 output_size_inv : TEXCOORD1, diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-mask-resize-horizontal.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-mask-resize-horizontal.fxh deleted file mode 100644 index 3aa0be9..0000000 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-mask-resize-horizontal.fxh +++ /dev/null @@ -1,99 +0,0 @@ -///////////////////////////// GPL LICENSE NOTICE ///////////////////////////// - -// crt-royale: A full-featured CRT shader, with cheese. -// Copyright (C) 2014 TroggleMonkey -// -// crt-royale-reshade: A port of TroggleMonkey's crt-royale from libretro to ReShade. -// Copyright (C) 2020 Alex Gunter -// -// This program 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 2 of the License, or any later version. -// -// This program 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 -// this program; if not, write to the Free Software Foundation, Inc., 59 Temple -// Place, Suite 330, Boston, MA 02111-1307 USA - - -#include "../lib/bind-shader-params.fxh" -#include "../lib/phosphor-mask-resizing.fxh" -#include "shared-objects.fxh" - -void pixelShader6( - in const float4 pos : SV_Position, - in const float2 texcoord : TEXCOORD0, - - out float4 color : SV_Target -) { - float2 tex_uv = texcoord; - // First estimate the viewport size (the user will get the wrong number of - // triads if it's wrong and mask_specify_num_triads is 1.0/true). - const float2 input_size = tex2Dsize(samplerMaskResizeVertical); - // const float2 output_size = tex2Dsize(samplerMaskResizeHorizontal); - const float2 output_size = TEX_MASKHORIZONTAL_SIZE; - // const float2 estimated_viewport_size = output_size / mask_resize_viewport_scale; - const float2 estimated_viewport_size = content_size; - // Find the final size of our resized phosphor mask tiles. We probably - // estimated the viewport size and MASK_RESIZE output size differently last - // pass, so do not swear they were the same. ;) - const float2 mask_resize_tile_size = get_resized_mask_tile_size( - estimated_viewport_size, output_size, true); - - // We'll render resized tiles until filling the output FBO or meeting a - // limit, so compute [wrapped] tile uv coords based on the output uv coords - // and the number of tiles that will fit in the FBO. - const float2 output_tiles_this_pass = output_size / mask_resize_tile_size; - const float2 output_video_uv = tex_uv; - const float2 tile_uv_wrap = output_video_uv * output_tiles_this_pass; - - // Get the texel size of an input tile and related values: - const float2 input_tile_size = float2(min( - mask_resize_src_lut_size.x, input_size.x), mask_resize_tile_size.y); - const float2 tile_size_uv = input_tile_size / input_size; - const float2 input_tiles_per_texture = input_size / input_tile_size; - - // Derive [wrapped] texture uv coords from [wrapped] tile uv coords and - // the tile size in uv coords, and save frac() for the fragment shader. - const float2 src_tex_uv_wrap = tile_uv_wrap * tile_size_uv; - - // Output the values we need, including the magnification scale and step: - //tile_uv_wrap = tile_uv_wrap; - //src_tex_uv_wrap = src_tex_uv_wrap; - const float2 resize_magnification_scale = mask_resize_tile_size / input_tile_size; - const float2 src_dxdy = float2(1.0/input_size.x, 0.0); - //tile_size_uv = tile_size_uv; - //input_tiles_per_texture = input_tiles_per_texture; - - // The input contains one mask tile horizontally and a number vertically. - // Resize the tile horizontally to its final screen size and repeat it - // until drawing at least mask_resize_num_tiles, leaving it unchanged - // vertically. Lanczos-resizing the phosphor mask achieves much sharper - // results than mipmapping, outputting >= mask_resize_num_tiles makes for - // easier tiled sampling later. - #if _PHOSPHOR_MASK_MANUALLY_RESIZE - // Discard unneeded fragments in case our profile allows real branches. - //const float2 tile_uv_wrap = tile_uv_wrap; - if(get_mask_sample_mode() < 0.5 && - max(tile_uv_wrap.x, tile_uv_wrap.y) <= mask_resize_num_tiles) - { - const float2 src_tex_uv = frac(src_tex_uv_wrap); - const float3 pixel_color = downsample_horizontal_sinc_tiled(samplerMaskResizeVertical, - src_tex_uv, input_size, src_dxdy.x, - resize_magnification_scale.x, tile_size_uv.x); - // The input LUT was linear RGB, and so is our output: - color = float4(pixel_color, 1.0); - } - else - { - discard; - } - #else - discard; - // color = float4(1.0,1.0,1.0,1.0); - #endif -} \ No newline at end of file diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-mask-resize-vertical.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-mask-resize-vertical.fxh deleted file mode 100644 index a75ad1a..0000000 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-mask-resize-vertical.fxh +++ /dev/null @@ -1,113 +0,0 @@ -///////////////////////////// GPL LICENSE NOTICE ///////////////////////////// - -// crt-royale: A full-featured CRT shader, with cheese. -// Copyright (C) 2014 TroggleMonkey -// -// crt-royale-reshade: A port of TroggleMonkey's crt-royale from libretro to ReShade. -// Copyright (C) 2020 Alex Gunter -// -// This program 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 2 of the License, or any later version. -// -// This program 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 -// this program; if not, write to the Free Software Foundation, Inc., 59 Temple -// Place, Suite 330, Boston, MA 02111-1307 USA - -#include "../lib/bind-shader-params.fxh" -#include "../lib/phosphor-mask-resizing.fxh" -#include "../lib/texture-settings.fxh" -#include "shared-objects.fxh" - -void pixelShader5( - in const float4 pos : SV_Position, - in const float2 texcoord : TEXCOORD0, - - out float4 color : SV_Target -) { - float2 tex_uv = texcoord.xy; - // First estimate the viewport size (the user will get the wrong number of - // triads if it's wrong and mask_specify_num_triads is 1.0/true). - // const float2 output_size = tex2Dsize(samplerMaskResizeVertical); - const float2 output_size = TEX_MASKVERTICAL_SIZE; - const float viewport_y = CONTENT_HEIGHT; - const float aspect_ratio = geom_aspect_ratio_x / geom_aspect_ratio_y; - // const float2 estimated_viewport_size = float2(viewport_y * aspect_ratio, viewport_y); - const float2 estimated_viewport_size = content_size; - // Estimate the output size of MASK_RESIZE (the next pass). The estimated - // x component shouldn't matter, because we're not using the x result, and - // we're not swearing it's correct (if we did, the x result would influence - // the y result to maintain the tile aspect ratio). - // const float2 estimated_mask_resize_output_size = float2(output_size.y * aspect_ratio, output_size.y); - const float2 estimated_mask_resize_output_size = tex2Dsize(samplerMaskResizeHorizontal); - // Find the final intended [y] size of our resized phosphor mask tiles, - // then the tile size for the current pass (resize y only): - const float2 mask_resize_tile_size = get_resized_mask_tile_size(estimated_viewport_size, estimated_mask_resize_output_size, true); - const float2 pass_output_tile_size = float2(min(mask_size_xy, output_size.x), mask_resize_tile_size.y); - - // We'll render resized tiles until filling the output FBO or meeting a - // limit, so compute [wrapped] tile uv coords based on the output uv coords - // and the number of tiles that will fit in the FBO. - const float2 output_tiles_this_pass = output_size / pass_output_tile_size; - const float2 output_video_uv = tex_uv; // * texture_size / video_size; - const float2 tile_uv_wrap = output_video_uv * output_tiles_this_pass; - - // The input LUT is just a single mask tile, so texture uv coords are the - // same as tile uv coords (save frac() for the fragment shader). The - // magnification scale is also straightforward: - const float2 src_tex_uv_wrap = tile_uv_wrap; - const float2 resize_magnification_scale = pass_output_tile_size / mask_size_xy; - - - // Resize the input phosphor mask tile to the final vertical size it will - // appear on screen. Keep 1x horizontal size if possible (IN.output_size - // >= mask_size_xy), and otherwise linearly sample horizontally - // to fit exactly one tile. Lanczos-resizing the phosphor mask achieves - // much sharper results than mipmapping, and vertically resizing first - // minimizes the total number of taps required. We output a number of - // resized tiles >= mask_resize_num_tiles for easier tiled sampling later. - //const float2 src_tex_uv_wrap = src_tex_uv_wrap; - #if _PHOSPHOR_MASK_MANUALLY_RESIZE - // Discard unneeded fragments in case our profile allows real branches. - // const float2 tile_uv_wrap = src_tex_uv_wrap; - if(get_mask_sample_mode() < 0.5 && - tile_uv_wrap.y <= mask_resize_num_tiles) - { - static const float src_dy = 1.0/mask_size_xy; - const float2 src_tex_uv = frac(src_tex_uv_wrap); - float3 pixel_color; - if(mask_type < 0.5) - { - pixel_color = downsample_vertical_sinc_tiled( - samplerMaskGrille, src_tex_uv, mask_size, - src_dy, resize_magnification_scale.y, 1.0); - } - else if(mask_type < 1.5) - { - pixel_color = downsample_vertical_sinc_tiled( - samplerMaskSlot, src_tex_uv, mask_size, - src_dy, resize_magnification_scale.y, 1.0); - } - else - { - pixel_color = downsample_vertical_sinc_tiled( - samplerMaskShadow, src_tex_uv, mask_size, - src_dy, resize_magnification_scale.y, 1.0); - } - // The input LUT was linear RGB, and so is our output: - color = float4(pixel_color, 1.0); - } - else - { - discard; - } - #else - discard; - // color = float4(1.0, 1.0, 1.0, 1.0); - #endif -} diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-mask-resize.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-mask-resize.fxh index de14802..14dc542 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-mask-resize.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-mask-resize.fxh @@ -32,9 +32,7 @@ void maskResizeVertVS( out float4 source_mask_size_inv_and_tile_size : TEXCOORD1, out float3 downsizing_factor_and_true_tile_size : TEXCOORD2 ) { - texcoord.x = (id == 2) ? 2.0 : 0.0; - texcoord.y = (id == 1) ? 2.0 : 0.0; - position = float4(texcoord * float2(2, -2) + float2(-1, 1), 0, 1); + PostProcessVS(id, position, texcoord); source_mask_size_inv_and_tile_size = float4(1.0 / mask_size, TEX_MASKHORIZONTAL_SIZE); downsizing_factor_and_true_tile_size = get_downsizing_factor_and_true_tile_size(); diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-scanlines-horizontal-apply-mask.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-scanlines-horizontal-apply-mask.fxh index 5851682..c86458a 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-scanlines-horizontal-apply-mask.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-scanlines-horizontal-apply-mask.fxh @@ -27,7 +27,7 @@ #include "shared-objects.fxh" -void newPixelShader7( +void applyPhosphorMaskPS( in const float4 pos : SV_Position, in const float2 texcoord : TEXCOORD0, @@ -47,9 +47,10 @@ void newPixelShader7( // Horizontally sample the current row (a vertically interpolated scanline) // and account for horizontal convergence offsets, given in units of texels. - const float3 scanline_color_dim = sample_rgb_scanline_horizontal( - samplerVerticalOffset, texcoord, - scanline_texture_size, scanline_texture_size_inv); + // const float3 scanline_color_dim = sample_rgb_scanline_horizontal( + // samplerVerticalOffset, texcoord, + // scanline_texture_size, scanline_texture_size_inv); + const float3 scanline_color_dim = tex2D(samplerVerticalOffset, texcoord).rgb; const float auto_dim_factor = levels_autodim_temp; const float2 true_tile_size = get_downsizing_factor_and_true_tile_size().yz; diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-scanlines-vertical-interlacing.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-scanlines-vertical-interlacing.fxh deleted file mode 100644 index 9606159..0000000 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-scanlines-vertical-interlacing.fxh +++ /dev/null @@ -1,101 +0,0 @@ -///////////////////////////// GPL LICENSE NOTICE ///////////////////////////// - -// crt-royale: A full-featured CRT shader, with cheese. -// Copyright (C) 2014 TroggleMonkey -// -// crt-royale-reshade: A port of TroggleMonkey's crt-royale from libretro to ReShade. -// Copyright (C) 2020 Alex Gunter -// -// This program 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 2 of the License, or any later version. -// -// This program 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 -// this program; if not, write to the Free Software Foundation, Inc., 59 Temple -// Place, Suite 330, Boston, MA 02111-1307 USA - - -#include "../lib/bind-shader-params.fxh" -#include "../lib/scanline-functions.fxh" - -#include "shared-objects.fxh" - - -void pixelShader1( - in const float4 position : SV_Position, - in const float2 texcoord : TEXCOORD0, - - out float4 color : SV_Target -) { - const float2 orig_linearized_size = tex2Dsize(samplerOrigLinearized); - - // Calculate {sigma, shape}_range outside of scanline_contrib so it's only - // done once per pixel (not 6 times) with runtime params. Don't reuse the - // vertex shader calculations, so static versions can be constant-folded. - // const float sigma_range = max(beam_max_sigma, beam_min_sigma) - beam_min_sigma; - // const float shape_range = max(beam_max_shape, beam_min_shape) - beam_min_shape; - - const float wrong_field = curr_line_is_wrong_field(texcoord.y, orig_linearized_size.y); - - - // If we're in the current field, draw the beam - // wrong_field is always 0 when we aren't interlacing - if (!wrong_field) { - // Double the intensity when interlacing to maintain the same apparent brightness - const float contrib_factor = enable_interlacing + 1.0; - - - // float beam_center_0 = get_beam_center(texel_0, scanline_idx_0); - // const float2 beam_coord_0 = float2(texcoord.x, beam_center_0 / orig_linearized_size.y); - const float3 scanline_color_0 = tex2D_linearize(samplerOrigLinearized, texcoord, get_intermediate_gamma()).rgb; - - /* - const float3 beam_dist_0 = 0; - const float3 scanline_contrib_0 = get_beam_strength( - beam_dist_0, - scanline_color_0, sigma_range, shape_range - ); - */ - - float3 scanline_intensity = contrib_factor * scanline_color_0; - - // Temporarily auto-dim the output to avoid clipping. - color = encode_output(float4(scanline_intensity * levels_autodim_temp, 1.0), get_intermediate_gamma()); - } - // If we're not in the current field, don't draw the beam - // It's tempting to add a gaussian here to account for bleeding, but it usually ends up - // either doing nothing or making the colors wrong. - else { - color = float4(0, 0, 0, 1); - } -} - -void verticalOffsetPS( - in const float4 position : SV_Position, - in const float2 texcoord : TEXCOORD0, - - out float4 color : SV_Target -) { - if (beam_misconvergence) { - const float3 convergence_offsets_y = get_convergence_offsets_y_vector(); - const float3 offset_v = (convergence_offsets_y * scanline_num_pixels) / TEX_VERTICALSCANLINES_HEIGHT; - - const float2 r_coord = texcoord + float2(0, offset_v.r); - const float2 g_coord = texcoord + float2(0, offset_v.g); - const float2 b_coord = texcoord + float2(0, offset_v.b); - - const float r = tex2D(samplerVerticalScanlines, r_coord).r; - const float g = tex2D(samplerVerticalScanlines, g_coord).g; - const float b = tex2D(samplerVerticalScanlines, b_coord).b; - - color = float4(r, g, b, 1); - } - else { - color = tex2D(samplerVerticalScanlines, texcoord); - } -} \ No newline at end of file From 65fff8d1391bbecba543ce5e71af345eee1efeed Mon Sep 17 00:00:00 2001 From: Alex Gunter Date: Mon, 20 Dec 2021 23:25:47 -0600 Subject: [PATCH 02/16] Restructured pipeline and files, also improved blended weaving algorithm --- README.MD | 22 +- reshade-shaders/Shaders/crt-royale.fx | 48 ++-- .../crt-royale/lib/bind-shader-params.fxh | 4 +- .../crt-royale/lib/scanline-functions.fxh | 4 +- .../shaders/crt-royale-blend-frames.fxh | 36 ++- .../shaders/crt-royale-electron-beams.fxh | 6 +- .../crt-royale-geometry-aa-last-pass.fxh | 6 +- .../shaders/crt-royale-mask-resize.fxh | 117 -------- .../shaders/crt-royale-phosphor-mask.fxh | 271 ++++++++++++++++++ 9 files changed, 342 insertions(+), 172 deletions(-) delete mode 100644 reshade-shaders/Shaders/crt-royale/shaders/crt-royale-mask-resize.fxh create mode 100644 reshade-shaders/Shaders/crt-royale/shaders/crt-royale-phosphor-mask.fxh diff --git a/README.MD b/README.MD index 66e9cb6..0ae6559 100644 --- a/README.MD +++ b/README.MD @@ -45,22 +45,28 @@ For the most part, configuring this implementation of crt-royale should be the s There are a handful of notable differences: 1) Mask Sample Mode "Smooth" has a new setting `Downsampling Sharpness`. You can use it to make Mode "Smooth" look more similar to Mode "Sharp" if you prefer something in between. I find this particularly useful for the Shadow mask. -2) The scanline logic is now user-toggleable instead of autodetected, and you can change the thickness of the scanlines if you want. You can now enable frame blending to smooth out VSync artifacting. +2) The scanline logic is now user-toggleable instead of autodetected, and you can change the thickness of the scanlines if you want. -3) In DX9, the Mask Type is set by a preprocessor definition `phosphor_mask_type`. It can be either 0 for the Grille mask, 1 for the Slot mask, or 2 for the Shadow mask. You can also type "GRILLE", "SLOT", or "SHADOW" if you prefer. +3) You can now choose from a selection of "deinterlacing algorithms," to use the term loosely. The original crt-royale implicitely accomplished this by naively drawing the image on the screen and letting your monitor and eyes do the work. This resulted in image retention, jitter, and combing artifacts. Now you can choose from None (the original behavior), Weaving, Blended Weaving, and Static. All of these work by merging the current frame with the previous frame. + None: the original crt-royale behavior + Weaving: crisp, jitter-free, but noticeable combing artifacts + Blended Weaving: a middleground between None and Weaving + Static: emphasizes the scanlines by choosing the same field for every frame -4) `PHOSPHOR_BLOOM_TRIADS_LARGER_THAN_3_PIXELS` and its siblings are still present. You can choose one or none of them by setting `PHOSPHOR_BLOOM_TRIAD_SIZE_MODE` to a value in range `[0 - 4]`. This is defined in `user-settings.fxh`. +4) In DX9, the Mask Type is set by a preprocessor definition `phosphor_mask_type`. It can be either 0 for the Grille mask, 1 for the Slot mask, or 2 for the Shadow mask. You can also type "GRILLE", "SLOT", or "SHADOW" if you prefer. -5) The same is true for `SIMULATE_CRT_ON_LCD` and its siblings. Set `GAMMA_SIMULATION_MODE` to `[0 - 4]` to control which of those is active. This is defined in `derived-settings-and-constants.fxh`. +5) `PHOSPHOR_BLOOM_TRIADS_LARGER_THAN_3_PIXELS` and its siblings are still present. You can choose one or none of them by setting `PHOSPHOR_BLOOM_TRIAD_SIZE_MODE` to a value in range `[0 - 4]`. This is defined in `user-settings.fxh`. -6) Most of the "RUNTIME_...", "ANISOTROPIC_...", "DRIVERS_ALLOW_...", and other performance-related preprocessor definitions are either hidden from the UI or gone. Some of them are extremely difficult to port correctly. Some of them either don't do anything anymore or never did anything to begin with. And some of them I'll be adding back later, but I'll have to refactor them and the affected code to make them more user-friendly. +6) The same is true for `SIMULATE_CRT_ON_LCD` and its siblings. Set `GAMMA_SIMULATION_MODE` to `[0 - 4]` to control which of those is active. This is defined in `derived-settings-and-constants.fxh`. -7) I've disabled many of the beam settings due to my rewrite of the scanline logic. They currently are unused. +7) Most of the "RUNTIME_...", "ANISOTROPIC_...", "DRIVERS_ALLOW_...", and other performance-related preprocessor definitions are either hidden from the UI or gone. Some of them are extremely difficult to port correctly. Some of them either don't do anything anymore or never did anything to begin with. And some of them I'll be adding back later, but I'll have to refactor them and the affected code to make them more user-friendly. -8) I've exposed `antialias_filter`, `antialias_level`, and `antialias_temporal` as preprocessor definitions. Note that the code dedicated to having `antialias_level` set to `4` is disabled for now because it's currently broken. +8) I've disabled many of the beam settings due to my rewrite of the scanline logic. They currently are unused. + +9) I've exposed `antialias_filter`, `antialias_level`, and `antialias_temporal` as preprocessor definitions. Note that the code dedicated to having `antialias_level` set to `4` is disabled for now because it's currently broken. #### REPORTING DISCREPANCIES Please be aware that, because ReShade and RetroArch shaders work so differently, it won't be possible to replicate the libretro version's output perfectly. There simply are too many low-level differences in too many bits of math to achieve a pixel-perfect port, especially now that I've rewritten the scanline logic to be variable-width. That being said, you should be able to get pretty darn close. -Obviously my top-priority is to make sure there aren't any breaking issues. If a configuration completely breaks the shader or crashes the game, I need to know about it. Similarly, if there's a configuration that runs well in RetroArch but is unplayably laggy in ReShade, I need to know about it. The same is true for configurations that completely screw up color balance in some stupid way (e.g. setting the Scanline Width to 2 used to throw off the brightness/gamma horribly, but 1 and 3+ were alright). Discrepancies that I won't be able to fix include occassional crushing in shadows, the bloom effect being more potent in places, or the scanlines being slightly different. These go back to limitations of ReShade and those aforementioned functional differences. \ No newline at end of file +Obviously my top-priority is to make sure there aren't any breaking issues. If a configuration completely breaks the shader or crashes the game, I need to know about it. Similarly, if there's a configuration that runs well in RetroArch but is unplayably laggy in ReShade, I need to know about it. The same is true for configurations that completely screw up color balance in some stupid way (e.g. setting the Scanline Width to 2 used to throw off the brightness/gamma horribly, but 1 and 3 were alright). Discrepancies that I won't be able to fix include occassional crushing in shadows, the bloom effect being more potent in places, or the scanlines being slightly different. These go back to limitations of ReShade and those aforementioned functional differences. \ No newline at end of file diff --git a/reshade-shaders/Shaders/crt-royale.fx b/reshade-shaders/Shaders/crt-royale.fx index b254551..a995b49 100644 --- a/reshade-shaders/Shaders/crt-royale.fx +++ b/reshade-shaders/Shaders/crt-royale.fx @@ -25,14 +25,11 @@ #if !CONTENT_BOX_VISIBLE #include "crt-royale/shaders/content-crop.fxh" - // #include "crt-royale/shaders/crt-royale-first-pass-linearize-crt-gamma-bob-fields.fxh" - // #include "crt-royale/shaders/crt-royale-scanlines-vertical-interlacing.fxh" #include "crt-royale/shaders/crt-royale-electron-beams.fxh" #include "crt-royale/shaders/crt-royale-bloom-approx.fxh" #include "crt-royale/shaders/blur9fast-vertical.fxh" #include "crt-royale/shaders/blur9fast-horizontal.fxh" - #include "crt-royale/shaders/crt-royale-mask-resize.fxh" - #include "crt-royale/shaders/crt-royale-scanlines-horizontal-apply-mask.fxh" + #include "crt-royale/shaders/crt-royale-phosphor-mask.fxh" #include "crt-royale/shaders/crt-royale-brightpass.fxh" #include "crt-royale/shaders/crt-royale-bloom-vertical.fxh" #include "crt-royale/shaders/crt-royale-bloom-horizontal-reconstitute.fxh" @@ -107,7 +104,27 @@ technique CRT_Royale RenderTarget = texBlurHorizontal; } - // crt-royale-mask-resize.fxh + // crt-royale-blend-frames.fxh + pass scanlineBlendPass + { + VertexShader = lerpScanlinesVS; + PixelShader = lerpScanlinesPS; + + RenderTarget = texBlendScanline; + } + // crt-royale-blend-frames.fxh + pass freezeFramePass + { + VertexShader = PostProcessVS; + PixelShader = freezeFramePS; + + RenderTarget = texFreezeFrame; + + // Explicitly disable clearing render targets + // scanlineBlendPass will not work properly if this ever defaults to true + ClearRenderTargets = false; + } + // crt-royale-phosphor-mask.fxh pass phosphorMaskResizeVerticalPass { VertexShader = maskResizeVertVS; @@ -122,7 +139,6 @@ technique CRT_Royale RenderTarget = texMaskResizeHorizontal; } - // crt-royale-scanlines-horizontal-apply-mask.fxh pass phosphorMaskPass { VertexShader = PostProcessVS; @@ -154,26 +170,6 @@ technique CRT_Royale RenderTarget = texBloomHorizontal; } - // crt-royale-blend-frames.fxh - pass scanlineBlendPass - { - VertexShader = lerpScanlinesVS; - PixelShader = lerpScanlinesPS; - - RenderTarget = texBlendScanline; - } - // crt-royale-blend-frames.fxh - pass freezeFramePass - { - VertexShader = PostProcessVS; - PixelShader = freezeFramePS; - - RenderTarget = texFreezeFrame; - - // Explicitly disable clearing render targets - // scanlineBlendPass will not work properly if this ever defaults to true - ClearRenderTargets = false; - } // crt-royale-geometry-aa-last-pass.fxh pass geometryPass { diff --git a/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh b/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh index 693ddf0..a02fbbc 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh @@ -178,7 +178,7 @@ uniform int scanline_deinterlacing_mode < ui_type = "combo"; ui_items = "None\0" "Weaving\0" - "Bobbed Blending\0" + "Blended Weaving\0" "Static\0"; > = 0; uniform float scanline_num_pixels < @@ -193,7 +193,7 @@ uniform float scanline_blend_strength < ui_tooltip = "Removes VSync artifacting by blending the current and previous frames"; ui_type = "slider"; ui_min = 0.0; - ui_max = 1.0; + ui_max = 5.0; ui_step = 0.01; > = 0.0; uniform float scanline_blend_gamma < diff --git a/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh b/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh index 0400ca7..925a708 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh @@ -400,7 +400,9 @@ float2 get_frame_and_line_field_idx(const float curr_scanline_idx) const float modulus = enable_interlacing + 1.0; const float alternate_between_frames = float(scanline_deinterlacing_mode != 3); - const float frame_field_idx = alternate_between_frames * fmod(frame_count + interlace_bff, modulus); + const float non_alternating_idx = interlace_bff; + const float alternating_idx = fmod(frame_count + interlace_bff, modulus); + const float frame_field_idx = lerp(non_alternating_idx, alternating_idx, alternate_between_frames); const float line_field_idx = fmod(curr_scanline_idx, modulus); return float2(frame_field_idx, line_field_idx); diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-blend-frames.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-blend-frames.fxh index 272a61b..fdcf5bc 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-blend-frames.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-blend-frames.fxh @@ -25,24 +25,31 @@ void lerpScanlinesPS( out float4 color : SV_Target ) { + // Weaving + // Sample texcoord from this frame and the previous frame + // If we're in the correct field, use the current sample + // If we're in the wrong field, average the current and prev samples + // In this case, we're probably averaging a color with 0. if (enable_interlacing && scanline_deinterlacing_mode == 1) { const float cur_scanline_idx = get_curr_scanline_idx(texcoord.y, CONTENT_HEIGHT_INTERNAL); const float wrong_field = curr_line_is_wrong_field(cur_scanline_idx); - const float4 cur_line_color = tex2D(samplerBloomHorizontal, texcoord); + const float4 cur_line_color = tex2D(samplerVerticalOffset, texcoord); const float4 cur_line_prev_color = tex2D(samplerFreezeFrame, texcoord); const float4 avg_color = (cur_line_color + cur_line_prev_color) / 2.0; - - const float use_blend_params = float( - scanline_deinterlacing_mode > 0 && scanline_deinterlacing_mode < 4 - ); - const float blend_strength = scanline_blend_strength * use_blend_params; - const float base_blend_gamma = lerp(1.0, scanline_blend_gamma, use_blend_params); - const float4 raw_out_color = lerp(cur_line_color, avg_color, blend_strength); - color = encode_output(raw_out_color, lerp(1.0, base_blend_gamma, scanline_blend_strength)); + // Multiply by 1.5, so each pair of scanlines has total brightness 2 + const float4 raw_out_color = lerp(1.5*cur_line_color, avg_color, wrong_field); + color = encode_output(raw_out_color, scanline_blend_gamma); } + // Blended Weaving + // Sample texcoord from this frame + // From the previous frame, sample the current scanline's sibling + // Do this by shifting up or down by a line + // If we're in the correct field, use the current sample + // If we're in the wrong field, average the current and prev samples + // In this case, we're averaging two fully illuminated colors else if (enable_interlacing && scanline_deinterlacing_mode == 2) { const float cur_scanline_idx = get_curr_scanline_idx(texcoord.y, CONTENT_HEIGHT_INTERNAL); const float2 frame_and_line_field_idx = get_frame_and_line_field_idx(cur_scanline_idx); @@ -54,15 +61,16 @@ void lerpScanlinesPS( const float2 curr_offset = lerp(0, raw_offset, wrong_field); const float2 prev_offset = lerp(raw_offset, 0, wrong_field); - const float4 cur_line_color = tex2D(samplerBloomHorizontal, texcoord + curr_offset); - const float4 cur_line_prev_color = tex2D(samplerFreezeFrame, texcoord + prev_offset); + const float4 cur_line_color = tex2D(samplerVerticalOffset, texcoord + curr_offset); + const float4 prev_line_color = tex2D(samplerFreezeFrame, texcoord + prev_offset); - const float4 avg_color = (cur_line_color + cur_line_prev_color) / 2.0; + const float4 avg_color = (cur_line_color + prev_line_color) / 2.0; const float4 raw_out_color = lerp(cur_line_color, avg_color, wrong_field); color = encode_output(raw_out_color, scanline_blend_gamma); } + // No temporal blending else { - color = tex2D(samplerBloomHorizontal, texcoord); + color = tex2D(samplerVerticalOffset, texcoord); } } @@ -72,5 +80,5 @@ void freezeFramePS( out float4 color : SV_Target ) { - color = tex2D(samplerBloomHorizontal, texcoord); + color = tex2D(samplerVerticalOffset, texcoord); } \ No newline at end of file diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh index 7f7280f..cf37d33 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh @@ -125,7 +125,11 @@ void scanWithElectronBeams( // wrong_field is always 0 when we aren't interlacing if (!wrong_field) { // Double the intensity when interlacing to maintain the same apparent brightness - const float contrib_factor = enable_interlacing + 1.0; + const float interlacing_factor = enable_interlacing * float( + scanline_deinterlacing_mode != 1 && + scanline_deinterlacing_mode != 2 + ); + const float contrib_factor = interlacing_factor + 1.0; // float beam_center_0 = get_beam_center(texel_0, scanline_idx_0); diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-geometry-aa-last-pass.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-geometry-aa-last-pass.fxh index 7f91c4e..572d561 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-geometry-aa-last-pass.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-geometry-aa-last-pass.fxh @@ -194,18 +194,18 @@ void geometryPS( if(antialias_level > 0.5 && (geom_mode > 0.5 || any(bool2((geom_overscan.x != 1.0), (geom_overscan.y != 1.0))))) { // Sample the input with antialiasing (due to sharp phosphors, etc.): - raw_color = tex2Daa(samplerBlendScanline, tex_uv, pixel_to_tex_uv, float(frame_count), get_intermediate_gamma()); + raw_color = tex2Daa(samplerBloomHorizontal, tex_uv, pixel_to_tex_uv, float(frame_count), get_intermediate_gamma()); } else if(antialias_level > 0.5 && need_subpixel_aa) { // Sample at each subpixel location: raw_color = tex2Daa_subpixel_weights_only( - samplerBlendScanline, tex_uv, pixel_to_tex_uv, get_intermediate_gamma()); + samplerBloomHorizontal, tex_uv, pixel_to_tex_uv, get_intermediate_gamma()); } else { - raw_color = tex2D_linearize(samplerBlendScanline, tex_uv, get_intermediate_gamma()).rgb; + raw_color = tex2D_linearize(samplerBloomHorizontal, tex_uv, get_intermediate_gamma()).rgb; } // Dim borders and output the final result: diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-mask-resize.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-mask-resize.fxh deleted file mode 100644 index 14dc542..0000000 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-mask-resize.fxh +++ /dev/null @@ -1,117 +0,0 @@ -///////////////////////////// GPL LICENSE NOTICE ///////////////////////////// - -// crt-royale-reshade: A port of TroggleMonkey's crt-royale from libretro to ReShade. -// Copyright (C) 2020 Alex Gunter -// -// This program 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 2 of the License, or any later version. -// -// This program 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 -// this program; if not, write to the Free Software Foundation, Inc., 59 Temple -// Place, Suite 330, Boston, MA 02111-1307 USA - - -#include "../lib/bind-shader-params.fxh" -#include "../lib/phosphor-mask-resizing.fxh" -#include "shared-objects.fxh" - -static const int num_sinc_lobes = mask_sinc_lobes; - -void maskResizeVertVS( - in const uint id : SV_VertexID, - - out float4 position : SV_Position, - out float2 texcoord : TEXCOORD0, - - out float4 source_mask_size_inv_and_tile_size : TEXCOORD1, - out float3 downsizing_factor_and_true_tile_size : TEXCOORD2 -) { - PostProcessVS(id, position, texcoord); - - source_mask_size_inv_and_tile_size = float4(1.0 / mask_size, TEX_MASKHORIZONTAL_SIZE); - downsizing_factor_and_true_tile_size = get_downsizing_factor_and_true_tile_size(); -} - -void maskResizeVertPS( - in const float4 pos : SV_Position, - in const float2 texcoord : TEXCOORD0, - - in const float4 source_mask_size_inv_and_tile_size : TEXCOORD1, - in const float3 downsizing_factor_and_true_tile_size : TEXCOORD2, - - out float4 color : SV_Target -) { - const float2 source_mask_size_inv = source_mask_size_inv_and_tile_size.xy; - const float2 tile_size = source_mask_size_inv_and_tile_size.zw; - const float downsizing_factor = downsizing_factor_and_true_tile_size.x; - const float2 true_tile_size = downsizing_factor_and_true_tile_size.yz; - - if (mask_sample_mode_desired > 0.5 || texcoord.y * tile_size.y >= true_tile_size.y) { - color = float4(0, 0, 0, 0); - } - else if (mask_type == 0) { - color = lanczos_downsample_vert( - samplerMaskGrille, source_mask_size_inv, - texcoord, downsizing_factor, num_sinc_lobes - ); - } - else if (mask_type == 2) { - color = lanczos_downsample_vert( - samplerMaskShadow, source_mask_size_inv, - texcoord, downsizing_factor, num_sinc_lobes - ); - } - else { - color = lanczos_downsample_vert( - samplerMaskSlot, source_mask_size_inv, - texcoord, downsizing_factor, num_sinc_lobes - ); - } -} - -void maskResizeHorizVS( - in const uint id : SV_VertexID, - - out float4 position : SV_Position, - out float2 texcoord : TEXCOORD0, - - out float4 source_mask_size_inv_and_tile_size : TEXCOORD1, - out float3 downsizing_factor_and_true_tile_size : TEXCOORD2 -) { - texcoord.x = (id == 2) ? 2.0 : 0.0; - texcoord.y = (id == 1) ? 2.0 : 0.0; - position = float4(texcoord * float2(2, -2) + float2(-1, 1), 0, 1); - - source_mask_size_inv_and_tile_size = float4(1.0 / TEX_MASKVERTICAL_SIZE, TEX_MASKHORIZONTAL_SIZE); - downsizing_factor_and_true_tile_size = get_downsizing_factor_and_true_tile_size(); -} - -void maskResizeHorizPS( - in const float4 pos : SV_Position, - in const float2 texcoord : TEXCOORD0, - in const float4 source_mask_size_inv_and_tile_size : TEXCOORD1, - in const float3 downsizing_factor_and_true_tile_size : TEXCOORD2, - - out float4 color : SV_Target -) { - const float2 source_mask_size_inv = source_mask_size_inv_and_tile_size.xy; - const float2 tile_size = source_mask_size_inv_and_tile_size.zw; - const float downsizing_factor = downsizing_factor_and_true_tile_size.x; - const float2 true_tile_size = downsizing_factor_and_true_tile_size.yz; - - if (mask_sample_mode_desired > 0.5 || texcoord.x * tile_size.x >= true_tile_size.x) { - color = float4(0, 0, 0, 0); - } - else { - color = lanczos_downsample_horiz( - samplerMaskResizeVertical, source_mask_size_inv, - texcoord, downsizing_factor, num_sinc_lobes - ); - } -} \ No newline at end of file diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-phosphor-mask.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-phosphor-mask.fxh new file mode 100644 index 0000000..0481fcb --- /dev/null +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-phosphor-mask.fxh @@ -0,0 +1,271 @@ +///////////////////////////// GPL LICENSE NOTICE ///////////////////////////// + +// crt-royale-reshade: A port of TroggleMonkey's crt-royale from libretro to ReShade. +// Copyright (C) 2020 Alex Gunter +// +// This program 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 2 of the License, or any later version. +// +// This program 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 +// this program; if not, write to the Free Software Foundation, Inc., 59 Temple +// Place, Suite 330, Boston, MA 02111-1307 USA + + +#include "../lib/bind-shader-params.fxh" +#include "../lib/gamma-management.fxh" +#include "../lib/phosphor-mask-resizing.fxh" +#include "../lib/texture-settings.fxh" + +#include "shared-objects.fxh" + + +static const int num_sinc_lobes = mask_sinc_lobes; + + +void maskResizeVertVS( + in const uint id : SV_VertexID, + + out float4 position : SV_Position, + out float2 texcoord : TEXCOORD0, + + out float4 source_mask_size_inv_and_tile_size : TEXCOORD1, + out float3 downsizing_factor_and_true_tile_size : TEXCOORD2 +) { + PostProcessVS(id, position, texcoord); + + source_mask_size_inv_and_tile_size = float4(1.0 / mask_size, TEX_MASKHORIZONTAL_SIZE); + downsizing_factor_and_true_tile_size = get_downsizing_factor_and_true_tile_size(); +} + +void maskResizeVertPS( + in const float4 pos : SV_Position, + in const float2 texcoord : TEXCOORD0, + + in const float4 source_mask_size_inv_and_tile_size : TEXCOORD1, + in const float3 downsizing_factor_and_true_tile_size : TEXCOORD2, + + out float4 color : SV_Target +) { + const float2 source_mask_size_inv = source_mask_size_inv_and_tile_size.xy; + const float2 tile_size = source_mask_size_inv_and_tile_size.zw; + const float downsizing_factor = downsizing_factor_and_true_tile_size.x; + const float2 true_tile_size = downsizing_factor_and_true_tile_size.yz; + + if (mask_sample_mode_desired > 0.5 || texcoord.y * tile_size.y >= true_tile_size.y) { + color = float4(0, 0, 0, 0); + } + else if (mask_type == 0) { + color = lanczos_downsample_vert( + samplerMaskGrille, source_mask_size_inv, + texcoord, downsizing_factor, num_sinc_lobes + ); + } + else if (mask_type == 2) { + color = lanczos_downsample_vert( + samplerMaskShadow, source_mask_size_inv, + texcoord, downsizing_factor, num_sinc_lobes + ); + } + else { + color = lanczos_downsample_vert( + samplerMaskSlot, source_mask_size_inv, + texcoord, downsizing_factor, num_sinc_lobes + ); + } +} + +void maskResizeHorizVS( + in const uint id : SV_VertexID, + + out float4 position : SV_Position, + out float2 texcoord : TEXCOORD0, + + out float4 source_mask_size_inv_and_tile_size : TEXCOORD1, + out float3 downsizing_factor_and_true_tile_size : TEXCOORD2 +) { + texcoord.x = (id == 2) ? 2.0 : 0.0; + texcoord.y = (id == 1) ? 2.0 : 0.0; + position = float4(texcoord * float2(2, -2) + float2(-1, 1), 0, 1); + + source_mask_size_inv_and_tile_size = float4(1.0 / TEX_MASKVERTICAL_SIZE, TEX_MASKHORIZONTAL_SIZE); + downsizing_factor_and_true_tile_size = get_downsizing_factor_and_true_tile_size(); +} + + +void maskResizeHorizPS( + in const float4 pos : SV_Position, + in const float2 texcoord : TEXCOORD0, + in const float4 source_mask_size_inv_and_tile_size : TEXCOORD1, + in const float3 downsizing_factor_and_true_tile_size : TEXCOORD2, + + out float4 color : SV_Target +) { + const float2 source_mask_size_inv = source_mask_size_inv_and_tile_size.xy; + const float2 tile_size = source_mask_size_inv_and_tile_size.zw; + const float downsizing_factor = downsizing_factor_and_true_tile_size.x; + const float2 true_tile_size = downsizing_factor_and_true_tile_size.yz; + + if (mask_sample_mode_desired > 0.5 || texcoord.x * tile_size.x >= true_tile_size.x) { + color = float4(0, 0, 0, 0); + } + else { + color = lanczos_downsample_horiz( + samplerMaskResizeVertical, source_mask_size_inv, + texcoord, downsizing_factor, num_sinc_lobes + ); + } +} + + +void applyPhosphorMaskPS( + in const float4 pos : SV_Position, + in const float2 texcoord : TEXCOORD0, + + out float4 color : SV_Target +) { + const float2 scanline_texture_size = tex2Dsize(samplerBlendScanline); + // const float2 output_size = tex2Dsize(samplerMaskedScanlines); + const float2 output_size = TEX_MASKEDSCANLINES_SIZE; + + // Our various input textures use different coords. + const float2 scanline_texture_size_inv = 1.0 / scanline_texture_size; + + // This pass: Sample (misconverged?) scanlines to the final horizontal + // resolution, apply halation (bouncing electrons), and apply the phosphor + // mask. Fake a bloom if requested. Unless we fake a bloom, the output + // will be dim from the scanline auto-dim, mask dimming, and low gamma. + + // Horizontally sample the current row (a vertically interpolated scanline) + // and account for horizontal convergence offsets, given in units of texels. + // const float3 scanline_color_dim = sample_rgb_scanline_horizontal( + // samplerVerticalOffset, texcoord, + // scanline_texture_size, scanline_texture_size_inv); + const float3 scanline_color_dim = tex2D(samplerBlendScanline, texcoord).rgb; + const float auto_dim_factor = levels_autodim_temp; + + const float2 true_tile_size = get_downsizing_factor_and_true_tile_size().yz; + const float2 tiles_per_screen = content_size / true_tile_size; + + float3 phosphor_mask_sample; + if(mask_sample_mode_desired > 0.5) + { + const float2 tile_uv_wrap = texcoord * tiles_per_screen; + phosphor_mask_sample = samplePhosphorMask(tile_uv_wrap).rgb; + } + else + { + const float2 tile_uv_wrap = frac(texcoord * tiles_per_screen); + const float2 tile_uv_crop = tile_uv_wrap * true_tile_size / TEX_MASKHORIZONTAL_SIZE; + + // Sample the resized mask, and avoid tiling artifacts: + phosphor_mask_sample = tex2D(samplerMaskResizeHorizontal, tile_uv_crop).rgb; + } + + // Sample the halation texture (auto-dim to match the scanlines), and + // account for both horizontal and vertical convergence offsets, given + // in units of texels horizontally and same-field scanlines vertically: + const float3 halation_color = tex2D_linearize(samplerBlurHorizontal, texcoord, get_intermediate_gamma()).rgb; + + // Apply halation: Halation models electrons flying around under the glass + // and hitting the wrong phosphors (of any color). It desaturates, so + // average the halation electrons to a scalar. Reduce the local scanline + // intensity accordingly to conserve energy. + const float halation_intensity_dim_scalar = dot(halation_color, auto_dim_factor / float3(3, 3, 3)); + const float3 halation_intensity_dim = halation_intensity_dim_scalar * float3(1, 1, 1); + const float3 electron_intensity_dim = lerp(scanline_color_dim, halation_intensity_dim, halation_weight); + + // Apply the phosphor mask: + const float3 phosphor_emission_dim = electron_intensity_dim * phosphor_mask_sample; + + #ifdef PHOSPHOR_BLOOM_FAKE + // The BLOOM_APPROX pass approximates a blurred version of a masked + // and scanlined image. It's usually used to compute the brightpass, + // but we can also use it to fake the bloom stage entirely. Caveats: + // 1.) A fake bloom is conceptually different, since we're mixing in a + // fully blurred low-res image, and the biggest implication are: + // 2.) If mask_amplify is incorrect, results deteriorate more quickly. + // 3.) The inaccurate blurring hurts quality in high-contrast areas. + // 4.) The bloom_underestimate_levels parameter seems less sensitive. + // Reverse the auto-dimming and amplify to compensate for mask dimming: + #define PHOSPHOR_BLOOM_FAKE_WITH_SIMPLE_BLEND + #ifdef PHOSPHOR_BLOOM_FAKE_WITH_SIMPLE_BLEND + static const float blur_contrast = 1.05; + #else + static const float blur_contrast = 1.0; + #endif + const float mask_amplify = get_mask_amplify(); + const float undim_factor = 1.0/auto_dim_factor; + const float3 phosphor_emission = + phosphor_emission_dim * undim_factor * mask_amplify; + // Get a phosphor blur estimate, accounting for convergence offsets: + const float3 electron_intensity = electron_intensity_dim * undim_factor; + const float3 phosphor_blur_approx_soft = tex2D_linearize( + samplerBloomApprox, texcoord, get_intermediate_gamma()).rgb; + const float3 phosphor_blur_approx = lerp(phosphor_blur_approx_soft, + electron_intensity, 0.1) * blur_contrast; + // We could blend between phosphor_emission and phosphor_blur_approx, + // solving for the minimum blend_ratio that avoids clipping past 1.0: + // 1.0 >= total_intensity + // 1.0 >= phosphor_emission * (1.0 - blend_ratio) + + // phosphor_blur_approx * blend_ratio + // blend_ratio = (phosphor_emission - 1.0)/ + // (phosphor_emission - phosphor_blur_approx); + // However, this blurs far more than necessary, because it aims for + // full brightness, not minimal blurring. To fix it, base blend_ratio + // on a max area intensity only so it varies more smoothly: + const float3 phosphor_blur_underestimate = + phosphor_blur_approx * bloom_underestimate_levels; + const float3 area_max_underestimate = + phosphor_blur_underestimate * mask_amplify; + #ifdef PHOSPHOR_BLOOM_FAKE_WITH_SIMPLE_BLEND + const float3 blend_ratio_temp = + (area_max_underestimate - float3(1.0, 1.0, 1.0)) / + (area_max_underestimate - phosphor_blur_underestimate); + #else + // Try doing it like an area-based brightpass. This is nearly + // identical, but it's worth toying with the code in case I ever + // find a way to make it look more like a real bloom. (I've had + // some promising textures from combining an area-based blend ratio + // for the phosphor blur and a more brightpass-like blend-ratio for + // the phosphor emission, but I haven't found a way to make the + // brightness correct across the whole color range, especially with + // different bloom_underestimate_levels values.) + const float desired_triad_size = lerp(mask_triad_size_desired, + output_size.x/mask_num_triads_desired, + mask_specify_num_triads); + const float bloom_sigma = get_min_sigma_to_blur_triad( + desired_triad_size, bloom_diff_thresh); + const float center_weight = get_center_weight(bloom_sigma); + const float3 max_area_contribution_approx = + max(float3(0.0, 0.0, 0.0), phosphor_blur_approx - + center_weight * phosphor_emission); + const float3 area_contrib_underestimate = + bloom_underestimate_levels * max_area_contribution_approx; + const float3 blend_ratio_temp = + ((float3(1.0, 1.0, 1.0) - area_contrib_underestimate) / + area_max_underestimate - float3(1.0, 1.0, 1.0)) / (center_weight - 1.0); + #endif + // Clamp blend_ratio in case it's out-of-range, but be SUPER careful: + // min/max/clamp are BIZARRELY broken with lerp (optimization bug?), + // and this redundant sequence avoids bugs, at least on nVidia cards: + const float3 blend_ratio_clamped = max(clamp(blend_ratio_temp, 0.0, 1.0), 0.0); + const float3 blend_ratio = lerp(blend_ratio_clamped, float3(1.0,1.0,1.0), bloom_excess); + // Blend the blurred and unblurred images: + const float3 phosphor_emission_unclipped = + lerp(phosphor_emission, phosphor_blur_approx, blend_ratio); + // Simulate refractive diffusion by reusing the halation sample. + const float3 pixel_color = lerp(phosphor_emission_unclipped, + halation_color, diffusion_weight); + #else + const float3 pixel_color = phosphor_emission_dim; + #endif + // Encode if necessary, and output. + + color = encode_output(float4(pixel_color, 1.0), get_intermediate_gamma()); +} \ No newline at end of file From 833d6f57f8277d3660c1db7630fc6149716f0b12 Mon Sep 17 00:00:00 2001 From: Alex Gunter Date: Wed, 22 Dec 2021 12:50:09 -0600 Subject: [PATCH 03/16] Re-added some of the gaussian scanline logic, with some options to turn it off --- reshade-shaders/Shaders/crt-royale.fx | 4 +- .../crt-royale/lib/bind-shader-params.fxh | 128 +++++++++++------ .../crt-royale/lib/scanline-functions.fxh | 39 +++++- .../shaders/crt-royale-electron-beams.fxh | 131 ++++++++++++++++-- 4 files changed, 239 insertions(+), 63 deletions(-) diff --git a/reshade-shaders/Shaders/crt-royale.fx b/reshade-shaders/Shaders/crt-royale.fx index a995b49..c34d575 100644 --- a/reshade-shaders/Shaders/crt-royale.fx +++ b/reshade-shaders/Shaders/crt-royale.fx @@ -69,8 +69,8 @@ technique CRT_Royale } pass electronBeamPass { - VertexShader = PostProcessVS; - PixelShader = scanWithElectronBeams; + VertexShader = fancyScanWithElectronBeamsVS; + PixelShader = fancyScanWithElectronBeamsPS; RenderTarget = texVerticalScanlines; } diff --git a/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh b/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh index a02fbbc..ac9eda7 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh @@ -52,6 +52,7 @@ static const float gba_gamma = 3.5; // Irrelevant but necessary to define. uniform int mask_type < ui_label = "Mask Type"; ui_tooltip = "Selects the phosphor shape"; + ui_category = "Phosphor Mask"; ui_type = "combo"; ui_items = "Grille\0" "Slot\0" @@ -72,6 +73,7 @@ static const float gba_gamma = 3.5; // Irrelevant but necessary to define. uniform int mask_sample_mode_desired < ui_label = "Mask Sample Mode"; ui_tooltip = "Selects the phosphor downsampling method"; + ui_category = "Phosphor Mask"; ui_type = "combo"; ui_items = "Smooth (Lanczos)\0" "Sharp (Point)\0" @@ -80,6 +82,7 @@ uniform int mask_sample_mode_desired < uniform float lanczos_weight_at_center < ui_label = "Downsampling Sharpness"; ui_tooltip = "Tunes the sharpness of the Smooth Mask Sample Mode"; + ui_category = "Phosphor Mask"; ui_type = "slider"; ui_min = 0.1; ui_max = 50.0; @@ -88,6 +91,7 @@ uniform float lanczos_weight_at_center < uniform int mask_specify_num_triads < ui_label = "Mask Size Param"; ui_tooltip = "Switch between using Mask Triad Size or Mask Num Triads"; + ui_category = "Phosphor Mask"; ui_type = "combo"; ui_items = "Triad Width\0" "Num Triads Across\0"; @@ -95,6 +99,7 @@ uniform int mask_specify_num_triads < uniform float mask_triad_size_desired < ui_label = "Mask Triad Width"; ui_tooltip = "The width of a triad"; + ui_category = "Phosphor Mask"; ui_type = "slider"; ui_min = 1.0; ui_max = 18.0; @@ -103,6 +108,7 @@ uniform float mask_triad_size_desired < uniform float mask_num_triads_desired < ui_label = "Mask Num Triads Across"; ui_tooltip = "The number of triads in the viewport (horizontally)"; + ui_category = "Phosphor Mask"; ui_type = "drag"; ui_min = 1.0; ui_max = 1280.0; @@ -113,6 +119,7 @@ uniform float mask_num_triads_desired < uniform float crt_gamma < ui_label = "CRT Gamma"; ui_tooltip = "The gamma-level of the original content"; + ui_category = "Colors and Effects"; ui_type = "slider"; ui_min = 1.0; ui_max = 5.0; @@ -121,6 +128,7 @@ uniform float crt_gamma < uniform float lcd_gamma < ui_label = "LCD Gamma"; ui_tooltip = "The gamma-level of your display"; + ui_category = "Colors and Effects"; ui_type = "slider"; ui_min = 1.0; ui_max = 5.0; @@ -128,6 +136,7 @@ uniform float lcd_gamma < > = lcd_gamma_static; uniform float levels_contrast < ui_label = "Levels Contrast"; + ui_category = "Colors and Effects"; ui_type = "slider"; ui_min = 0.0; ui_max = 4.0; @@ -136,6 +145,7 @@ uniform float levels_contrast < uniform float halation_weight < ui_label = "Halation"; ui_tooltip = "Desaturation due to eletrons exciting the wrong phosphors"; + ui_category = "Colors and Effects"; ui_type = "slider"; ui_min = 0.0; ui_max = 1.0; @@ -144,6 +154,7 @@ uniform float halation_weight < uniform float diffusion_weight < ui_label = "Diffusion"; ui_tooltip = "Blurring due to refraction from the screen's glass"; + ui_category = "Colors and Effects"; ui_type = "slider"; ui_min = 0.0; ui_max = 1.0; @@ -152,6 +163,7 @@ uniform float diffusion_weight < uniform float bloom_underestimate_levels < ui_label = "Bloom Underestimation"; ui_tooltip = "Scale the bloom effect's intensity"; + ui_category = "Colors and Effects"; ui_type = "drag"; ui_min = FIX_ZERO(0.0); ui_step = 0.01; @@ -159,22 +171,36 @@ uniform float bloom_underestimate_levels < uniform float bloom_excess < ui_label = "Bloom Excess"; ui_tooltip = "Extra bloom applied to all colors"; + ui_category = "Colors and Effects"; ui_type = "slider"; ui_min = 0.0; ui_max = 1.0; ui_step = 0.01; > = bloom_excess_static; +uniform float2 aa_subpixel_r_offset_runtime < + ui_label = "AA Subpixel R Offet XY"; + ui_category = "Colors and Effects"; + ui_type = "drag"; + ui_min = -0.5; + ui_max = 0.5; + ui_step = 0.01; +> = aa_subpixel_r_offset_static; + +static const float aa_cubic_c = aa_cubic_c_static; +static const float aa_gauss_sigma = aa_gauss_sigma_static; // ==== INTERLACING ==== uniform bool enable_interlacing < ui_label = "Enable Interlacing"; + ui_category = "Interlacing"; // ui_type = "combo"; // ui_items = "No\0Yes\0"; > = true; uniform int scanline_deinterlacing_mode < ui_label = "Deinterlacing Mode"; - ui_tooltip = "Selects the deinterlacing algorithm. Choose the setting that looks the best to you. For crt-royale's original appearance, choose None or Weaving."; + ui_tooltip = "Selects the deinterlacing algorithm. For crt-royale's original appearance, choose None."; + ui_category = "Interlacing"; ui_type = "combo"; ui_items = "None\0" "Weaving\0" @@ -183,54 +209,34 @@ uniform int scanline_deinterlacing_mode < > = 0; uniform float scanline_num_pixels < ui_label = "Scanline Thickness"; + ui_category = "Interlacing"; ui_type = "slider"; ui_min = 1.0; - ui_max = 10.0; + ui_max = 30.0; ui_step = 1.0; > = 2.0; -uniform float scanline_blend_strength < - ui_label = "Scanline Blend Strength"; - ui_tooltip = "Removes VSync artifacting by blending the current and previous frames"; - ui_type = "slider"; - ui_min = 0.0; - ui_max = 5.0; - ui_step = 0.01; -> = 0.0; uniform float scanline_blend_gamma < ui_label = "Scanline Blend Gamma"; ui_tooltip = "Nudge this if Scanline Blend Strength changes your colors too much"; + ui_category = "Interlacing"; ui_type = "slider"; ui_min = 0.01; ui_max = 5.0; ui_step = 0.01; -> = 0.93; +> = 1.0; uniform bool interlace_bff < // ui_type = "combo"; ui_label = "Draw Back-Field First"; ui_tooltip = "Draw odd-numbered scanlines first (often has no effect)"; + ui_category = "Interlacing"; // ui_items = "No\0Yes\0"; > = interlace_bff_static; -uniform float3 convergence_offset_x < - ui_label = "Convergence Offset X RGB"; - ui_tooltip = "Shift the color channels horizontally"; - ui_type = "drag"; - ui_min = -4; - ui_max = 4; - ui_step = 0.05; -> = 0; -uniform float3 convergence_offset_y < - ui_label = "Convergence Offset Y RGB"; - ui_tooltip = "Shift the color channels vertically"; - ui_type = "drag"; - ui_min = -4; - ui_max = 4; - ui_step = 0.05; -> = 0; // ==== GEOMETRY ==== uniform int geom_mode_runtime < ui_label = "Geom Mode"; ui_tooltip = "Select screen curvature"; + ui_category = "Screen Geometry"; ui_type = "combo"; ui_items = "Flat\0" "Spherical\0" @@ -240,6 +246,7 @@ uniform int geom_mode_runtime < uniform float geom_radius < ui_label = "Geom Radius"; ui_tooltip = "Select screen curvature radius"; + ui_category = "Screen Geometry"; ui_type = "slider"; ui_min = 1.0 / (2.0 * pi); ui_max = 1024; @@ -247,6 +254,7 @@ uniform float geom_radius < > = geom_radius_static; uniform float geom_view_dist < ui_label = "Geom View Distance"; + ui_category = "Screen Geometry"; ui_type = "slider"; ui_min = 0.5; ui_max = 1024; @@ -254,6 +262,7 @@ uniform float geom_view_dist < > = geom_view_dist_static; uniform float2 geom_tilt_angle < ui_label = "Geom Tilt Angle XY"; + ui_category = "Screen Geometry"; ui_type = "drag"; ui_min = -pi; ui_max = pi; @@ -261,32 +270,23 @@ uniform float2 geom_tilt_angle < > = geom_tilt_angle_static; uniform float2 geom_aspect_ratio < ui_label = "Geom Aspect Ratio XY"; + ui_category = "Screen Geometry"; ui_type = "drag"; ui_min = 1.0; ui_step = 0.01; > = float2(geom_aspect_ratio_static, 1); uniform float2 geom_overscan < ui_label = "Geom Overscan XY"; + ui_category = "Screen Geometry"; ui_type = "drag"; ui_min = FIX_ZERO(0.0); ui_step = 0.01; > = geom_overscan_static; -// ==== ANTI-ALIASING ==== -uniform float2 aa_subpixel_r_offset_runtime < - ui_label = "AA Subpixel R Offet XY"; - ui_type = "drag"; - ui_min = -0.5; - ui_max = 0.5; - ui_step = 0.01; -> = aa_subpixel_r_offset_static; - -static const float aa_cubic_c = aa_cubic_c_static; -static const float aa_gauss_sigma = aa_gauss_sigma_static; - // ==== BORDER ==== uniform float border_size < ui_label = "Border Size"; + ui_category = "Screen Border"; ui_type = "slider"; ui_min = 0.0; ui_max = 0.5; @@ -294,12 +294,14 @@ uniform float border_size < > = border_size_static; uniform float border_darkness < ui_label = "Border Darkness"; + ui_category = "Screen Border"; ui_type = "drag"; ui_min = 0.0; ui_step = 0.01; > = border_darkness_static; uniform float border_compress < ui_label = "Border Compress"; + ui_category = "Screen Border"; ui_type = "drag"; ui_min = 0.0; ui_step = 0.01; @@ -307,15 +309,41 @@ uniform float border_compress < // ==== ELECTRON BEAM ==== -static const float beam_min_sigma = beam_min_sigma_static; -static const float beam_max_sigma = beam_max_sigma_static; +// static const float beam_min_sigma = beam_min_sigma_static; +// static const float beam_max_sigma = beam_max_sigma_static; static const float beam_spot_power = beam_spot_power_static; -static const float beam_min_shape = beam_min_shape_static; -static const float beam_max_shape = beam_max_shape_static; +// static const float beam_min_shape = beam_min_shape_static; +// static const float beam_max_shape = beam_max_shape_static; static const float beam_shape_power = beam_shape_power_static; -/* + +uniform float3 convergence_offset_x < + ui_label = "Convergence Offset X RGB"; + ui_tooltip = "Shift the color channels horizontally"; + ui_category = "Electron Beam"; + ui_type = "drag"; + ui_min = -4; + ui_max = 4; + ui_step = 0.05; +> = 0; +uniform float3 convergence_offset_y < + ui_label = "Convergence Offset Y RGB"; + ui_tooltip = "Shift the color channels vertically"; + ui_category = "Electron Beam"; + ui_type = "drag"; + ui_min = -4; + ui_max = 4; + ui_step = 0.05; +> = 0; +uniform int beam_shape_mode < + ui_lable = "Beam Shape Mode"; + ui_category = "Electron Beam"; + ui_type = "combo"; + ui_items = "Digital\0" + "Gaussian\0"; +> = 0; uniform float beam_min_sigma < ui_label = "Beam Min Sigma"; + ui_category = "Electron Beam"; ui_type = "drag"; ui_min = 0.0; ui_step = 0.01; @@ -323,18 +351,22 @@ uniform float beam_min_sigma < uniform float beam_max_sigma < ui_label = "Beam Max Sigma"; ui_tooltip = "Must be >= Beam Min Sigma"; + ui_category = "Electron Beam"; ui_type = "drag"; ui_min = 0.0; ui_step = 0.01; > = beam_max_sigma_static; +/* uniform float beam_spot_power < ui_label = "Beam Spot Power"; ui_type = "drag"; ui_min = 0.0; ui_step = 0.01; > = beam_spot_power_static; +*/ uniform float beam_min_shape < ui_label = "Beam Min Shape"; + ui_category = "Electron Beam"; ui_type = "drag"; ui_min = 0.0; ui_step = 0.01; @@ -342,10 +374,12 @@ uniform float beam_min_shape < uniform float beam_max_shape < ui_label = "Beam Max Shape"; ui_tooltip = "Must be >= Beam Min Shape"; + ui_category = "Electron Beam"; ui_type = "drag"; ui_min = 0.0; ui_step = 0.01; > = beam_max_shape_static; +/* uniform float beam_shape_power < ui_label = "Beam Shape Power"; ui_type = "drag"; @@ -356,14 +390,17 @@ uniform float beam_shape_power < uniform int beam_horiz_filter < ui_label = "Beam Horiz Filter"; ui_tooltip = "Default is Quilez"; + ui_category = "Electron Beam"; ui_type = "combo"; - ui_items = "Quilez (Fast)\0" + ui_items = "None\0" + "Quilez (Fast)\0" "Gaussian (Tunable)\0" "Lanczos (Sharp)\0"; > = beam_horiz_filter_static; uniform float beam_horiz_sigma < ui_label = "Beam Horiz Sigma"; ui_tooltip = "Requires Gaussian Horiz Filter"; + ui_category = "Electron Beam"; ui_type = "slider"; ui_min = 0.01; ui_max = 0.67; @@ -371,6 +408,7 @@ uniform float beam_horiz_sigma < > = beam_horiz_sigma_static; uniform float beam_horiz_linear_rgb_weight < ui_label = "Beam Horiz Linear RGB Weight"; + ui_category = "Electron Beam"; ui_type = "slider"; ui_min = 0.0; ui_max = 1.0; diff --git a/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh b/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh index 925a708..cd8063e 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh @@ -269,8 +269,7 @@ float3 sample_single_scanline_horizontal(const sampler2D tex, // Snap to the previous texel and get sample dists from 2/4 nearby texels: const float2 curr_texel = tex_uv * tex_size; // Use under_half to fix a rounding bug right around exact texel locations. - const float2 prev_texel = - floor(curr_texel - under_half) + 0.5; + const float2 prev_texel = floor(curr_texel - under_half) + 0.5; const float2 prev_texel_hor = float2(prev_texel.x, curr_texel.y); const float2 prev_texel_hor_uv = prev_texel_hor * texture_size_inv; const float prev_dist = curr_texel.x - prev_texel_hor.x; @@ -278,14 +277,18 @@ float3 sample_single_scanline_horizontal(const sampler2D tex, 1.0 - prev_dist, 2.0 - prev_dist); // Get Quilez, Lanczos2, or Gaussian resize weights for 2/4 nearby texels: float4 weights; - if(beam_horiz_filter < 0.5) + if (beam_horiz_filter < 0.5) { + // None: + weights = float4(0, 1, 0, 0); + } + else if(beam_horiz_filter < 1.5) { // Quilez: const float x = sample_dists.y; const float w2 = x*x*x*(x*(x*6.0 - 15.0) + 10.0); weights = float4(0.0, 1.0 - w2, w2, 0.0); } - else if(beam_horiz_filter < 1.5) + else if(beam_horiz_filter < 2.5) { // Gaussian: float inner_denom_inv = 1.0/(2.0*beam_horiz_sigma*beam_horiz_sigma); @@ -322,17 +325,24 @@ float3 sample_rgb_scanline( const float2 scanline_uv_g = tex_uv - float2(offset_u_rgb.g, offset_v_rgb.g); const float2 scanline_uv_b = tex_uv - float2(offset_u_rgb.b, offset_v_rgb.b); + const float4 sample_r = tex2D(tex, scanline_uv_r); + const float4 sample_g = tex2D(tex, scanline_uv_g); + const float4 sample_b = tex2D(tex, scanline_uv_b); + + /* const float3 sample_r = sample_single_scanline_horizontal( tex, scanline_uv_r, tex_size, texture_size_inv); const float3 sample_g = sample_single_scanline_horizontal( tex, scanline_uv_g, tex_size, texture_size_inv); const float3 sample_b = sample_single_scanline_horizontal( tex, scanline_uv_b, tex_size, texture_size_inv); + */ return float3(sample_r.r, sample_g.g, sample_b.b); } else { - return sample_single_scanline_horizontal(tex, tex_uv, tex_size, texture_size_inv); + return tex2D(tex, tex_uv).rgb; + // return sample_single_scanline_horizontal(tex, tex_uv, tex_size, texture_size_inv); } } @@ -392,6 +402,22 @@ float get_curr_scanline_idx( return floor(curr_line_texel_y / scanline_num_pixels + FIX_ZERO(0.0)); } +float get_scanline_center( + const float line_texel_y, + const float scanline_idx +) { + const float scanline_start_y = scanline_idx * scanline_num_pixels; + + const float half_num_pixels = scanline_num_pixels / 2; + const float half_size = floor(half_num_pixels + under_half); + const float num_pixels_is_even = float(half_size >= half_num_pixels); + + const float upper_center = scanline_start_y + half_size; + const float shift = num_pixels_is_even * float(line_texel_y > upper_center); + + return upper_center + shift; +} + float2 get_frame_and_line_field_idx(const float curr_scanline_idx) { // Given a scanline index, determine which field it belongs to. @@ -432,7 +458,7 @@ float curr_line_is_wrong_field( float3 get_beam_strength(float3 dist, float3 color, const float sigma_range, const float shape_range) { - // See scanline_gaussian integral_contrib() for detailed comments! + // See scanline_gaussian_integral_contrib() for detailed comments! // gaussian sample = 1/(sigma*sqrt(2*pi)) * e**(-(x**2)/(2*sigma**2)) const float3 sigma = get_gaussian_sigma(color, sigma_range); // Avoid repeated divides: @@ -444,5 +470,4 @@ float3 get_beam_strength(float3 dist, float3 color, } - #endif // _SCANLINE_FUNCTIONS_H \ No newline at end of file diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh index cf37d33..54d64ff 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh @@ -35,15 +35,9 @@ void linearizeAndBobVS( out float2 v_step : TEXCOORD2 ) { PostProcessVS(id, position, texcoord); - // texcoord.x = (id == 2) ? 2.0 : 0.0; - // texcoord.y = (id == 1) ? 2.0 : 0.0; - // position = float4(texcoord * float2(2, -2) + float2(-1, 1), 0, 1); - const float2 input_video_size = content_size; - - // Detect interlacing: 1.0 = true, 0.0 = false. interlaced = enable_interlacing; - v_step = float2(0.0, 1.0 / input_video_size.y); + v_step = float2(0.0, 1.0 / content_size.y); } void linearizeAndBobPS( @@ -62,7 +56,9 @@ void linearizeAndBobPS( // Sample the current line and an average of the previous/next line; // tex2D_linearize will decode CRT gamma. Don't bother branching: float curr_scanline_idx = get_curr_scanline_idx(texcoord.y, content_size.y); - float curr_scanline_start_y = curr_scanline_idx * scanline_num_pixels / content_size.y; + float curr_scanline_start_y = ( + curr_scanline_idx * scanline_num_pixels + TEXCOORD_OFFSET + ) / content_size.y; float3 in_field_interpolated_line = get_bobbed_scanline_sample( samplerCrop, texcoord, curr_scanline_start_y, v_step.y, @@ -104,7 +100,121 @@ void linearizeAndBobPS( } } -void scanWithElectronBeams( +void fancyScanWithElectronBeamsVS( + in const uint id : SV_VertexID, + + out float4 position : SV_Position, + out float2 texcoord : TEXCOORD0, + out float2 v_step : TEXCOORD1 +) { + PostProcessVS(id, position, texcoord); + + v_step = float2(0.0, 1.0 / tex2Dsize(samplerOrigLinearized).y); +} + + +void fancyScanWithElectronBeamsPS( + in const float4 position : SV_Position, + in const float2 texcoord : TEXCOORD0, + in const float2 v_step : TEXCOORD1, + + out float4 color : SV_Target +) { + const float2 orig_linearized_size = tex2Dsize(samplerOrigLinearized); + + const float wrong_field = curr_line_is_wrong_field(texcoord.y, orig_linearized_size.y); + + if (beam_shape_mode == 0) { + // If we're in the current field, draw the beam + // wrong_field is always 0 when we aren't interlacing + if (!wrong_field) { + // Double the intensity when interlacing to maintain the same apparent brightness + const float interlacing_brightness_factor = enable_interlacing * float( + scanline_deinterlacing_mode != 1 && + scanline_deinterlacing_mode != 2 + ); + const float contrib_factor = interlacing_brightness_factor + 1.0; + + // const float3 scanline_color = tex2D_linearize(samplerOrigLinearized, texcoord, get_intermediate_gamma()).rgb; + const float3 scanline_color = sample_single_scanline_horizontal( + samplerOrigLinearized, + texcoord, orig_linearized_size, + 1 / orig_linearized_size + ); + + float3 scanline_intensity = contrib_factor * scanline_color; + + // Temporarily auto-dim the output to avoid clipping. + color = encode_output(float4(scanline_intensity * levels_autodim_temp, 1.0), get_intermediate_gamma()); + } + // If we're not in the current field, don't draw the beam + // It's tempting to add a gaussian here to account for bleeding, but it usually ends up + // either doing nothing or making the colors wrong. + else { + color = float4(0, 0, 0, 1); + } + } + else { + // Calculate {sigma, shape}_range outside of scanline_contrib so it's only + // done once per pixel (not 6 times) with runtime params. Don't reuse the + // vertex shader calculations, so static versions can be constant-folded. + const float sigma_range = max(beam_max_sigma, beam_min_sigma) - beam_min_sigma; + const float shape_range = max(beam_max_shape, beam_min_shape) - beam_min_shape; + + // Find the texel position of the current scanline + const float curr_line_texel_v = floor(texcoord.y * orig_linearized_size.y + under_half); + const float curr_scanline_idx = get_curr_scanline_idx(texcoord.y, orig_linearized_size.y); + const float curr_scanline_start_v = curr_scanline_idx * scanline_num_pixels; + + // Find the center of the current scanline + const float half_num_pixels = scanline_num_pixels / 2; + const float half_size = floor(half_num_pixels + under_half); + const float num_pixels_is_even = float(half_size >= half_num_pixels); + const float upper_center = curr_scanline_start_v + half_size; + // Lower on screen means larger y-coordinate + const float curr_line_is_below_center = float(curr_line_texel_v > upper_center); + const float shift_center = num_pixels_is_even * curr_line_is_below_center; + + const float curr_scanline_center_v = upper_center + shift_center; + const float curr_scanline_center_y = (curr_scanline_center_v + TEXCOORD_OFFSET) / orig_linearized_size.y; + + // Find the center of the nearest in-field scanline + const float source_offset_direction = lerp(-1, 1, curr_line_is_below_center); + const float source_offset = source_offset_direction * wrong_field * scanline_num_pixels; + + const float source_scanline_center_v = curr_scanline_center_v + source_offset; + const float source_scanline_center_y = (source_scanline_center_v + TEXCOORD_OFFSET) / orig_linearized_size.y; + const float2 source_scanline_center_xy = float2(texcoord.x, source_scanline_center_y); + + // Sample the nearest in-field scanline + const float3 scanline_color = sample_single_scanline_horizontal( + samplerOrigLinearized, + texcoord, orig_linearized_size, + 1 / orig_linearized_size + ); + // const float4 scanline_color = decode_input( + // tex2D(samplerOrigLinearized, source_scanline_center_xy), + // get_intermediate_gamma() + // ); + + // Calculate the beam strength based upon distance from the scanline + // and intensity of the sampled color + const float scanlines_wider_than_1 = float(scanline_num_pixels > 1); + const float beam_dist_v = abs(curr_line_texel_v - source_scanline_center_v); + const float max_beam_dist = max(1, scanline_num_pixels - 1); + const float beam_dist_y = beam_dist_v / max_beam_dist; + + const float3 beam_strength = get_beam_strength( + beam_dist_y, scanline_color, + sigma_range, shape_range + ); + + // Output the corrected color + color = encode_output(float4(beam_strength*scanline_color, 1), get_intermediate_gamma()); + } +} + +void scanWithElectronBeamsPS( in const float4 position : SV_Position, in const float2 texcoord : TEXCOORD0, @@ -131,6 +241,8 @@ void scanWithElectronBeams( ); const float contrib_factor = interlacing_factor + 1.0; + // float curr_scanline_start_y = curr_scanline_idx * scanline_num_pixels / content_size.y; + // float beam_center_0 = get_beam_center(texel_0, scanline_idx_0); // const float2 beam_coord_0 = float2(texcoord.x, beam_center_0 / orig_linearized_size.y); @@ -145,6 +257,7 @@ void scanWithElectronBeams( */ float3 scanline_intensity = contrib_factor * scanline_color_0; + // float3 scanline_intensity = scanline_contrib_0 * scanline_color_0; // Temporarily auto-dim the output to avoid clipping. color = encode_output(float4(scanline_intensity * levels_autodim_temp, 1.0), get_intermediate_gamma()); From 7dae72c5890c24ac27a8b44a9e240dec63fe94c0 Mon Sep 17 00:00:00 2001 From: Alex Gunter Date: Wed, 22 Dec 2021 12:53:52 -0600 Subject: [PATCH 04/16] Fixed a typo --- reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh b/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh index ac9eda7..2411785 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh @@ -335,7 +335,7 @@ uniform float3 convergence_offset_y < ui_step = 0.05; > = 0; uniform int beam_shape_mode < - ui_lable = "Beam Shape Mode"; + ui_label = "Beam Shape Mode"; ui_category = "Electron Beam"; ui_type = "combo"; ui_items = "Digital\0" From 9315e665d4375b2f38c3dc9001b420464b319a3f Mon Sep 17 00:00:00 2001 From: Alex Gunter Date: Wed, 22 Dec 2021 21:00:06 -0600 Subject: [PATCH 05/16] Corrected a conceptual error in the beam strength code --- .../shaders/crt-royale-electron-beams.fxh | 61 +------------------ 1 file changed, 3 insertions(+), 58 deletions(-) diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh index 54d64ff..d5e74fd 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh @@ -189,7 +189,7 @@ void fancyScanWithElectronBeamsPS( // Sample the nearest in-field scanline const float3 scanline_color = sample_single_scanline_horizontal( samplerOrigLinearized, - texcoord, orig_linearized_size, + source_scanline_center_xy, orig_linearized_size, 1 / orig_linearized_size ); // const float4 scanline_color = decode_input( @@ -210,63 +210,8 @@ void fancyScanWithElectronBeamsPS( ); // Output the corrected color - color = encode_output(float4(beam_strength*scanline_color, 1), get_intermediate_gamma()); - } -} - -void scanWithElectronBeamsPS( - in const float4 position : SV_Position, - in const float2 texcoord : TEXCOORD0, - - out float4 color : SV_Target -) { - const float2 orig_linearized_size = tex2Dsize(samplerOrigLinearized); - - // Calculate {sigma, shape}_range outside of scanline_contrib so it's only - // done once per pixel (not 6 times) with runtime params. Don't reuse the - // vertex shader calculations, so static versions can be constant-folded. - // const float sigma_range = max(beam_max_sigma, beam_min_sigma) - beam_min_sigma; - // const float shape_range = max(beam_max_shape, beam_min_shape) - beam_min_shape; - - const float wrong_field = curr_line_is_wrong_field(texcoord.y, orig_linearized_size.y); - - - // If we're in the current field, draw the beam - // wrong_field is always 0 when we aren't interlacing - if (!wrong_field) { - // Double the intensity when interlacing to maintain the same apparent brightness - const float interlacing_factor = enable_interlacing * float( - scanline_deinterlacing_mode != 1 && - scanline_deinterlacing_mode != 2 - ); - const float contrib_factor = interlacing_factor + 1.0; - - // float curr_scanline_start_y = curr_scanline_idx * scanline_num_pixels / content_size.y; - - - // float beam_center_0 = get_beam_center(texel_0, scanline_idx_0); - // const float2 beam_coord_0 = float2(texcoord.x, beam_center_0 / orig_linearized_size.y); - const float3 scanline_color_0 = tex2D_linearize(samplerOrigLinearized, texcoord, get_intermediate_gamma()).rgb; - - /* - const float3 beam_dist_0 = 0; - const float3 scanline_contrib_0 = get_beam_strength( - beam_dist_0, - scanline_color_0, sigma_range, shape_range - ); - */ - - float3 scanline_intensity = contrib_factor * scanline_color_0; - // float3 scanline_intensity = scanline_contrib_0 * scanline_color_0; - - // Temporarily auto-dim the output to avoid clipping. - color = encode_output(float4(scanline_intensity * levels_autodim_temp, 1.0), get_intermediate_gamma()); - } - // If we're not in the current field, don't draw the beam - // It's tempting to add a gaussian here to account for bleeding, but it usually ends up - // either doing nothing or making the colors wrong. - else { - color = float4(0, 0, 0, 1); + // color = encode_output(float4(beam_strength*scanline_color, 1), get_intermediate_gamma()); + color = encode_output(float4(beam_strength, 1), get_intermediate_gamma()); } } From 2bfef3f3e0854a57ff983cc8382cda219b238db1 Mon Sep 17 00:00:00 2001 From: Alex Gunter Date: Thu, 23 Dec 2021 20:03:31 -0600 Subject: [PATCH 06/16] Added new beam shape and fixed a diffusion/halation artifact --- reshade-shaders/Shaders/crt-royale.fx | 2 +- .../crt-royale/lib/bind-shader-params.fxh | 302 +++++++++--------- .../shaders/crt-royale-blend-frames.fxh | 8 +- .../shaders/crt-royale-bloom-approx.fxh | 2 +- .../shaders/crt-royale-electron-beams.fxh | 208 +++++++----- .../crt-royale/shaders/shared-objects.fxh | 19 +- 6 files changed, 296 insertions(+), 245 deletions(-) diff --git a/reshade-shaders/Shaders/crt-royale.fx b/reshade-shaders/Shaders/crt-royale.fx index c34d575..3a7dc82 100644 --- a/reshade-shaders/Shaders/crt-royale.fx +++ b/reshade-shaders/Shaders/crt-royale.fx @@ -78,7 +78,7 @@ technique CRT_Royale VertexShader = PostProcessVS; PixelShader = beamMisaslignmentPS; - RenderTarget = texVerticalOffset; + RenderTarget = texBeamMisalignment; } // crt-royale-bloom-approx.fxh pass bloomApproxPass diff --git a/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh b/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh index 2411785..32d4421 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh @@ -115,6 +115,158 @@ uniform float mask_num_triads_desired < ui_step = 1.0; > = mask_num_triads_desired_static; + +// ==== INTERLACING ==== +uniform bool enable_interlacing < + ui_label = "Enable Interlacing"; + ui_category = "Interlacing"; + // ui_type = "combo"; + // ui_items = "No\0Yes\0"; +> = true; +uniform int scanline_deinterlacing_mode < + ui_label = "Deinterlacing Mode"; + ui_tooltip = "Selects the deinterlacing algorithm. For crt-royale's original appearance, choose None."; + ui_category = "Interlacing"; + ui_type = "combo"; + ui_items = "None\0" + "Weaving\0" + "Blended Weaving\0" + "Static\0"; +> = 0; +uniform float scanline_num_pixels < + ui_label = "Scanline Thickness"; + ui_category = "Interlacing"; + ui_type = "slider"; + ui_min = 1.0; + ui_max = 30.0; + ui_step = 1.0; +> = 2.0; +uniform float scanline_blend_gamma < + ui_label = "Scanline Blend Gamma"; + ui_tooltip = "Nudge this if Scanline Blend Strength changes your colors too much"; + ui_category = "Interlacing"; + ui_type = "slider"; + ui_min = 0.01; + ui_max = 5.0; + ui_step = 0.01; +> = 1.0; +uniform bool interlace_bff < + // ui_type = "combo"; + ui_label = "Draw Back-Field First"; + ui_tooltip = "Draw odd-numbered scanlines first (often has no effect)"; + ui_category = "Interlacing"; + // ui_items = "No\0Yes\0"; +> = interlace_bff_static; + +// ==== ELECTRON BEAM ==== +// static const float beam_min_sigma = beam_min_sigma_static; +// static const float beam_max_sigma = beam_max_sigma_static; +static const float beam_spot_power = beam_spot_power_static; +// static const float beam_min_shape = beam_min_shape_static; +// static const float beam_max_shape = beam_max_shape_static; +static const float beam_shape_power = beam_shape_power_static; + +uniform float3 convergence_offset_x < + ui_label = "Convergence Offset X RGB"; + ui_tooltip = "Shift the color channels horizontally"; + ui_category = "Electron Beam"; + ui_type = "drag"; + ui_min = -4; + ui_max = 4; + ui_step = 0.05; +> = 0; +uniform float3 convergence_offset_y < + ui_label = "Convergence Offset Y RGB"; + ui_tooltip = "Shift the color channels vertically"; + ui_category = "Electron Beam"; + ui_type = "drag"; + ui_min = -4; + ui_max = 4; + ui_step = 0.05; +> = 0; +uniform int beam_shape_mode < + ui_label = "Beam Shape Mode"; + ui_category = "Electron Beam"; + ui_type = "combo"; + ui_items = "Digital\0" + "Gaussian\0" + "Multi-Source Gaussian\0"; +> = 1; +uniform float beam_min_sigma < + ui_label = "Beam Min Sigma"; + ui_category = "Electron Beam"; + ui_type = "drag"; + ui_min = 0.0; + ui_step = 0.01; +> = beam_min_sigma_static; +uniform float beam_max_sigma < + ui_label = "Beam Max Sigma"; + ui_tooltip = "Must be >= Beam Min Sigma"; + ui_category = "Electron Beam"; + ui_type = "drag"; + ui_min = 0.0; + ui_step = 0.01; +> = beam_max_sigma_static; +/* +uniform float beam_spot_power < + ui_label = "Beam Spot Power"; + ui_type = "drag"; + ui_min = 0.0; + ui_step = 0.01; +> = beam_spot_power_static; +*/ +uniform float beam_min_shape < + ui_label = "Beam Min Shape"; + ui_category = "Electron Beam"; + ui_type = "drag"; + ui_min = 0.0; + ui_step = 0.01; +> = beam_min_shape_static; +uniform float beam_max_shape < + ui_label = "Beam Max Shape"; + ui_tooltip = "Must be >= Beam Min Shape"; + ui_category = "Electron Beam"; + ui_type = "drag"; + ui_min = 0.0; + ui_step = 0.01; +> = beam_max_shape_static; +/* +uniform float beam_shape_power < + ui_label = "Beam Shape Power"; + ui_type = "drag"; + ui_min = 0.0; + ui_step = 0.01; +> = beam_shape_power_static; +*/ +uniform int beam_horiz_filter < + ui_label = "Beam Horiz Filter"; + ui_tooltip = "Default is Quilez"; + ui_category = "Electron Beam"; + ui_type = "combo"; + ui_items = "None\0" + "Quilez (Fast)\0" + "Gaussian (Tunable)\0" + "Lanczos (Sharp)\0"; +> = beam_horiz_filter_static; +uniform float beam_horiz_sigma < + ui_label = "Beam Horiz Sigma"; + ui_tooltip = "Requires Gaussian Horiz Filter"; + ui_category = "Electron Beam"; + ui_type = "slider"; + ui_min = 0.01; + ui_max = 0.67; + ui_step = 0.01; +> = beam_horiz_sigma_static; +uniform float beam_horiz_linear_rgb_weight < + ui_label = "Beam Horiz Linear RGB Weight"; + ui_category = "Electron Beam"; + ui_type = "slider"; + ui_min = 0.0; + ui_max = 1.0; + ui_step = 0.01; +> = beam_horiz_linear_rgb_weight_static; + + // ==== IMAGE COLORIZATION ==== uniform float crt_gamma < ui_label = "CRT Gamma"; @@ -190,48 +342,6 @@ static const float aa_cubic_c = aa_cubic_c_static; static const float aa_gauss_sigma = aa_gauss_sigma_static; -// ==== INTERLACING ==== -uniform bool enable_interlacing < - ui_label = "Enable Interlacing"; - ui_category = "Interlacing"; - // ui_type = "combo"; - // ui_items = "No\0Yes\0"; -> = true; -uniform int scanline_deinterlacing_mode < - ui_label = "Deinterlacing Mode"; - ui_tooltip = "Selects the deinterlacing algorithm. For crt-royale's original appearance, choose None."; - ui_category = "Interlacing"; - ui_type = "combo"; - ui_items = "None\0" - "Weaving\0" - "Blended Weaving\0" - "Static\0"; -> = 0; -uniform float scanline_num_pixels < - ui_label = "Scanline Thickness"; - ui_category = "Interlacing"; - ui_type = "slider"; - ui_min = 1.0; - ui_max = 30.0; - ui_step = 1.0; -> = 2.0; -uniform float scanline_blend_gamma < - ui_label = "Scanline Blend Gamma"; - ui_tooltip = "Nudge this if Scanline Blend Strength changes your colors too much"; - ui_category = "Interlacing"; - ui_type = "slider"; - ui_min = 0.01; - ui_max = 5.0; - ui_step = 0.01; -> = 1.0; -uniform bool interlace_bff < - // ui_type = "combo"; - ui_label = "Draw Back-Field First"; - ui_tooltip = "Draw odd-numbered scanlines first (often has no effect)"; - ui_category = "Interlacing"; - // ui_items = "No\0Yes\0"; -> = interlace_bff_static; - // ==== GEOMETRY ==== uniform int geom_mode_runtime < ui_label = "Geom Mode"; @@ -308,114 +418,6 @@ uniform float border_compress < > = border_compress_static; -// ==== ELECTRON BEAM ==== -// static const float beam_min_sigma = beam_min_sigma_static; -// static const float beam_max_sigma = beam_max_sigma_static; -static const float beam_spot_power = beam_spot_power_static; -// static const float beam_min_shape = beam_min_shape_static; -// static const float beam_max_shape = beam_max_shape_static; -static const float beam_shape_power = beam_shape_power_static; - -uniform float3 convergence_offset_x < - ui_label = "Convergence Offset X RGB"; - ui_tooltip = "Shift the color channels horizontally"; - ui_category = "Electron Beam"; - ui_type = "drag"; - ui_min = -4; - ui_max = 4; - ui_step = 0.05; -> = 0; -uniform float3 convergence_offset_y < - ui_label = "Convergence Offset Y RGB"; - ui_tooltip = "Shift the color channels vertically"; - ui_category = "Electron Beam"; - ui_type = "drag"; - ui_min = -4; - ui_max = 4; - ui_step = 0.05; -> = 0; -uniform int beam_shape_mode < - ui_label = "Beam Shape Mode"; - ui_category = "Electron Beam"; - ui_type = "combo"; - ui_items = "Digital\0" - "Gaussian\0"; -> = 0; -uniform float beam_min_sigma < - ui_label = "Beam Min Sigma"; - ui_category = "Electron Beam"; - ui_type = "drag"; - ui_min = 0.0; - ui_step = 0.01; -> = beam_min_sigma_static; -uniform float beam_max_sigma < - ui_label = "Beam Max Sigma"; - ui_tooltip = "Must be >= Beam Min Sigma"; - ui_category = "Electron Beam"; - ui_type = "drag"; - ui_min = 0.0; - ui_step = 0.01; -> = beam_max_sigma_static; -/* -uniform float beam_spot_power < - ui_label = "Beam Spot Power"; - ui_type = "drag"; - ui_min = 0.0; - ui_step = 0.01; -> = beam_spot_power_static; -*/ -uniform float beam_min_shape < - ui_label = "Beam Min Shape"; - ui_category = "Electron Beam"; - ui_type = "drag"; - ui_min = 0.0; - ui_step = 0.01; -> = beam_min_shape_static; -uniform float beam_max_shape < - ui_label = "Beam Max Shape"; - ui_tooltip = "Must be >= Beam Min Shape"; - ui_category = "Electron Beam"; - ui_type = "drag"; - ui_min = 0.0; - ui_step = 0.01; -> = beam_max_shape_static; -/* -uniform float beam_shape_power < - ui_label = "Beam Shape Power"; - ui_type = "drag"; - ui_min = 0.0; - ui_step = 0.01; -> = beam_shape_power_static; -*/ -uniform int beam_horiz_filter < - ui_label = "Beam Horiz Filter"; - ui_tooltip = "Default is Quilez"; - ui_category = "Electron Beam"; - ui_type = "combo"; - ui_items = "None\0" - "Quilez (Fast)\0" - "Gaussian (Tunable)\0" - "Lanczos (Sharp)\0"; -> = beam_horiz_filter_static; -uniform float beam_horiz_sigma < - ui_label = "Beam Horiz Sigma"; - ui_tooltip = "Requires Gaussian Horiz Filter"; - ui_category = "Electron Beam"; - ui_type = "slider"; - ui_min = 0.01; - ui_max = 0.67; - ui_step = 0.01; -> = beam_horiz_sigma_static; -uniform float beam_horiz_linear_rgb_weight < - ui_label = "Beam Horiz Linear RGB Weight"; - ui_category = "Electron Beam"; - ui_type = "slider"; - ui_min = 0.0; - ui_max = 1.0; - ui_step = 0.01; -> = beam_horiz_linear_rgb_weight_static; - - diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-blend-frames.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-blend-frames.fxh index fdcf5bc..8f2510f 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-blend-frames.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-blend-frames.fxh @@ -34,7 +34,7 @@ void lerpScanlinesPS( const float cur_scanline_idx = get_curr_scanline_idx(texcoord.y, CONTENT_HEIGHT_INTERNAL); const float wrong_field = curr_line_is_wrong_field(cur_scanline_idx); - const float4 cur_line_color = tex2D(samplerVerticalOffset, texcoord); + const float4 cur_line_color = tex2D(samplerBeamMisalignment, texcoord); const float4 cur_line_prev_color = tex2D(samplerFreezeFrame, texcoord); const float4 avg_color = (cur_line_color + cur_line_prev_color) / 2.0; @@ -61,7 +61,7 @@ void lerpScanlinesPS( const float2 curr_offset = lerp(0, raw_offset, wrong_field); const float2 prev_offset = lerp(raw_offset, 0, wrong_field); - const float4 cur_line_color = tex2D(samplerVerticalOffset, texcoord + curr_offset); + const float4 cur_line_color = tex2D(samplerBeamMisalignment, texcoord + curr_offset); const float4 prev_line_color = tex2D(samplerFreezeFrame, texcoord + prev_offset); const float4 avg_color = (cur_line_color + prev_line_color) / 2.0; @@ -70,7 +70,7 @@ void lerpScanlinesPS( } // No temporal blending else { - color = tex2D(samplerVerticalOffset, texcoord); + color = tex2D(samplerBeamMisalignment, texcoord); } } @@ -80,5 +80,5 @@ void freezeFramePS( out float4 color : SV_Target ) { - color = tex2D(samplerVerticalOffset, texcoord); + color = tex2D(samplerBeamMisalignment, texcoord); } \ No newline at end of file diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-bloom-approx.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-bloom-approx.fxh index 2c94d78..5a69ab9 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-bloom-approx.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-bloom-approx.fxh @@ -154,7 +154,7 @@ void approximateBloomPS( out float4 color : SV_Target ) { const float2 orig_linearized_size = tex2Dsize(samplerOrigLinearized); - const float2 vertical_scanlines_size = tex2Dsize(samplerVerticalOffset); + const float2 vertical_scanlines_size = tex2Dsize(samplerBeamMisalignment); const float2 output_size = TEX_BLOOMAPPROX_SIZE; // const float2 video_uv = vTexCoord * texture_size / video_size; diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh index d5e74fd..9069aec 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh @@ -48,56 +48,22 @@ void linearizeAndBobPS( out float4 color : SV_Target ) { - // Linearize the input based on CRT gamma and bob interlaced fields. - // Bobbing ensures we can immediately blur without getting artifacts. - // Note: TFF/BFF won't matter for sources that double-weave or similar. - if (bool(interlaced)) - { - // Sample the current line and an average of the previous/next line; - // tex2D_linearize will decode CRT gamma. Don't bother branching: - float curr_scanline_idx = get_curr_scanline_idx(texcoord.y, content_size.y); - float curr_scanline_start_y = ( - curr_scanline_idx * scanline_num_pixels + TEXCOORD_OFFSET - ) / content_size.y; - float3 in_field_interpolated_line = get_bobbed_scanline_sample( - samplerCrop, texcoord, - curr_scanline_start_y, v_step.y, - get_input_gamma() - ); - - float prev_scanline_start_y = curr_scanline_start_y - scanline_num_pixels * v_step.y; - float next_scanline_starty_y = curr_scanline_start_y + scanline_num_pixels * v_step.y; - float3 prev_interpolated_line = get_bobbed_scanline_sample( - samplerCrop, texcoord, - prev_scanline_start_y, v_step.y, - get_input_gamma() - ); - float3 next_interpolated_line = get_bobbed_scanline_sample( - samplerCrop, texcoord, - next_scanline_starty_y, v_step.y, - get_input_gamma() - ); - - float3 out_field_interpolated_line = 0.5 * (prev_interpolated_line + next_interpolated_line); - - // Select the correct color, and output the result: - const float wrong_field = curr_line_is_wrong_field(curr_scanline_idx); - const float3 selected_color = lerp(in_field_interpolated_line, out_field_interpolated_line, wrong_field); - - color = encode_output(float4(selected_color, 1.0), get_intermediate_gamma()); - } - else - { - float curr_scanline_idx = get_curr_scanline_idx(texcoord.y, content_size.y); - float curr_scanline_start_y = curr_scanline_idx * scanline_num_pixels / content_size.y; - float3 in_field_interpolated_line = get_bobbed_scanline_sample( - samplerCrop, texcoord, - curr_scanline_start_y, v_step.y, - get_input_gamma() - ); + // Sample the current line and an average of the previous/next line; + // tex2D_linearize will decode CRT gamma. Don't bother branching: + float curr_scanline_idx = get_curr_scanline_idx(texcoord.y, content_size.y); + float curr_scanline_start_y = ( + curr_scanline_idx * scanline_num_pixels + TEXCOORD_OFFSET + ) / content_size.y; + float3 in_field_interpolated_line = get_bobbed_scanline_sample( + samplerCrop, texcoord, + curr_scanline_start_y, v_step.y, + get_input_gamma() + ); + + const float wrong_field = interlaced * curr_line_is_wrong_field(curr_scanline_idx); + const float3 selected_color = lerp(in_field_interpolated_line, 0, wrong_field); - color = encode_output(float4(in_field_interpolated_line, 1.0), get_intermediate_gamma()); - } + color = encode_output(float4(selected_color, 1.0), get_intermediate_gamma()); } void fancyScanWithElectronBeamsVS( @@ -124,37 +90,34 @@ void fancyScanWithElectronBeamsPS( const float wrong_field = curr_line_is_wrong_field(texcoord.y, orig_linearized_size.y); + // Digital shape + // Beam will be perfectly rectangular if (beam_shape_mode == 0) { // If we're in the current field, draw the beam // wrong_field is always 0 when we aren't interlacing - if (!wrong_field) { - // Double the intensity when interlacing to maintain the same apparent brightness - const float interlacing_brightness_factor = enable_interlacing * float( - scanline_deinterlacing_mode != 1 && - scanline_deinterlacing_mode != 2 - ); - const float contrib_factor = interlacing_brightness_factor + 1.0; - - // const float3 scanline_color = tex2D_linearize(samplerOrigLinearized, texcoord, get_intermediate_gamma()).rgb; - const float3 scanline_color = sample_single_scanline_horizontal( - samplerOrigLinearized, - texcoord, orig_linearized_size, - 1 / orig_linearized_size - ); - - float3 scanline_intensity = contrib_factor * scanline_color; - - // Temporarily auto-dim the output to avoid clipping. - color = encode_output(float4(scanline_intensity * levels_autodim_temp, 1.0), get_intermediate_gamma()); - } - // If we're not in the current field, don't draw the beam - // It's tempting to add a gaussian here to account for bleeding, but it usually ends up - // either doing nothing or making the colors wrong. - else { - color = float4(0, 0, 0, 1); - } + // Double the intensity when interlacing to maintain the same apparent brightness + const float interlacing_brightness_factor = enable_interlacing * float( + scanline_deinterlacing_mode != 1 && + scanline_deinterlacing_mode != 2 + ); + const float contrib_factor = interlacing_brightness_factor + 1.0; + + // const float3 scanline_color = tex2D_linearize(samplerOrigLinearized, texcoord, get_intermediate_gamma()).rgb; + const float3 scanline_color = sample_single_scanline_horizontal( + samplerOrigLinearized, + texcoord, orig_linearized_size, + 1 / orig_linearized_size + ); + + const float3 scanline_intensity = (1 - wrong_field) * contrib_factor * scanline_color; + + // Temporarily auto-dim the output to avoid clipping. + color = encode_output(float4(scanline_intensity * levels_autodim_temp, 1.0), get_intermediate_gamma()); } - else { + // Gaussian Shape + // Beam will be a distorted Gaussian, dependent on color brightness and hyperparameters + // Will only consider contribution from nearest scanline + else if (beam_shape_mode == 1) { // Calculate {sigma, shape}_range outside of scanline_contrib so it's only // done once per pixel (not 6 times) with runtime params. Don't reuse the // vertex shader calculations, so static versions can be constant-folded. @@ -200,18 +163,103 @@ void fancyScanWithElectronBeamsPS( // Calculate the beam strength based upon distance from the scanline // and intensity of the sampled color const float scanlines_wider_than_1 = float(scanline_num_pixels > 1); + const float max_beam_dist_factor = 1 + float(enable_interlacing); + const float max_beam_dist = max(1, max_beam_dist_factor*half_size - 1); + const float beam_dist_v = abs(curr_line_texel_v - source_scanline_center_v); - const float max_beam_dist = max(1, scanline_num_pixels - 1); const float beam_dist_y = beam_dist_v / max_beam_dist; - const float3 beam_strength = get_beam_strength( beam_dist_y, scanline_color, sigma_range, shape_range ); // Output the corrected color - // color = encode_output(float4(beam_strength*scanline_color, 1), get_intermediate_gamma()); - color = encode_output(float4(beam_strength, 1), get_intermediate_gamma()); + color = encode_output(float4(beam_strength * levels_autodim_temp * 1.5, 1), get_intermediate_gamma()); + } + // Gaussian Shape + // Beam will be a distorted Gaussian, dependent on color brightness and hyperparameters + // Will consider contributions from current scanline and two neighboring in-field scanlines + else { + // Calculate {sigma, shape}_range outside of scanline_contrib so it's only + // done once per pixel (not 6 times) with runtime params. Don't reuse the + // vertex shader calculations, so static versions can be constant-folded. + const float sigma_range = max(beam_max_sigma, beam_min_sigma) - beam_min_sigma; + const float shape_range = max(beam_max_shape, beam_min_shape) - beam_min_shape; + + // Find the texel position of the current scanline + const float curr_line_texel_v = floor(texcoord.y * orig_linearized_size.y + under_half); + const float curr_scanline_idx = get_curr_scanline_idx(texcoord.y, orig_linearized_size.y); + const float curr_scanline_start_v = curr_scanline_idx * scanline_num_pixels; + + // Find the center of the current scanline + const float half_num_pixels = scanline_num_pixels / 2; + const float half_size = floor(half_num_pixels + under_half); + const float num_pixels_is_even = float(half_size >= half_num_pixels); + const float upper_center = curr_scanline_start_v + half_size; + // Lower on screen means larger y-coordinate + const float curr_line_is_below_center = float(curr_line_texel_v > upper_center); + const float shift_center = num_pixels_is_even * curr_line_is_below_center; + const float bounding_scanline_offset_v = (2 - wrong_field) * scanline_num_pixels; + + const float3 scanline_offsets_v = float3( + -bounding_scanline_offset_v, + 0, + bounding_scanline_offset_v + ); + const float3 scanline_centers_v = upper_center + shift_center + scanline_offsets_v; + const float3 scanline_centers_y = ( + scanline_centers_v + TEXCOORD_OFFSET + ) / orig_linearized_size.y; + + const float2 upper_scanline_center_xy = float2(texcoord.x, scanline_centers_y.x); + const float2 curr_scanline_center_xy = float2(texcoord.x, scanline_centers_y.y); + const float2 lower_scanline_center_xy = float2(texcoord.x, scanline_centers_y.z); + + const float3 upper_scanline_color = sample_single_scanline_horizontal( + samplerOrigLinearized, + upper_scanline_center_xy, orig_linearized_size, + 1 / orig_linearized_size + ); + const float3 curr_scanline_color = sample_single_scanline_horizontal( + samplerOrigLinearized, + curr_scanline_center_xy, orig_linearized_size, + 1 / orig_linearized_size + ); + const float3 lower_scanline_color = sample_single_scanline_horizontal( + samplerOrigLinearized, + lower_scanline_center_xy, orig_linearized_size, + 1 / orig_linearized_size + ); + + // Calculate the beam strength based upon distance from the scanline + // and intensity of the sampled color + const float scanlines_wider_than_1 = float(scanline_num_pixels > 1); + const float max_beam_dist_factor = 1 + float(enable_interlacing); + const float max_beam_dist = max(1, max_beam_dist_factor*half_size - 1); + + const float3 beam_dists_v = abs(curr_line_texel_v - scanline_centers_v); + const float3 beam_dists_y = beam_dists_v / max_beam_dist; + + const float3 upper_beam_strength = get_beam_strength( + beam_dists_y.x, upper_scanline_color, + sigma_range, shape_range + ); + const float3 curr_beam_strength = get_beam_strength( + beam_dists_y.y, curr_scanline_color, + sigma_range, shape_range + ); + const float3 lower_beam_strength = get_beam_strength( + beam_dists_y.z, lower_scanline_color, + sigma_range, shape_range + ); + const float3 beam_strength = ( + upper_beam_strength + + (1 - wrong_field) * curr_beam_strength + + lower_beam_strength + ); + + // Output the corrected color + color = encode_output(float4(beam_strength * levels_autodim_temp * 1.5, 1), get_intermediate_gamma()); } } diff --git a/reshade-shaders/Shaders/crt-royale/shaders/shared-objects.fxh b/reshade-shaders/Shaders/crt-royale/shaders/shared-objects.fxh index f694a70..62e43ab 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/shared-objects.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/shared-objects.fxh @@ -86,7 +86,7 @@ texture2D texCrop { }; sampler2D samplerCrop { Texture = texCrop; }; -// Pass 0 Buffer (ORIG_LINEARIZED) +// Pass 0 Buffer (ORIGLINEARIZED) #define TEX_ORIGLINEARIZED_WIDTH CONTENT_WIDTH_INTERNAL #define TEX_ORIGLINEARIZED_HEIGHT CONTENT_HEIGHT_INTERNAL #define TEX_ORIGLINEARIZED_SIZE int2(TEX_ORIGLINEARIZED_WIDTH, TEX_ORIGLINEARIZED_HEIGHT) @@ -98,7 +98,7 @@ texture2D texOrigLinearized { }; sampler2D samplerOrigLinearized { Texture = texOrigLinearized; }; -// Pass 1 Buffer (VERTICAL_SCANLINES) +// Pass 1 Buffer (VERTICALSCANLINES) #define TEX_VERTICALSCANLINES_WIDTH CONTENT_WIDTH_INTERNAL #define TEX_VERTICALSCANLINES_HEIGHT CONTENT_HEIGHT_INTERNAL #define TEX_VERTICALSCANLINES_SIZE int2(TEX_VERTICALSCANLINES_WIDTH, TEX_VERTICALSCANLINES_HEIGHT) @@ -110,16 +110,17 @@ texture2D texVerticalScanlines { }; sampler2D samplerVerticalScanlines { Texture = texVerticalScanlines; }; -#define TEX_VERTICALOFFSET_WIDTH CONTENT_WIDTH_INTERNAL -#define TEX_VERTICALOFFSET_HEIGHT CONTENT_HEIGHT_INTERNAL -#define TEX_VERTICALOFFSET_SIZE int2(TEX_VERTICALOFFSET_WIDTH, TEX_VERTICALOFFSET_HEIGHT) -texture2D texVerticalOffset { - Width = TEX_VERTICALOFFSET_WIDTH; - Height = TEX_VERTICALOFFSET_HEIGHT; +// Pass 2 Buffer (BEAMMISALIGNMENT) +#define TEX_BEAMMISALIGNMENT_WIDTH CONTENT_WIDTH_INTERNAL +#define TEX_BEAMMISALIGNMENT_HEIGHT CONTENT_HEIGHT_INTERNAL +#define TEX_BEAMMISALIGNMENT_SIZE int2(TEX_BEAMMISALIGNMENT_WIDTH, TEX_BEAMMISALIGNMENT_HEIGHT) +texture2D texBeamMisalignment { + Width = TEX_BEAMMISALIGNMENT_WIDTH; + Height = TEX_BEAMMISALIGNMENT_HEIGHT; Format = RGBA16; }; -sampler2D samplerVerticalOffset { Texture = texVerticalOffset; }; +sampler2D samplerBeamMisalignment { Texture = texBeamMisalignment; }; // Pass 2 Buffer (BLOOM_APPROX) #define TEX_BLOOMAPPROX_WIDTH 320 From a018282705376bec760d9cccef3b9f36f872085f Mon Sep 17 00:00:00 2001 From: Alex Gunter Date: Fri, 24 Dec 2021 01:26:14 -0600 Subject: [PATCH 07/16] Renamed and restructured things. Added minor configs --- reshade-shaders/Shaders/crt-royale.fx | 64 +++-- .../crt-royale/lib/bind-shader-params.fxh | 17 +- .../crt-royale/lib/scanline-functions.fxh | 10 +- .../crt-royale/shaders/content-crop.fxh | 8 +- .../shaders/crt-royale-bloom-approx.fxh | 34 +-- ...-frames.fxh => crt-royale-deinterlace.fxh} | 12 +- .../shaders/crt-royale-electron-beams.fxh | 43 ++-- .../shaders/crt-royale-phosphor-mask.fxh | 4 +- ...royale-scanlines-horizontal-apply-mask.fxh | 175 -------------- .../crt-royale/shaders/shared-objects.fxh | 219 ++++++++++-------- 10 files changed, 244 insertions(+), 342 deletions(-) rename reshade-shaders/Shaders/crt-royale/shaders/{crt-royale-blend-frames.fxh => crt-royale-deinterlace.fxh} (88%) delete mode 100644 reshade-shaders/Shaders/crt-royale/shaders/crt-royale-scanlines-horizontal-apply-mask.fxh diff --git a/reshade-shaders/Shaders/crt-royale.fx b/reshade-shaders/Shaders/crt-royale.fx index 3a7dc82..58ae424 100644 --- a/reshade-shaders/Shaders/crt-royale.fx +++ b/reshade-shaders/Shaders/crt-royale.fx @@ -29,12 +29,12 @@ #include "crt-royale/shaders/crt-royale-bloom-approx.fxh" #include "crt-royale/shaders/blur9fast-vertical.fxh" #include "crt-royale/shaders/blur9fast-horizontal.fxh" + #include "crt-royale/shaders/crt-royale-deinterlace.fxh" #include "crt-royale/shaders/crt-royale-phosphor-mask.fxh" #include "crt-royale/shaders/crt-royale-brightpass.fxh" #include "crt-royale/shaders/crt-royale-bloom-vertical.fxh" #include "crt-royale/shaders/crt-royale-bloom-horizontal-reconstitute.fxh" #include "crt-royale/shaders/crt-royale-geometry-aa-last-pass.fxh" - #include "crt-royale/shaders/crt-royale-blend-frames.fxh" #else #include "crt-royale/shaders/content-box.fxh" #endif @@ -47,6 +47,7 @@ technique CRT_Royale // content-box.fxh pass contentBoxPass { + // Draw a box that displays the crop we'll perform. VertexShader = PostProcessVS; PixelShader = contentBoxPixelShader; } @@ -54,35 +55,47 @@ technique CRT_Royale // content-crop.fxh pass cropPass { + // Crop the input buffer, so all our math is scaled to the actual + // game content rather than the entire window. VertexShader = PostProcessVS; PixelShader = cropContentPixelShader; RenderTarget = texCrop; } // crt-royale-electron-beams.fx - pass linearizeAndBobPass + pass interlacingPass { - VertexShader = linearizeAndBobVS; - PixelShader = linearizeAndBobPS; + // Simulate interlacing by blending in-field rows + // and blanking out out-of-field rows. + VertexShader = simulateInterpolationVS; + PixelShader = simulateInterpolationPS; - RenderTarget = texOrigLinearized; + RenderTarget = texInterlaced; } pass electronBeamPass { - VertexShader = fancyScanWithElectronBeamsVS; - PixelShader = fancyScanWithElectronBeamsPS; + // Simulate emission of the interlaced video as electron beams. + VertexShader = simulateEletronBeamsVS; + PixelShader = simulateEletronBeamsPS; - RenderTarget = texVerticalScanlines; + RenderTarget = texElectronBeams; } - pass beamMisaslignmentPass { + pass beamConvergencPass + { + // Simulate beam convergence miscalibration + // Not to be confused with beam purity VertexShader = PostProcessVS; - PixelShader = beamMisaslignmentPS; + PixelShader = beamConvergencePS; - RenderTarget = texBeamMisalignment; + RenderTarget = texBeamConvergence; } // crt-royale-bloom-approx.fxh pass bloomApproxPass { + // The original crt-royale did a bunch of math in this pass + // and then threw it all away. So this is a no-op for now. + // It still has a blur effect b/c its a smaller buffer. + // TODO: activate the math in this pass and see what happens. VertexShader = PostProcessVS; PixelShader = approximateBloomPS; @@ -91,6 +104,7 @@ technique CRT_Royale // blur9fast-vertical.fxh pass blurVerticalPass { + // Vertically blur the approx bloom VertexShader = blurVerticalVS; PixelShader = blurVerticalPS; @@ -99,22 +113,27 @@ technique CRT_Royale // blur9fast-horizontal.fxh pass blurHorizontalPass { + // Horizontally blur the approx bloom VertexShader = blurHorizontalVS; PixelShader = blurHorizontalPS; RenderTarget = texBlurHorizontal; } - // crt-royale-blend-frames.fxh - pass scanlineBlendPass + // crt-royale-deinterlace.fxh + pass deinterlacePass { - VertexShader = lerpScanlinesVS; - PixelShader = lerpScanlinesPS; + // Optionally deinterlace the video. This can produce more + // consistent behavior across monitors and emulators. + // It can also help simulate some edge cases. + VertexShader = deinterlaceVS; + PixelShader = deinterlacePS; - RenderTarget = texBlendScanline; + RenderTarget = texDeinterlace; } - // crt-royale-blend-frames.fxh pass freezeFramePass { + // Capture the current frame, so we can use it in the next + // frame's deinterlacing pass. VertexShader = PostProcessVS; PixelShader = freezeFramePS; @@ -127,6 +146,7 @@ technique CRT_Royale // crt-royale-phosphor-mask.fxh pass phosphorMaskResizeVerticalPass { + // Scale the phosphor mask vertically VertexShader = maskResizeVertVS; PixelShader = maskResizeVertPS; @@ -134,6 +154,7 @@ technique CRT_Royale } pass phosphorMaskResizeHorizontalPass { + // Scale the phosphor mask horizontally VertexShader = maskResizeHorizVS; PixelShader = maskResizeHorizPS; @@ -141,6 +162,8 @@ technique CRT_Royale } pass phosphorMaskPass { + // Tile the scaled phosphor mask and apply it to + // the deinterlaced image. VertexShader = PostProcessVS; PixelShader = applyPhosphorMaskPS; @@ -149,6 +172,7 @@ technique CRT_Royale // crt-royale-brightpass.fxh pass brightpassPass { + // Apply a brightpass filter for the bloom effect VertexShader = brightpassVS; PixelShader = brightpassPS; @@ -157,6 +181,7 @@ technique CRT_Royale // crt-royale-bloom-vertical.fxh pass bloomVerticalPass { + // Blur vertically for the bloom effect VertexShader = bloomVerticalVS; PixelShader = bloomVerticalPS; @@ -165,6 +190,8 @@ technique CRT_Royale // crt-royale-bloom-horizontal-reconstitute.fxh pass bloomHorizontalPass { + // Blur horizontally for the bloom effect. + // Also apply various color changes and effects. VertexShader = bloomHorizontalVS; PixelShader = bloomHorizontalPS; @@ -173,6 +200,7 @@ technique CRT_Royale // crt-royale-geometry-aa-last-pass.fxh pass geometryPass { + // Apply screen geometry and anti-aliasing. VertexShader = geometryVS; PixelShader = geometryPS; @@ -181,6 +209,8 @@ technique CRT_Royale // content-crop.fxh pass uncropPass { + // Uncrop the video, so we draw the game's content + // in the same position it started in. VertexShader = PostProcessVS; PixelShader = uncropContentPixelShader; } diff --git a/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh b/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh index 32d4421..cecc128 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh @@ -171,8 +171,8 @@ uniform float3 convergence_offset_x < ui_tooltip = "Shift the color channels horizontally"; ui_category = "Electron Beam"; ui_type = "drag"; - ui_min = -4; - ui_max = 4; + ui_min = -10; + ui_max = 10; ui_step = 0.05; > = 0; uniform float3 convergence_offset_y < @@ -180,8 +180,8 @@ uniform float3 convergence_offset_y < ui_tooltip = "Shift the color channels vertically"; ui_category = "Electron Beam"; ui_type = "drag"; - ui_min = -4; - ui_max = 4; + ui_min = -10; + ui_max = 10; ui_step = 0.05; > = 0; uniform int beam_shape_mode < @@ -192,6 +192,15 @@ uniform int beam_shape_mode < "Gaussian\0" "Multi-Source Gaussian\0"; > = 1; +uniform float beam_intensity < + ui_label = "Beam Intensity"; + ui_tooltip = "0.5 recommended for Digital Beam Shape and 0.7 for Gaussian. Adjust from there."; + ui_category = "Electron Beam"; + ui_type = "slider"; + ui_min = 0.01; + ui_max = 2.0; + ui_step = 0.01; +> = 0.7; uniform float beam_min_sigma < ui_label = "Beam Min Sigma"; ui_category = "Electron Beam"; diff --git a/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh b/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh index cd8063e..691d682 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh @@ -325,24 +325,26 @@ float3 sample_rgb_scanline( const float2 scanline_uv_g = tex_uv - float2(offset_u_rgb.g, offset_v_rgb.g); const float2 scanline_uv_b = tex_uv - float2(offset_u_rgb.b, offset_v_rgb.b); + /* const float4 sample_r = tex2D(tex, scanline_uv_r); const float4 sample_g = tex2D(tex, scanline_uv_g); const float4 sample_b = tex2D(tex, scanline_uv_b); + */ - /* + /**/ const float3 sample_r = sample_single_scanline_horizontal( tex, scanline_uv_r, tex_size, texture_size_inv); const float3 sample_g = sample_single_scanline_horizontal( tex, scanline_uv_g, tex_size, texture_size_inv); const float3 sample_b = sample_single_scanline_horizontal( tex, scanline_uv_b, tex_size, texture_size_inv); - */ + /**/ return float3(sample_r.r, sample_g.g, sample_b.b); } else { - return tex2D(tex, tex_uv).rgb; - // return sample_single_scanline_horizontal(tex, tex_uv, tex_size, texture_size_inv); + // return tex2D(tex, tex_uv).rgb; + return sample_single_scanline_horizontal(tex, tex_uv, tex_size, texture_size_inv); } } diff --git a/reshade-shaders/Shaders/crt-royale/shaders/content-crop.fxh b/reshade-shaders/Shaders/crt-royale/shaders/content-crop.fxh index 7455dca..f947841 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/content-crop.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/content-crop.fxh @@ -24,7 +24,7 @@ // The normalized center is 0.5 plus the normalized offset -static const float2 content_center = float2(CONTENT_CENTER_X, CONTENT_CENTER_Y) / buffer_size + 0.5; +static const float2 content_center = (TEXCOORD_OFFSET + float2(CONTENT_CENTER_X, CONTENT_CENTER_Y)) / buffer_size + 0.5; // The content's normalized diameter d is its size divided by the buffer's size. The radius is d/2. static const float2 content_radius = content_size / (2.0 * buffer_size); @@ -53,14 +53,14 @@ void uncropContentPixelShader( out float4 color : SV_Target ) { - const bool is_in_boundary = ( + const bool is_in_boundary = float( texcoord.x >= content_left && texcoord.x <= content_right && texcoord.y >= content_upper && texcoord.y <= content_lower ); const float2 texcoord_uncropped = (texcoord - content_offset) * buffer_size / content_size; - if (is_in_boundary) color = tex2D(samplerGeometry, texcoord_uncropped); - else color = float4(0, 0, 0, 1); + const float3 raw_color = tex2D(samplerGeometry, texcoord_uncropped).rgb; + color = float4(is_in_boundary * raw_color, 1); } #endif // _CONTENT_CROPPING \ No newline at end of file diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-bloom-approx.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-bloom-approx.fxh index 5a69ab9..60658ac 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-bloom-approx.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-bloom-approx.fxh @@ -153,8 +153,8 @@ void approximateBloomPS( out float4 color : SV_Target ) { - const float2 orig_linearized_size = tex2Dsize(samplerOrigLinearized); - const float2 vertical_scanlines_size = tex2Dsize(samplerBeamMisalignment); + const float2 orig_linearized_size = tex2Dsize(samplerBeamConvergence); + const float2 vertical_scanlines_size = tex2Dsize(samplerBeamConvergence); const float2 output_size = TEX_BLOOMAPPROX_SIZE; // const float2 video_uv = vTexCoord * texture_size / video_size; @@ -239,7 +239,7 @@ void approximateBloomPS( // const sampler2D texture = ORIG_LINEARIZED.texture; // const float2 tex_uv = tex_uv; // const float2 blur_dxdy = blur_dxdy; - // const float2 texture_size_ = tex2Dsize(samplerOrigLinearized); + // const float2 texture_size_ = tex2Dsize(samplerBeamConvergence); // const float2 texture_size_inv = texture_size_inv; // const float2 tex_uv_to_pixel_scale = tex_uv_to_pixel_scale; float2 tex_uv_r, tex_uv_g, tex_uv_b; @@ -268,19 +268,19 @@ void approximateBloomPS( // Use a 4x4 Gaussian resize. This is slower but technically correct. if(beam_misconvergence) { - out_color_r = tex2Dresize_gaussian4x4(samplerOrigLinearized, tex_uv_r, + out_color_r = tex2Dresize_gaussian4x4(samplerBeamConvergence, tex_uv_r, blur_dxdy, orig_linearized_size, texture_size_inv, tex_uv_to_pixel_scale, bloom_approx_sigma, get_intermediate_gamma()); - out_color_g = tex2Dresize_gaussian4x4(samplerOrigLinearized, tex_uv_g, + out_color_g = tex2Dresize_gaussian4x4(samplerBeamConvergence, tex_uv_g, blur_dxdy, orig_linearized_size, texture_size_inv, tex_uv_to_pixel_scale, bloom_approx_sigma, get_intermediate_gamma()); - out_color_b = tex2Dresize_gaussian4x4(samplerOrigLinearized, tex_uv_b, + out_color_b = tex2Dresize_gaussian4x4(samplerBeamConvergence, tex_uv_b, blur_dxdy, orig_linearized_size, texture_size_inv, tex_uv_to_pixel_scale, bloom_approx_sigma, get_intermediate_gamma()); } else { - out_color = tex2Dresize_gaussian4x4(samplerOrigLinearized, tex_uv, + out_color = tex2Dresize_gaussian4x4(samplerBeamConvergence, tex_uv, blur_dxdy, orig_linearized_size, texture_size_inv, tex_uv_to_pixel_scale, bloom_approx_sigma, get_intermediate_gamma()); } @@ -292,16 +292,16 @@ void approximateBloomPS( // nicely with convergence offsets, but it has its charms. if(beam_misconvergence) { - out_color_r = tex2Dblur3x3resize(samplerOrigLinearized, tex_uv_r, + out_color_r = tex2Dblur3x3resize(samplerBeamConvergence, tex_uv_r, blur_dxdy, bloom_approx_sigma, get_intermediate_gamma()); - out_color_g = tex2Dblur3x3resize(samplerOrigLinearized, tex_uv_g, + out_color_g = tex2Dblur3x3resize(samplerBeamConvergence, tex_uv_g, blur_dxdy, bloom_approx_sigma, get_intermediate_gamma()); - out_color_b = tex2Dblur3x3resize(samplerOrigLinearized, tex_uv_b, + out_color_b = tex2Dblur3x3resize(samplerBeamConvergence, tex_uv_b, blur_dxdy, bloom_approx_sigma, get_intermediate_gamma()); } else { - out_color = tex2Dblur3x3resize(samplerOrigLinearized, tex_uv, blur_dxdy, get_intermediate_gamma()); + out_color = tex2Dblur3x3resize(samplerBeamConvergence, tex_uv, blur_dxdy, get_intermediate_gamma()); } } else @@ -314,13 +314,13 @@ void approximateBloomPS( // keep bloom_approx_scale_x/min_allowed_viewport_triads < ~1.1658025.) if(beam_misconvergence) { - out_color_r = tex2D_linearize(samplerOrigLinearized, tex_uv_r, get_intermediate_gamma()).rgb; - out_color_g = tex2D_linearize(samplerOrigLinearized, tex_uv_g, get_intermediate_gamma()).rgb; - out_color_b = tex2D_linearize(samplerOrigLinearized, tex_uv_b, get_intermediate_gamma()).rgb; + out_color_r = tex2D_linearize(samplerBeamConvergence, tex_uv_r, get_intermediate_gamma()).rgb; + out_color_g = tex2D_linearize(samplerBeamConvergence, tex_uv_g, get_intermediate_gamma()).rgb; + out_color_b = tex2D_linearize(samplerBeamConvergence, tex_uv_b, get_intermediate_gamma()).rgb; } else { - out_color = tex2D_linearize(samplerOrigLinearized, tex_uv, get_intermediate_gamma()).rgb; + out_color = tex2D_linearize(samplerBeamConvergence, tex_uv, get_intermediate_gamma()).rgb; } } // Pack the colors from the red/green/blue beams into a single vector: @@ -333,9 +333,9 @@ void approximateBloomPS( // Encode and output the blurred image // Currently the bloom-approx logic is completely disabled - const float4 input_color = tex2D_linearize(samplerOrigLinearized, tex_uv, get_intermediate_gamma()); + const float4 input_color = tex2D_linearize(samplerBeamConvergence, tex_uv, get_intermediate_gamma()); color = encode_output(input_color, get_intermediate_gamma()); - // color = tex2D(samplerOrigLinearized, tex_uv); + // color = tex2D(samplerBeamConvergence, tex_uv); // color = encode_output(float4(out_color, 1.0), get_intermediate_gamma()); } \ No newline at end of file diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-blend-frames.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-deinterlace.fxh similarity index 88% rename from reshade-shaders/Shaders/crt-royale/shaders/crt-royale-blend-frames.fxh rename to reshade-shaders/Shaders/crt-royale/shaders/crt-royale-deinterlace.fxh index 8f2510f..c9cb449 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-blend-frames.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-deinterlace.fxh @@ -5,7 +5,7 @@ #include "../lib/scanline-functions.fxh" -void lerpScanlinesVS( +void deinterlaceVS( in const uint id : SV_VertexID, out float4 position : SV_Position, @@ -18,7 +18,7 @@ void lerpScanlinesVS( } -void lerpScanlinesPS( +void deinterlacePS( in const float4 pos : SV_Position, in const float2 texcoord : TEXCOORD0, in const float2 v_step : TEXCOORD1, @@ -34,7 +34,7 @@ void lerpScanlinesPS( const float cur_scanline_idx = get_curr_scanline_idx(texcoord.y, CONTENT_HEIGHT_INTERNAL); const float wrong_field = curr_line_is_wrong_field(cur_scanline_idx); - const float4 cur_line_color = tex2D(samplerBeamMisalignment, texcoord); + const float4 cur_line_color = tex2D(samplerBeamConvergence, texcoord); const float4 cur_line_prev_color = tex2D(samplerFreezeFrame, texcoord); const float4 avg_color = (cur_line_color + cur_line_prev_color) / 2.0; @@ -61,7 +61,7 @@ void lerpScanlinesPS( const float2 curr_offset = lerp(0, raw_offset, wrong_field); const float2 prev_offset = lerp(raw_offset, 0, wrong_field); - const float4 cur_line_color = tex2D(samplerBeamMisalignment, texcoord + curr_offset); + const float4 cur_line_color = tex2D(samplerBeamConvergence, texcoord + curr_offset); const float4 prev_line_color = tex2D(samplerFreezeFrame, texcoord + prev_offset); const float4 avg_color = (cur_line_color + prev_line_color) / 2.0; @@ -70,7 +70,7 @@ void lerpScanlinesPS( } // No temporal blending else { - color = tex2D(samplerBeamMisalignment, texcoord); + color = tex2D(samplerBeamConvergence, texcoord); } } @@ -80,5 +80,5 @@ void freezeFramePS( out float4 color : SV_Target ) { - color = tex2D(samplerBeamMisalignment, texcoord); + color = tex2D(samplerBeamConvergence, texcoord); } \ No newline at end of file diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh index 9069aec..d4107ac 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh @@ -26,7 +26,7 @@ #include "shared-objects.fxh" -void linearizeAndBobVS( +void simulateInterpolationVS( in const uint id : SV_VertexID, out float4 position : SV_Position, @@ -40,7 +40,7 @@ void linearizeAndBobVS( v_step = float2(0.0, 1.0 / content_size.y); } -void linearizeAndBobPS( +void simulateInterpolationPS( in const float4 pos : SV_Position, in const float2 texcoord : TEXCOORD0, in const float interlaced : TEXCOORD1, @@ -66,7 +66,7 @@ void linearizeAndBobPS( color = encode_output(float4(selected_color, 1.0), get_intermediate_gamma()); } -void fancyScanWithElectronBeamsVS( +void simulateEletronBeamsVS( in const uint id : SV_VertexID, out float4 position : SV_Position, @@ -75,18 +75,18 @@ void fancyScanWithElectronBeamsVS( ) { PostProcessVS(id, position, texcoord); - v_step = float2(0.0, 1.0 / tex2Dsize(samplerOrigLinearized).y); + v_step = float2(0.0, 1.0 / tex2Dsize(samplerInterlaced).y); } -void fancyScanWithElectronBeamsPS( +void simulateEletronBeamsPS( in const float4 position : SV_Position, in const float2 texcoord : TEXCOORD0, in const float2 v_step : TEXCOORD1, out float4 color : SV_Target ) { - const float2 orig_linearized_size = tex2Dsize(samplerOrigLinearized); + const float2 orig_linearized_size = tex2Dsize(samplerInterlaced); const float wrong_field = curr_line_is_wrong_field(texcoord.y, orig_linearized_size.y); @@ -96,23 +96,22 @@ void fancyScanWithElectronBeamsPS( // If we're in the current field, draw the beam // wrong_field is always 0 when we aren't interlacing // Double the intensity when interlacing to maintain the same apparent brightness - const float interlacing_brightness_factor = enable_interlacing * float( + const float interlacing_brightness_factor = 1 + enable_interlacing * float( scanline_deinterlacing_mode != 1 && scanline_deinterlacing_mode != 2 ); - const float contrib_factor = interlacing_brightness_factor + 1.0; - // const float3 scanline_color = tex2D_linearize(samplerOrigLinearized, texcoord, get_intermediate_gamma()).rgb; + // const float3 scanline_color = tex2D_linearize(samplerInterlaced, texcoord, get_intermediate_gamma()).rgb; const float3 scanline_color = sample_single_scanline_horizontal( - samplerOrigLinearized, + samplerInterlaced, texcoord, orig_linearized_size, 1 / orig_linearized_size ); - const float3 scanline_intensity = (1 - wrong_field) * contrib_factor * scanline_color; + const float3 scanline_intensity = (1 - wrong_field) * interlacing_brightness_factor * scanline_color; // Temporarily auto-dim the output to avoid clipping. - color = encode_output(float4(scanline_intensity * levels_autodim_temp, 1.0), get_intermediate_gamma()); + color = encode_output(float4(scanline_intensity * beam_intensity, 1.0), get_intermediate_gamma()); } // Gaussian Shape // Beam will be a distorted Gaussian, dependent on color brightness and hyperparameters @@ -151,12 +150,12 @@ void fancyScanWithElectronBeamsPS( // Sample the nearest in-field scanline const float3 scanline_color = sample_single_scanline_horizontal( - samplerOrigLinearized, + samplerInterlaced, source_scanline_center_xy, orig_linearized_size, 1 / orig_linearized_size ); // const float4 scanline_color = decode_input( - // tex2D(samplerOrigLinearized, source_scanline_center_xy), + // tex2D(samplerInterlaced, source_scanline_center_xy), // get_intermediate_gamma() // ); @@ -174,7 +173,7 @@ void fancyScanWithElectronBeamsPS( ); // Output the corrected color - color = encode_output(float4(beam_strength * levels_autodim_temp * 1.5, 1), get_intermediate_gamma()); + color = encode_output(float4(beam_strength * beam_intensity, 1), get_intermediate_gamma()); } // Gaussian Shape // Beam will be a distorted Gaussian, dependent on color brightness and hyperparameters @@ -216,17 +215,17 @@ void fancyScanWithElectronBeamsPS( const float2 lower_scanline_center_xy = float2(texcoord.x, scanline_centers_y.z); const float3 upper_scanline_color = sample_single_scanline_horizontal( - samplerOrigLinearized, + samplerInterlaced, upper_scanline_center_xy, orig_linearized_size, 1 / orig_linearized_size ); const float3 curr_scanline_color = sample_single_scanline_horizontal( - samplerOrigLinearized, + samplerInterlaced, curr_scanline_center_xy, orig_linearized_size, 1 / orig_linearized_size ); const float3 lower_scanline_color = sample_single_scanline_horizontal( - samplerOrigLinearized, + samplerInterlaced, lower_scanline_center_xy, orig_linearized_size, 1 / orig_linearized_size ); @@ -259,21 +258,21 @@ void fancyScanWithElectronBeamsPS( ); // Output the corrected color - color = encode_output(float4(beam_strength * levels_autodim_temp * 1.5, 1), get_intermediate_gamma()); + color = encode_output(float4(beam_strength * beam_intensity, 1), get_intermediate_gamma()); } } -void beamMisaslignmentPS( +void beamConvergencePS( in const float4 position : SV_Position, in const float2 texcoord : TEXCOORD0, out float4 color : SV_TARGET ) { - const float2 scanline_texture_size = tex2Dsize(samplerVerticalScanlines); + const float2 scanline_texture_size = tex2Dsize(samplerElectronBeams); const float2 scanline_texture_size_inv = 1.0 / scanline_texture_size; const float3 offset_sample = sample_rgb_scanline( - samplerVerticalScanlines, texcoord, + samplerElectronBeams, texcoord, scanline_texture_size, scanline_texture_size_inv ); diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-phosphor-mask.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-phosphor-mask.fxh index 0481fcb..74662b3 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-phosphor-mask.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-phosphor-mask.fxh @@ -129,7 +129,7 @@ void applyPhosphorMaskPS( out float4 color : SV_Target ) { - const float2 scanline_texture_size = tex2Dsize(samplerBlendScanline); + const float2 scanline_texture_size = tex2Dsize(samplerDeinterlace); // const float2 output_size = tex2Dsize(samplerMaskedScanlines); const float2 output_size = TEX_MASKEDSCANLINES_SIZE; @@ -146,7 +146,7 @@ void applyPhosphorMaskPS( // const float3 scanline_color_dim = sample_rgb_scanline_horizontal( // samplerVerticalOffset, texcoord, // scanline_texture_size, scanline_texture_size_inv); - const float3 scanline_color_dim = tex2D(samplerBlendScanline, texcoord).rgb; + const float3 scanline_color_dim = tex2D(samplerDeinterlace, texcoord).rgb; const float auto_dim_factor = levels_autodim_temp; const float2 true_tile_size = get_downsizing_factor_and_true_tile_size().yz; diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-scanlines-horizontal-apply-mask.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-scanlines-horizontal-apply-mask.fxh deleted file mode 100644 index c86458a..0000000 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-scanlines-horizontal-apply-mask.fxh +++ /dev/null @@ -1,175 +0,0 @@ -///////////////////////////// GPL LICENSE NOTICE ///////////////////////////// - -// crt-royale: A full-featured CRT shader, with cheese. -// Copyright (C) 2014 TroggleMonkey -// -// crt-royale-reshade: A port of TroggleMonkey's crt-royale from libretro to ReShade. -// Copyright (C) 2020 Alex Gunter -// -// This program 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 2 of the License, or any later version. -// -// This program 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 -// this program; if not, write to the Free Software Foundation, Inc., 59 Temple -// Place, Suite 330, Boston, MA 02111-1307 USA - -#include "../lib/bind-shader-params.fxh" -#include "../lib/gamma-management.fxh" -#include "../lib/phosphor-mask-resizing.fxh" - -#include "../lib/texture-settings.fxh" -#include "shared-objects.fxh" - - -void applyPhosphorMaskPS( - in const float4 pos : SV_Position, - in const float2 texcoord : TEXCOORD0, - - out float4 color : SV_Target -) { - const float2 scanline_texture_size = tex2Dsize(samplerVerticalOffset); - // const float2 output_size = tex2Dsize(samplerMaskedScanlines); - const float2 output_size = TEX_MASKEDSCANLINES_SIZE; - - // Our various input textures use different coords. - const float2 scanline_texture_size_inv = 1.0 / scanline_texture_size; - - // This pass: Sample (misconverged?) scanlines to the final horizontal - // resolution, apply halation (bouncing electrons), and apply the phosphor - // mask. Fake a bloom if requested. Unless we fake a bloom, the output - // will be dim from the scanline auto-dim, mask dimming, and low gamma. - - // Horizontally sample the current row (a vertically interpolated scanline) - // and account for horizontal convergence offsets, given in units of texels. - // const float3 scanline_color_dim = sample_rgb_scanline_horizontal( - // samplerVerticalOffset, texcoord, - // scanline_texture_size, scanline_texture_size_inv); - const float3 scanline_color_dim = tex2D(samplerVerticalOffset, texcoord).rgb; - const float auto_dim_factor = levels_autodim_temp; - - const float2 true_tile_size = get_downsizing_factor_and_true_tile_size().yz; - const float2 tiles_per_screen = content_size / true_tile_size; - - float3 phosphor_mask_sample; - if(mask_sample_mode_desired > 0.5) - { - const float2 tile_uv_wrap = texcoord * tiles_per_screen; - phosphor_mask_sample = samplePhosphorMask(tile_uv_wrap).rgb; - } - else - { - const float2 tile_uv_wrap = frac(texcoord * tiles_per_screen); - const float2 tile_uv_crop = tile_uv_wrap * true_tile_size / TEX_MASKHORIZONTAL_SIZE; - - // Sample the resized mask, and avoid tiling artifacts: - phosphor_mask_sample = tex2D(samplerMaskResizeHorizontal, tile_uv_crop).rgb; - } - - // Sample the halation texture (auto-dim to match the scanlines), and - // account for both horizontal and vertical convergence offsets, given - // in units of texels horizontally and same-field scanlines vertically: - const float3 halation_color = tex2D_linearize(samplerBlurHorizontal, texcoord, get_intermediate_gamma()).rgb; - - // Apply halation: Halation models electrons flying around under the glass - // and hitting the wrong phosphors (of any color). It desaturates, so - // average the halation electrons to a scalar. Reduce the local scanline - // intensity accordingly to conserve energy. - const float halation_intensity_dim_scalar = dot(halation_color, auto_dim_factor / float3(3, 3, 3)); - const float3 halation_intensity_dim = halation_intensity_dim_scalar * float3(1, 1, 1); - const float3 electron_intensity_dim = lerp(scanline_color_dim, halation_intensity_dim, halation_weight); - - // Apply the phosphor mask: - const float3 phosphor_emission_dim = electron_intensity_dim * phosphor_mask_sample; - - #ifdef PHOSPHOR_BLOOM_FAKE - // The BLOOM_APPROX pass approximates a blurred version of a masked - // and scanlined image. It's usually used to compute the brightpass, - // but we can also use it to fake the bloom stage entirely. Caveats: - // 1.) A fake bloom is conceptually different, since we're mixing in a - // fully blurred low-res image, and the biggest implication are: - // 2.) If mask_amplify is incorrect, results deteriorate more quickly. - // 3.) The inaccurate blurring hurts quality in high-contrast areas. - // 4.) The bloom_underestimate_levels parameter seems less sensitive. - // Reverse the auto-dimming and amplify to compensate for mask dimming: - #define PHOSPHOR_BLOOM_FAKE_WITH_SIMPLE_BLEND - #ifdef PHOSPHOR_BLOOM_FAKE_WITH_SIMPLE_BLEND - static const float blur_contrast = 1.05; - #else - static const float blur_contrast = 1.0; - #endif - const float mask_amplify = get_mask_amplify(); - const float undim_factor = 1.0/auto_dim_factor; - const float3 phosphor_emission = - phosphor_emission_dim * undim_factor * mask_amplify; - // Get a phosphor blur estimate, accounting for convergence offsets: - const float3 electron_intensity = electron_intensity_dim * undim_factor; - const float3 phosphor_blur_approx_soft = tex2D_linearize( - samplerBloomApprox, texcoord, get_intermediate_gamma()).rgb; - const float3 phosphor_blur_approx = lerp(phosphor_blur_approx_soft, - electron_intensity, 0.1) * blur_contrast; - // We could blend between phosphor_emission and phosphor_blur_approx, - // solving for the minimum blend_ratio that avoids clipping past 1.0: - // 1.0 >= total_intensity - // 1.0 >= phosphor_emission * (1.0 - blend_ratio) + - // phosphor_blur_approx * blend_ratio - // blend_ratio = (phosphor_emission - 1.0)/ - // (phosphor_emission - phosphor_blur_approx); - // However, this blurs far more than necessary, because it aims for - // full brightness, not minimal blurring. To fix it, base blend_ratio - // on a max area intensity only so it varies more smoothly: - const float3 phosphor_blur_underestimate = - phosphor_blur_approx * bloom_underestimate_levels; - const float3 area_max_underestimate = - phosphor_blur_underestimate * mask_amplify; - #ifdef PHOSPHOR_BLOOM_FAKE_WITH_SIMPLE_BLEND - const float3 blend_ratio_temp = - (area_max_underestimate - float3(1.0, 1.0, 1.0)) / - (area_max_underestimate - phosphor_blur_underestimate); - #else - // Try doing it like an area-based brightpass. This is nearly - // identical, but it's worth toying with the code in case I ever - // find a way to make it look more like a real bloom. (I've had - // some promising textures from combining an area-based blend ratio - // for the phosphor blur and a more brightpass-like blend-ratio for - // the phosphor emission, but I haven't found a way to make the - // brightness correct across the whole color range, especially with - // different bloom_underestimate_levels values.) - const float desired_triad_size = lerp(mask_triad_size_desired, - output_size.x/mask_num_triads_desired, - mask_specify_num_triads); - const float bloom_sigma = get_min_sigma_to_blur_triad( - desired_triad_size, bloom_diff_thresh); - const float center_weight = get_center_weight(bloom_sigma); - const float3 max_area_contribution_approx = - max(float3(0.0, 0.0, 0.0), phosphor_blur_approx - - center_weight * phosphor_emission); - const float3 area_contrib_underestimate = - bloom_underestimate_levels * max_area_contribution_approx; - const float3 blend_ratio_temp = - ((float3(1.0, 1.0, 1.0) - area_contrib_underestimate) / - area_max_underestimate - float3(1.0, 1.0, 1.0)) / (center_weight - 1.0); - #endif - // Clamp blend_ratio in case it's out-of-range, but be SUPER careful: - // min/max/clamp are BIZARRELY broken with lerp (optimization bug?), - // and this redundant sequence avoids bugs, at least on nVidia cards: - const float3 blend_ratio_clamped = max(clamp(blend_ratio_temp, 0.0, 1.0), 0.0); - const float3 blend_ratio = lerp(blend_ratio_clamped, float3(1.0,1.0,1.0), bloom_excess); - // Blend the blurred and unblurred images: - const float3 phosphor_emission_unclipped = - lerp(phosphor_emission, phosphor_blur_approx, blend_ratio); - // Simulate refractive diffusion by reusing the halation sample. - const float3 pixel_color = lerp(phosphor_emission_unclipped, - halation_color, diffusion_weight); - #else - const float3 pixel_color = phosphor_emission_dim; - #endif - // Encode if necessary, and output. - - color = encode_output(float4(pixel_color, 1.0), get_intermediate_gamma()); -} \ No newline at end of file diff --git a/reshade-shaders/Shaders/crt-royale/shaders/shared-objects.fxh b/reshade-shaders/Shaders/crt-royale/shaders/shared-objects.fxh index 62e43ab..37fd264 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/shared-objects.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/shared-objects.fxh @@ -25,7 +25,6 @@ #include "../lib/texture-settings.fxh" - #if __RENDERER__ != 0x9000 #define TEXCOORD_OFFSET 0.0 #else @@ -58,23 +57,21 @@ static const float2 buffer_size = float2(BUFFER_WIDTH, BUFFER_HEIGHT); static const float2 content_size = float2(CONTENT_WIDTH_INTERNAL, CONTENT_HEIGHT_INTERNAL); +uniform int frame_count < source = "framecount"; >; -// Initial Color Buffer -// texture2D texColorBuffer : COLOR; -// sampler2D samplerColor { -// Texture = texColorBuffer; - -// MagFilter = NONE; -// MinFilter = NONE; -// MipFilter = NONE; -// }; // Yes, the WIDTH/HEIGHT/SIZE defines are kinda weird. // Yes, we have to have them or something similar. This is for D3D11 which -// returns (0, 0) when you call tex2Dsize() on the pass's output texture. +// returns (0, 0) when you call tex2Dsize() on the pass's render target. -// Crop pass +// Pass 0 Buffer (cropPass) +// Cannot be conditioned on __RENDERER__ b/c there are no +// available buffers of the same size +// Last usage is in interlacingPass +// electronBeamPass -> beamConvergencePass +// deinterlacePass -> phosphorMaskPass +// brightpassPass -> bloomHorizontalPass #define TEX_CROP_WIDTH CONTENT_WIDTH_INTERNAL #define TEX_CROP_HEIGHT CONTENT_HEIGHT_INTERNAL #define TEX_CROP_SIZE int2(TEX_CROP_WIDTH, TEX_CROP_HEIGHT) @@ -86,43 +83,67 @@ texture2D texCrop { }; sampler2D samplerCrop { Texture = texCrop; }; -// Pass 0 Buffer (ORIGLINEARIZED) -#define TEX_ORIGLINEARIZED_WIDTH CONTENT_WIDTH_INTERNAL -#define TEX_ORIGLINEARIZED_HEIGHT CONTENT_HEIGHT_INTERNAL -#define TEX_ORIGLINEARIZED_SIZE int2(TEX_ORIGLINEARIZED_WIDTH, TEX_ORIGLINEARIZED_HEIGHT) -texture2D texOrigLinearized { - Width = TEX_ORIGLINEARIZED_WIDTH; - Height = TEX_ORIGLINEARIZED_HEIGHT; + +// Pass 1 Buffer (interlacingPass) +// Cannot be conditioned on __RENDERER__ b/c there are no +// available buffers of the same size +// Last usage is in electronBeamPass +// beamConvergencPass -> freezeFramePass +// phosphorMaskPass -> bloomHorizontalPass +#define TEX_INTERLACED_WIDTH CONTENT_WIDTH_INTERNAL +#define TEX_INTERLACED_HEIGHT CONTENT_HEIGHT_INTERNAL +#define TEX_INTERLACED_SIZE int2(TEX_INTERLACED_WIDTH, TEX_INTERLACED_HEIGHT) +texture2D texInterlaced { + Width = TEX_INTERLACED_WIDTH; + Height = TEX_INTERLACED_HEIGHT; Format = RGBA16; }; -sampler2D samplerOrigLinearized { Texture = texOrigLinearized; }; +sampler2D samplerInterlaced { Texture = texInterlaced; }; -// Pass 1 Buffer (VERTICALSCANLINES) -#define TEX_VERTICALSCANLINES_WIDTH CONTENT_WIDTH_INTERNAL -#define TEX_VERTICALSCANLINES_HEIGHT CONTENT_HEIGHT_INTERNAL -#define TEX_VERTICALSCANLINES_SIZE int2(TEX_VERTICALSCANLINES_WIDTH, TEX_VERTICALSCANLINES_HEIGHT) -texture2D texVerticalScanlines { - Width = TEX_VERTICALSCANLINES_WIDTH; - Height = TEX_VERTICALSCANLINES_HEIGHT; - Format = RGBA16; -}; -sampler2D samplerVerticalScanlines { Texture = texVerticalScanlines; }; - -// Pass 2 Buffer (BEAMMISALIGNMENT) -#define TEX_BEAMMISALIGNMENT_WIDTH CONTENT_WIDTH_INTERNAL -#define TEX_BEAMMISALIGNMENT_HEIGHT CONTENT_HEIGHT_INTERNAL -#define TEX_BEAMMISALIGNMENT_SIZE int2(TEX_BEAMMISALIGNMENT_WIDTH, TEX_BEAMMISALIGNMENT_HEIGHT) -texture2D texBeamMisalignment { - Width = TEX_BEAMMISALIGNMENT_WIDTH; - Height = TEX_BEAMMISALIGNMENT_HEIGHT; - - Format = RGBA16; -}; -sampler2D samplerBeamMisalignment { Texture = texBeamMisalignment; }; +// Pass 2 Buffer (electronBeamPass) +// Last usage is in beamConvergencePass +#define TEX_ELECTRONBEAMS_WIDTH CONTENT_WIDTH_INTERNAL +#define TEX_ELECTRONBEAMS_HEIGHT CONTENT_HEIGHT_INTERNAL +#define TEX_ELECTRONBEAMS_SIZE int2(TEX_ELECTRONBEAMS_WIDTH, TEX_ELECTRONBEAMS_HEIGHT) +#if __RENDERER__ != 0x9000 + texture2D texElectronBeams { + Width = TEX_ELECTRONBEAMS_WIDTH; + Height = TEX_ELECTRONBEAMS_HEIGHT; -// Pass 2 Buffer (BLOOM_APPROX) + Format = RGBA16; + }; + sampler2D samplerElectronBeams { Texture = texElectronBeams; }; +#else + #define texElectronBeams texCrop + #define samplerElectronBeams samplerCrop +#endif + + +// Pass 3 Buffer (beamConvergencPass) +// Last usage is freezeFramePass +#define TEX_BEAMCONVERGENCE_WIDTH CONTENT_WIDTH_INTERNAL +#define TEX_BEAMCONVERGENCE_HEIGHT CONTENT_HEIGHT_INTERNAL +#define TEX_BEAMCONVERGENCE_SIZE int2(TEX_BEAMCONVERGENCE_WIDTH, TEX_BEAMCONVERGENCE_HEIGHT) +#if __RENDERER__ != 0x9000 + texture2D texBeamConvergence { + Width = TEX_BEAMCONVERGENCE_WIDTH; + Height = TEX_BEAMCONVERGENCE_HEIGHT; + + Format = RGBA16; + }; + sampler2D samplerBeamConvergence { Texture = texBeamConvergence; }; +#else + #define texBeamConvergence texInterlaced + #define samplerBeamConvergence samplerInterlaced +#endif + + +// Pass 4 Buffer (bloomApproxPass) +// Cannot be conditioned on __RENDERER__ b/c there are no +// available buffers of the same size +// Last usage is in brightpassPass #define TEX_BLOOMAPPROX_WIDTH 320 #define TEX_BLOOMAPPROX_HEIGHT 240 #define TEX_BLOOMAPPROX_SIZE int2(TEX_BLOOMAPPROX_WIDTH, TEX_BLOOMAPPROX_HEIGHT) @@ -135,7 +156,10 @@ texture2D texBloomApprox { sampler2D samplerBloomApprox { Texture = texBloomApprox; }; -// Pass 3 Buffer +// Pass 5 Buffer (blurVerticalPass) +// Cannot be conditioned on __RENDERER__ b/c there are no +// available buffers of the same size +// Last usage is blurHorizontalPass #define TEX_BLURVERTICAL_WIDTH TEX_BLOOMAPPROX_WIDTH #define TEX_BLURVERTICAL_HEIGHT TEX_BLOOMAPPROX_HEIGHT #define TEX_BLURVERTICAL_SIZE int2(TEX_BLURVERTICAL_WIDTH, TEX_BLURVERTICAL_HEIGHT) @@ -148,7 +172,10 @@ texture2D texBlurVertical { sampler2D samplerBlurVertical { Texture = texBlurVertical; }; -// Pass 4 Buffer (HALATION_BLUR) +// Pass 6 Buffer (blurHorizontalPass) +// Cannot be conditioned on __RENDERER__ b/c there are no +// available buffers of the same size +// Last usage is bloomHorizontalPass #define TEX_BLURHORIZONTAL_WIDTH TEX_BLOOMAPPROX_WIDTH #define TEX_BLURHORIZONTAL_HEIGHT TEX_BLOOMAPPROX_HEIGHT #define TEX_BLURHORIZONTAL_SIZE int2(TEX_BLURHORIZONTAL_WIDTH, TEX_BLURHORIZONTAL_HEIGHT) @@ -161,7 +188,41 @@ texture2D texBlurHorizontal { sampler2D samplerBlurHorizontal { Texture = texBlurHorizontal; }; -// Pass 5 Mask Texture +// Pass 7 (deinterlacePass) +// Last usage is phosphorMaskPass +#define TEX_DEINTERLACE_WIDTH CONTENT_WIDTH_INTERNAL +#define TEX_DEINTERLACE_HEIGHT CONTENT_HEIGHT_INTERNAL +#define TEX_DEINTERLACE_SIZE int2(TEX_DEINTERLACE_WIDTH, TEX_DEINTERLACE_HEIGHT) +#if __RENDERER__ != 0x9000 + texture2D texDeinterlace { + Width = TEX_DEINTERLACE_WIDTH; + Height = TEX_DEINTERLACE_HEIGHT; + + Format = RGBA16; + }; + sampler2D samplerDeinterlace { Texture = texDeinterlace; }; +#else + #define texDeinterlace texCrop + #define samplerDeinterlace samplerCrop +#endif + +// Pass 8 (freezeFramePass) +// Do not condition this on __RENDERER__. It will not work if another +// pass corrupts it. +#define TEX_FREEZEFRAME_WIDTH CONTENT_WIDTH_INTERNAL +#define TEX_FREEZEFRAME_HEIGHT CONTENT_HEIGHT_INTERNAL +#define TEX_FREEZEFRAME_SIZE int2(TEX_FREEZEFRAME_WIDTH, TEX_FREEZEFRAME_HEIGHT +texture2D texFreezeFrame { + Width = TEX_FREEZEFRAME_WIDTH; + Height = TEX_FREEZEFRAME_HEIGHT; + + Format = RGBA16; +}; +sampler2D samplerFreezeFrame { Texture = texFreezeFrame; }; + +// Pass 9 Mask Texture (phosphorMaskResizeVerticalPass) +// Cannot be conditioned on __RENDERER__ b/c there are no +// available buffers of the same size // TODO: Figure out how to set these to 144 insead of 512 // without losing data during downsampling #define TEX_MASKVERTICAL_WIDTH mask_size_xy @@ -180,7 +241,9 @@ sampler2D samplerMaskResizeVertical { }; -// Pass 6 Mask Texture (MASK_RESIZE) +// Pass 10 Mask Texture (phosphorMaskResizeHorizontalPass) +// Cannot be conditioned on __RENDERER__ b/c there are no +// available buffers of the same size // TODO: Figure out how to set these to 144 insead of 512 // without losing data during downsampling #define TEX_MASKHORIZONTAL_WIDTH mask_size_xy @@ -199,7 +262,8 @@ sampler2D samplerMaskResizeHorizontal { }; -// Pass 7 Buffer (MASKED_SCANLINES) +// Pass 11 Buffer (phosphorMaskPass) +// Last usage is bloomHorizontalPass #define TEX_MASKEDSCANLINES_WIDTH CONTENT_WIDTH_INTERNAL #define TEX_MASKEDSCANLINES_HEIGHT CONTENT_HEIGHT_INTERNAL #define TEX_MASKEDSCANLINES_SIZE int2(TEX_MASKEDSCANLINES_WIDTH, TEX_MASKEDSCANLINES_HEIGHT) @@ -213,15 +277,13 @@ sampler2D samplerMaskResizeHorizontal { }; sampler2D samplerMaskedScanlines { Texture = texMaskedScanlines; }; #else - #define texMaskedScanlines texCrop - #define samplerMaskedScanlines samplerCrop + #define texMaskedScanlines texInterlaced + #define samplerMaskedScanlines samplerInterlaced #endif - - - -// Pass 8 Buffer (BRIGHTPASS) +// Pass 12 Buffer (brightpassPass) +// Last usage is bloomHorizontalPass #define TEX_BRIGHTPASS_WIDTH CONTENT_WIDTH_INTERNAL #define TEX_BRIGHTPASS_HEIGHT CONTENT_HEIGHT_INTERNAL #define TEX_BRIGHTPASS_SIZE int2(TEX_BRIGHTPASS_WIDTH, TEX_BRIGHTPASS_HEIGHT) @@ -235,19 +297,18 @@ sampler2D samplerMaskResizeHorizontal { }; sampler2D samplerBrightpass { Texture = texBrightpass; }; #else - #define texBrightpass texOrigLinearized - #define samplerBrightpass samplerOrigLinearized + #define texBrightpass texCrop + #define samplerBrightpass samplerCrop #endif - - - -// Pass 9 Buffer +// Pass 13 Buffer (bloomVerticalPass) +// Cannot be conditioned on __RENDERER__ b/c there are no +// available buffers of the same size +// Last usage is bloomHorizontalPass #define TEX_BLOOMVERTICAL_WIDTH CONTENT_WIDTH_INTERNAL #define TEX_BLOOMVERTICAL_HEIGHT CONTENT_HEIGHT_INTERNAL #define TEX_BLOOMVERTICAL_SIZE int2(TEX_BLOOMVERTICAL_WIDTH, TEX_BLOOMVERTICAL_HEIGHT) - texture2D texBloomVertical { Width = TEX_BLOOMVERTICAL_WIDTH; Height = TEX_BLOOMVERTICAL_HEIGHT; @@ -257,7 +318,10 @@ texture2D texBloomVertical { sampler2D samplerBloomVertical { Texture = texBloomVertical; }; -// Pass 10 Buffer +// Pass 14 Buffer (bloomHorizontalPass) +// Cannot be conditioned on __RENDERER__ b/c there are no +// available buffers of the same size +// Last usage is geometryPass #define TEX_BLOOMHORIZONTAL_WIDTH CONTENT_WIDTH_INTERNAL #define TEX_BLOOMHORIZONTAL_HEIGHT CONTENT_HEIGHT_INTERNAL #define TEX_BLOOMHORIZONTAL_SIZE int2(TEX_BLOOMHORIZONTAL_WIDTH, TEX_BLOOMHORIZONTAL_HEIGHT) @@ -270,7 +334,8 @@ texture2D texBloomHorizontal { sampler2D samplerBloomHorizontal { Texture = texBloomHorizontal; }; -// Pass 11 Buffer +// Pass 15 Buffer (geometryPass) +// Last usage is uncropPass #define TEX_GEOMETRY_WIDTH CONTENT_WIDTH_INTERNAL #define TEX_GEOMETRY_HEIGHT CONTENT_HEIGHT_INTERNAL #define TEX_GEOMETRY_SIZE int2(TEX_GEOMETRY_WIDTH, TEX_GEOMETRY_HEIGHT) @@ -288,32 +353,4 @@ sampler2D samplerBloomHorizontal { Texture = texBloomHorizontal; }; #define samplerGeometry samplerCrop #endif - -// Scanline Blend Buffer -#define TEX_BLENDSCANLINE_WIDTH CONTENT_WIDTH_INTERNAL -#define TEX_BLENDSCANLINE_HEIGHT CONTENT_HEIGHT_INTERNAL -#define TEX_BLENDSCANLINE_SIZE int2(TEX_BLENDSCANLINE_WIDTH, TEX_BLENDSCANLINE_HEIGHT) -texture2D texBlendScanline { - Width = TEX_BLENDSCANLINE_WIDTH; - Height = TEX_BLENDSCANLINE_HEIGHT; - - Format = RGBA16; -}; -sampler2D samplerBlendScanline { Texture = texBlendScanline; }; - -// Frame Merge Buffer -#define TEX_FREEZEFRAME_WIDTH CONTENT_WIDTH_INTERNAL -#define TEX_FREEZEFRAME_HEIGHT CONTENT_HEIGHT_INTERNAL -#define TEX_FREEZEFRAME_SIZE int2(TEX_FREEZEFRAME_WIDTH, TEX_FREEZEFRAME_HEIGHT -texture2D texFreezeFrame { - Width = TEX_FREEZEFRAME_WIDTH; - Height = TEX_FREEZEFRAME_HEIGHT; - - Format = RGBA16; -}; -sampler2D samplerFreezeFrame { Texture = texFreezeFrame; }; - - -uniform int frame_count < source = "framecount"; >; - #endif // _SHARED_OBJECTS_H \ No newline at end of file From 8e12a4a47f4a91a6c8eac3e5b61d1a943e876cbc Mon Sep 17 00:00:00 2001 From: Alex Gunter Date: Fri, 24 Dec 2021 18:41:19 -0600 Subject: [PATCH 08/16] Swapped to fancier gaussian beam math --- .../crt-royale/lib/bind-shader-params.fxh | 5 ++-- .../crt-royale/lib/scanline-functions.fxh | 23 +++++++++++++++++++ .../shaders/crt-royale-electron-beams.fxh | 6 ++--- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh b/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh index cecc128..c5ecfd8 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh @@ -161,7 +161,7 @@ uniform bool interlace_bff < // ==== ELECTRON BEAM ==== // static const float beam_min_sigma = beam_min_sigma_static; // static const float beam_max_sigma = beam_max_sigma_static; -static const float beam_spot_power = beam_spot_power_static; +// static const float beam_spot_power = beam_spot_power_static; // static const float beam_min_shape = beam_min_shape_static; // static const float beam_max_shape = beam_max_shape_static; static const float beam_shape_power = beam_shape_power_static; @@ -216,14 +216,13 @@ uniform float beam_max_sigma < ui_min = 0.0; ui_step = 0.01; > = beam_max_sigma_static; -/* uniform float beam_spot_power < ui_label = "Beam Spot Power"; + ui_category = "Electron Beam"; ui_type = "drag"; ui_min = 0.0; ui_step = 0.01; > = beam_spot_power_static; -*/ uniform float beam_min_shape < ui_label = "Beam Min Shape"; ui_category = "Electron Beam"; diff --git a/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh b/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh index 691d682..dfdcf00 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh @@ -460,6 +460,9 @@ float curr_line_is_wrong_field( float3 get_beam_strength(float3 dist, float3 color, const float sigma_range, const float shape_range) { + // entry point in original is scanline_contrib() + // this is based on scanline_gaussian_sampled_contrib() from original + // See scanline_gaussian_integral_contrib() for detailed comments! // gaussian sample = 1/(sigma*sqrt(2*pi)) * e**(-(x**2)/(2*sigma**2)) const float3 sigma = get_gaussian_sigma(color, sigma_range); @@ -471,5 +474,25 @@ float3 get_beam_strength(float3 dist, float3 color, return color*exp(-(dist*dist)*inner_denom_inv)*outer_denom_inv; } +float3 get_fancy_beam_strength(float3 dist, float3 color, + const float sigma_range, const float shape_range) +{ + // entry point in original is scanline_contrib() + // this is based on scanline_generalized_gaussian_sampled_contrib() from original + + // See scanline_generalized_gaussian_integral_contrib() for details! + // generalized sample = + // beta/(2*alpha*gamma(1/beta)) * e**(-(|x|/alpha)**beta) + const float3 alpha = sqrt(2.0) * get_gaussian_sigma(color, sigma_range); + const float3 beta = get_generalized_gaussian_beta(color, shape_range); + // Avoid repeated divides: + const float3 alpha_inv = 1.0 / alpha; + const float3 beta_inv = 1.0 / beta; + const float3 scale = color * beta * 0.5 * alpha_inv / + gamma_impl(beta_inv, beta); + + return scale * exp(-pow(abs(dist*alpha_inv), beta)); +} + #endif // _SCANLINE_FUNCTIONS_H \ No newline at end of file diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh index d4107ac..80c8f48 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh @@ -239,15 +239,15 @@ void simulateEletronBeamsPS( const float3 beam_dists_v = abs(curr_line_texel_v - scanline_centers_v); const float3 beam_dists_y = beam_dists_v / max_beam_dist; - const float3 upper_beam_strength = get_beam_strength( + const float3 upper_beam_strength = get_fancy_beam_strength( beam_dists_y.x, upper_scanline_color, sigma_range, shape_range ); - const float3 curr_beam_strength = get_beam_strength( + const float3 curr_beam_strength = get_fancy_beam_strength( beam_dists_y.y, curr_scanline_color, sigma_range, shape_range ); - const float3 lower_beam_strength = get_beam_strength( + const float3 lower_beam_strength = get_fancy_beam_strength( beam_dists_y.z, lower_scanline_color, sigma_range, shape_range ); From 73dad6d44cdd2d3749352099d0a05de58613ae06 Mon Sep 17 00:00:00 2001 From: Alex Gunter Date: Sun, 26 Dec 2021 07:56:15 -0600 Subject: [PATCH 09/16] config cleanup and small tweaks to logic --- reshade-shaders/Shaders/crt-royale.fx | 4 +- .../crt-royale/lib/bind-shader-params.fxh | 39 ++++++++++--------- .../lib/derived-settings-and-constants.fxh | 4 +- .../crt-royale/lib/scanline-functions.fxh | 8 ++++ .../Shaders/crt-royale/lib/user-settings.fxh | 8 ++-- .../shaders/crt-royale-electron-beams.fxh | 6 +-- 6 files changed, 40 insertions(+), 29 deletions(-) diff --git a/reshade-shaders/Shaders/crt-royale.fx b/reshade-shaders/Shaders/crt-royale.fx index 58ae424..e68009e 100644 --- a/reshade-shaders/Shaders/crt-royale.fx +++ b/reshade-shaders/Shaders/crt-royale.fx @@ -23,6 +23,8 @@ #define CONTENT_BOX_VISIBLE 0 #endif +#include "crt-royale/shaders/content-box.fxh" + #if !CONTENT_BOX_VISIBLE #include "crt-royale/shaders/content-crop.fxh" #include "crt-royale/shaders/crt-royale-electron-beams.fxh" @@ -35,8 +37,6 @@ #include "crt-royale/shaders/crt-royale-bloom-vertical.fxh" #include "crt-royale/shaders/crt-royale-bloom-horizontal-reconstitute.fxh" #include "crt-royale/shaders/crt-royale-geometry-aa-last-pass.fxh" -#else - #include "crt-royale/shaders/content-box.fxh" #endif diff --git a/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh b/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh index c5ecfd8..092ed94 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh @@ -119,23 +119,23 @@ uniform float mask_num_triads_desired < // ==== INTERLACING ==== uniform bool enable_interlacing < ui_label = "Enable Interlacing"; - ui_category = "Interlacing"; + ui_category = "Interlacing and Scanlines"; // ui_type = "combo"; // ui_items = "No\0Yes\0"; > = true; uniform int scanline_deinterlacing_mode < ui_label = "Deinterlacing Mode"; ui_tooltip = "Selects the deinterlacing algorithm. For crt-royale's original appearance, choose None."; - ui_category = "Interlacing"; + ui_category = "Interlacing and Scanlines"; ui_type = "combo"; ui_items = "None\0" "Weaving\0" "Blended Weaving\0" "Static\0"; -> = 0; +> = 1; uniform float scanline_num_pixels < ui_label = "Scanline Thickness"; - ui_category = "Interlacing"; + ui_category = "Interlacing and Scanlines"; ui_type = "slider"; ui_min = 1.0; ui_max = 30.0; @@ -144,7 +144,7 @@ uniform float scanline_num_pixels < uniform float scanline_blend_gamma < ui_label = "Scanline Blend Gamma"; ui_tooltip = "Nudge this if Scanline Blend Strength changes your colors too much"; - ui_category = "Interlacing"; + ui_category = "Interlacing and Scanlines"; ui_type = "slider"; ui_min = 0.01; ui_max = 5.0; @@ -154,7 +154,7 @@ uniform bool interlace_bff < // ui_type = "combo"; ui_label = "Draw Back-Field First"; ui_tooltip = "Draw odd-numbered scanlines first (often has no effect)"; - ui_category = "Interlacing"; + ui_category = "Interlacing and Scanlines"; // ui_items = "No\0Yes\0"; > = interlace_bff_static; @@ -192,6 +192,7 @@ uniform int beam_shape_mode < "Gaussian\0" "Multi-Source Gaussian\0"; > = 1; +/* uniform float beam_intensity < ui_label = "Beam Intensity"; ui_tooltip = "0.5 recommended for Digital Beam Shape and 0.7 for Gaussian. Adjust from there."; @@ -201,6 +202,7 @@ uniform float beam_intensity < ui_max = 2.0; ui_step = 0.01; > = 0.7; +*/ uniform float beam_min_sigma < ui_label = "Beam Min Sigma"; ui_category = "Electron Beam"; @@ -210,7 +212,7 @@ uniform float beam_min_sigma < > = beam_min_sigma_static; uniform float beam_max_sigma < ui_label = "Beam Max Sigma"; - ui_tooltip = "Must be >= Beam Min Sigma"; + ui_tooltip = "Should be >= Beam Min Sigma"; ui_category = "Electron Beam"; ui_type = "drag"; ui_min = 0.0; @@ -232,7 +234,7 @@ uniform float beam_min_shape < > = beam_min_shape_static; uniform float beam_max_shape < ui_label = "Beam Max Shape"; - ui_tooltip = "Must be >= Beam Min Shape"; + ui_tooltip = "Should be >= Beam Min Shape"; ui_category = "Electron Beam"; ui_type = "drag"; ui_min = 0.0; @@ -296,6 +298,7 @@ uniform float lcd_gamma < > = lcd_gamma_static; uniform float levels_contrast < ui_label = "Levels Contrast"; + ui_tooltip = "2.0 recommended for Gaussian Beam Shapes. 1.0 for Digital."; ui_category = "Colors and Effects"; ui_type = "slider"; ui_min = 0.0; @@ -337,14 +340,16 @@ uniform float bloom_excess < ui_max = 1.0; ui_step = 0.01; > = bloom_excess_static; -uniform float2 aa_subpixel_r_offset_runtime < - ui_label = "AA Subpixel R Offet XY"; - ui_category = "Colors and Effects"; - ui_type = "drag"; - ui_min = -0.5; - ui_max = 0.5; - ui_step = 0.01; -> = aa_subpixel_r_offset_static; +#if _RUNTIME_ANTIALIAS_SUBPIXEL_OFFSETS + uniform float2 aa_subpixel_r_offset_runtime < + ui_label = "AA Subpixel R Offet XY"; + ui_category = "Colors and Effects"; + ui_type = "drag"; + ui_min = -0.5; + ui_max = 0.5; + ui_step = 0.01; + > = aa_subpixel_r_offset_static; +#endif static const float aa_cubic_c = aa_cubic_c_static; static const float aa_gauss_sigma = aa_gauss_sigma_static; @@ -427,8 +432,6 @@ uniform float border_compress < - - // Provide accessors for vector constants that pack scalar uniforms: float2 get_aspect_vector(const float geom_aspect_ratio) { diff --git a/reshade-shaders/Shaders/crt-royale/lib/derived-settings-and-constants.fxh b/reshade-shaders/Shaders/crt-royale/lib/derived-settings-and-constants.fxh index ffc50b0..44ed0b2 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/derived-settings-and-constants.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/derived-settings-and-constants.fxh @@ -118,9 +118,7 @@ static const float mask_shadow_avg_color = 41.0/255.0; #define _SIMULATE_GBA_ON_CRT 4 // Ensure the first pass decodes CRT gamma and the last encodes LCD gamma. -#ifndef GAMMA_SIMULATION_MODE - #define GAMMA_SIMULATION_MODE _SIMULATE_CRT_ON_LCD -#endif +#define GAMMA_SIMULATION_MODE _SIMULATE_CRT_ON_LCD // Manually tiling a manually resized texture creates texture coord derivative // discontinuities and confuses anisotropic filtering, causing discolored tile diff --git a/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh b/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh index dfdcf00..f85ec30 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh @@ -425,12 +425,20 @@ float2 get_frame_and_line_field_idx(const float curr_scanline_idx) // Given a scanline index, determine which field it belongs to. // Also determine which field is being drawn this frame. + // When interlacing is disabled, every scanline is in-field b/c + // the modulus is 1 const float modulus = enable_interlacing + 1.0; + // We implement the Static deinterlacing algorithm by lerp'ing + // between two candidate frame parities. The static algorithm + // simply takes the front or back field every frame. The others + // use the adjusted frame count's parity. We lerp on whether + // or not the deinterlacing algorithm is the Static one. const float alternate_between_frames = float(scanline_deinterlacing_mode != 3); const float non_alternating_idx = interlace_bff; const float alternating_idx = fmod(frame_count + interlace_bff, modulus); const float frame_field_idx = lerp(non_alternating_idx, alternating_idx, alternate_between_frames); + const float line_field_idx = fmod(curr_scanline_idx, modulus); return float2(frame_field_idx, line_field_idx); diff --git a/reshade-shaders/Shaders/crt-royale/lib/user-settings.fxh b/reshade-shaders/Shaders/crt-royale/lib/user-settings.fxh index ee54838..e47bd31 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/user-settings.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/user-settings.fxh @@ -152,8 +152,10 @@ #define _PHOSPHOR_BLOOM_TRIADS_LARGER_THAN_9_PIXELS 3 #define _PHOSPHOR_BLOOM_TRIADS_LARGER_THAN_12_PIXELS 4 -#ifndef PHOSPHOR_BLOOM_TRIAD_SIZE_MODE - #define PHOSPHOR_BLOOM_TRIAD_SIZE_MODE _PHOSPHOR_BLOOM_TRIADS_LARGER_THAN_3_PIXELS // [0 - 4] +#if !_RUNTIME_PHOSPHOR_BLOOM_SIGMA + #ifndef PHOSPHOR_BLOOM_TRIAD_SIZE_MODE + #define PHOSPHOR_BLOOM_TRIAD_SIZE_MODE _PHOSPHOR_BLOOM_TRIADS_LARGER_THAN_3_PIXELS // [0 - 4] + #endif #endif // Here's a helpful chart: @@ -176,7 +178,7 @@ static const float lcd_gamma_static = 2.2; // range [1, 5] // LEVELS MANAGEMENT: // Control the final multiplicative image contrast: -static const float levels_contrast_static = 1.0; // range [0, 4) +static const float levels_contrast_static = 2.0; // range [0, 4) // We auto-dim to avoid clipping between passes and restore brightness // later. Control the dim factor here: Lower values clip less but crush // blacks more (static only for now). diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh index 80c8f48..701ff9a 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh @@ -111,7 +111,7 @@ void simulateEletronBeamsPS( const float3 scanline_intensity = (1 - wrong_field) * interlacing_brightness_factor * scanline_color; // Temporarily auto-dim the output to avoid clipping. - color = encode_output(float4(scanline_intensity * beam_intensity, 1.0), get_intermediate_gamma()); + color = encode_output(float4(scanline_intensity * levels_autodim_temp, 1.0), get_intermediate_gamma()); } // Gaussian Shape // Beam will be a distorted Gaussian, dependent on color brightness and hyperparameters @@ -173,7 +173,7 @@ void simulateEletronBeamsPS( ); // Output the corrected color - color = encode_output(float4(beam_strength * beam_intensity, 1), get_intermediate_gamma()); + color = encode_output(float4(beam_strength * levels_autodim_temp, 1), get_intermediate_gamma()); } // Gaussian Shape // Beam will be a distorted Gaussian, dependent on color brightness and hyperparameters @@ -258,7 +258,7 @@ void simulateEletronBeamsPS( ); // Output the corrected color - color = encode_output(float4(beam_strength * beam_intensity, 1), get_intermediate_gamma()); + color = encode_output(float4(beam_strength * levels_autodim_temp, 1), get_intermediate_gamma()); } } From b16ce5477bf2f7548df81d31085da0c3780baddb Mon Sep 17 00:00:00 2001 From: Alex Gunter Date: Sun, 26 Dec 2021 11:21:29 -0600 Subject: [PATCH 10/16] addedd a tweaked beam strength function --- .../crt-royale/lib/scanline-functions.fxh | 38 +++++++++++++++++-- .../shaders/crt-royale-electron-beams.fxh | 8 +++- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh b/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh index f85ec30..c47085b 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh @@ -482,9 +482,12 @@ float3 get_beam_strength(float3 dist, float3 color, return color*exp(-(dist*dist)*inner_denom_inv)*outer_denom_inv; } -float3 get_fancy_beam_strength(float3 dist, float3 color, - const float sigma_range, const float shape_range) -{ +float3 get_fancy_beam_strength( + float dist, + float3 color, + const float sigma_range, + const float shape_range +) { // entry point in original is scanline_contrib() // this is based on scanline_generalized_gaussian_sampled_contrib() from original @@ -498,9 +501,36 @@ float3 get_fancy_beam_strength(float3 dist, float3 color, const float3 beta_inv = 1.0 / beta; const float3 scale = color * beta * 0.5 * alpha_inv / gamma_impl(beta_inv, beta); - + return scale * exp(-pow(abs(dist*alpha_inv), beta)); } +float3 get_fancy_beam_strength( + float dist, + float prev_dist, + float3 color, + const float sigma_range, + const float shape_range +) { + // entry point in original is scanline_contrib() + // this is based on scanline_generalized_gaussian_sampled_contrib() from original + + // See scanline_generalized_gaussian_integral_contrib() for details! + // generalized sample = + // beta/(2*alpha*gamma(1/beta)) * e**(-(|x|/alpha)**beta) + const float3 alpha = sqrt(2.0) * get_gaussian_sigma(color, sigma_range); + const float3 beta = get_generalized_gaussian_beta(color, shape_range); + // Avoid repeated divides: + const float3 alpha_inv = 1.0 / alpha; + const float3 beta_inv = 1.0 / beta; + const float3 scale = color * beta * 0.5 * alpha_inv / + gamma_impl(beta_inv, beta); + + const float3 strength_at_sample = scale * exp(-pow(abs(dist*alpha_inv), beta)); + const float3 strength_at_prev_sample = scale * exp(-pow(abs(prev_dist*alpha_inv), beta)); + + return 0.5 * (strength_at_sample + strength_at_prev_sample); +} + #endif // _SCANLINE_FUNCTIONS_H \ No newline at end of file diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh index 701ff9a..a1ae08e 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh @@ -167,7 +167,13 @@ void simulateEletronBeamsPS( const float beam_dist_v = abs(curr_line_texel_v - source_scanline_center_v); const float beam_dist_y = beam_dist_v / max_beam_dist; - const float3 beam_strength = get_beam_strength( + + // const float prev_dist_offset_v = source_offset_direction * + // float(beam_dist_v > 0) * max(0.5, sign(beam_dist_y)); + // const float prev_dist_offset_y = prev_dist_offset_v / max_beam_dist; + // const float prev_dist_y = beam_dist_y + prev_dist_offset_y; + + const float3 beam_strength = get_fancy_beam_strength( beam_dist_y, scanline_color, sigma_range, shape_range ); From f35437c8b11f3dbd640fa0e3ee99601b582b29ac Mon Sep 17 00:00:00 2001 From: Alex Gunter Date: Fri, 25 Feb 2022 01:40:00 -0600 Subject: [PATCH 11/16] Implemented lanczos for bloom downscaling --- reshade-shaders/Shaders/crt-royale.fx | 28 +- .../crt-royale/lib/phosphor-mask-resizing.fxh | 10 +- .../crt-royale/lib/scanline-functions.fxh | 2 +- .../crt-royale/shaders/blur9fast-vertical.fxh | 2 +- .../shaders/crt-royale-bloom-approx.fxh | 440 ++++++++++-------- .../shaders/crt-royale-brightpass.fxh | 2 +- .../shaders/crt-royale-electron-beams.fxh | 6 +- .../shaders/crt-royale-phosphor-mask.fxh | 12 +- .../crt-royale/shaders/shared-objects.fxh | 39 +- 9 files changed, 336 insertions(+), 205 deletions(-) diff --git a/reshade-shaders/Shaders/crt-royale.fx b/reshade-shaders/Shaders/crt-royale.fx index e68009e..e04fa63 100644 --- a/reshade-shaders/Shaders/crt-royale.fx +++ b/reshade-shaders/Shaders/crt-royale.fx @@ -67,8 +67,8 @@ technique CRT_Royale { // Simulate interlacing by blending in-field rows // and blanking out out-of-field rows. - VertexShader = simulateInterpolationVS; - PixelShader = simulateInterpolationPS; + VertexShader = simulateInterlacingVS; + PixelShader = simulateInterlacingPS; RenderTarget = texInterlaced; } @@ -90,6 +90,7 @@ technique CRT_Royale RenderTarget = texBeamConvergence; } // crt-royale-bloom-approx.fxh + /* pass bloomApproxPass { // The original crt-royale did a bunch of math in this pass @@ -101,6 +102,29 @@ technique CRT_Royale RenderTarget = texBloomApprox; } + */ + pass bloomApproxPassVert + { + // The original crt-royale did a bunch of math in this pass + // and then threw it all away. So this is a no-op for now. + // It still has a blur effect b/c its a smaller buffer. + // TODO: activate the math in this pass and see what happens. + VertexShader = approximateBloomVertVS; + PixelShader = approximateBloomVertPS; + + RenderTarget = texBloomApproxVert; + } + pass bloomApproxPassHoriz + { + // The original crt-royale did a bunch of math in this pass + // and then threw it all away. So this is a no-op for now. + // It still has a blur effect b/c its a smaller buffer. + // TODO: activate the math in this pass and see what happens. + VertexShader = approximateBloomHorizVS; + PixelShader = approximateBloomHorizPS; + + RenderTarget = texBloomApproxHoriz; + } // blur9fast-vertical.fxh pass blurVerticalPass { diff --git a/reshade-shaders/Shaders/crt-royale/lib/phosphor-mask-resizing.fxh b/reshade-shaders/Shaders/crt-royale/lib/phosphor-mask-resizing.fxh index 78409a6..48a3ee8 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/phosphor-mask-resizing.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/phosphor-mask-resizing.fxh @@ -154,7 +154,8 @@ float3 get_downsizing_factor_and_true_tile_size() { float4 lanczos_downsample_horiz( const sampler2D tex, const float2 tex_invsize, - const float2 tex_uv, const float downsizing_factor, const int num_sinc_lobes + const float2 tex_uv, const float downsizing_factor, const int num_sinc_lobes, + const float weight_at_center ) { const int downsizing_factor_int = ceil(downsizing_factor); @@ -174,7 +175,7 @@ float4 lanczos_downsample_horiz( const float sinc_x = i * sinc_dx; const float weight = i != 0 ? num_sinc_lobes * sin(pi*sinc_x) * sin(pi*sinc_x/float(num_sinc_lobes)) / (pi*pi * sinc_x*sinc_x) : - lanczos_weight_at_center; + weight_at_center; w_sum += weight; acc += tex2Dlod(tex, float4(coord, 0, 0)) * weight; @@ -185,7 +186,8 @@ float4 lanczos_downsample_horiz( float4 lanczos_downsample_vert( const sampler2D tex, const float2 tex_invsize, - const float2 tex_uv, const float downsizing_factor, const int num_sinc_lobes + const float2 tex_uv, const float downsizing_factor, const int num_sinc_lobes, + const float weight_at_center ) { const int downsizing_factor_int = ceil(downsizing_factor); @@ -205,7 +207,7 @@ float4 lanczos_downsample_vert( const float sinc_x = i * sinc_dx; const float weight = i != 0 ? num_sinc_lobes * sin(pi*sinc_x) * sin(pi*sinc_x/float(num_sinc_lobes)) / (pi*pi * sinc_x*sinc_x) : - lanczos_weight_at_center; + weight_at_center; w_sum += weight; acc += tex2Dlod(tex, float4(coord, 0, 0)) * weight; diff --git a/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh b/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh index c47085b..0b3bbdc 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh @@ -375,7 +375,7 @@ float3 sample_rgb_scanline_horizontal(const sampler2D tex, } } -float3 get_bobbed_scanline_sample( +float3 get_averaged_scanline_sample( sampler2D tex, const float2 texcoord, const float scanline_start_y, const float v_step_y, const float input_gamma diff --git a/reshade-shaders/Shaders/crt-royale/shaders/blur9fast-vertical.fxh b/reshade-shaders/Shaders/crt-royale/shaders/blur9fast-vertical.fxh index 8c7ebab..1c9c8d7 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/blur9fast-vertical.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/blur9fast-vertical.fxh @@ -80,7 +80,7 @@ void blurVerticalPS( out float4 color : SV_Target ) { - static const float3 blur_color = tex2Dblur9fast(samplerBloomApprox, texcoord, blur_dxdy, get_intermediate_gamma()); + static const float3 blur_color = tex2Dblur9fast(samplerBloomApproxHoriz, texcoord, blur_dxdy, get_intermediate_gamma()); // Encode and output the blurred image: // color = encode_output(float4(blur_color, 1.0), 1.0); color = encode_output(float4(blur_color, 1.0), get_intermediate_gamma()); diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-bloom-approx.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-bloom-approx.fxh index 60658ac..ac58eec 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-bloom-approx.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-bloom-approx.fxh @@ -22,6 +22,7 @@ #include "../lib/bind-shader-params.fxh" #include "../lib/gamma-management.fxh" +#include "../lib/phosphor-mask-resizing.fxh" #include "../lib/scanline-functions.fxh" #include "../lib/blur-functions.fxh" #include "../lib/bloom-functions.fxh" @@ -147,195 +148,264 @@ float3 tex2Dresize_gaussian4x4(sampler2D tex, float2 tex_uv, float2 dxdy, float2 } -void approximateBloomPS( +// void approximateBloomPS( +// in const float4 pos : SV_Position, +// in const float2 texcoord : TEXCOORD0, + +// out float4 color : SV_Target +// ) { +// const float2 orig_linearized_size = tex2Dsize(samplerBeamConvergence); +// const float2 vertical_scanlines_size = tex2Dsize(samplerBeamConvergence); +// const float2 output_size = TEX_BLOOMAPPROX_SIZE; + +// // const float2 video_uv = vTexCoord * texture_size / video_size; +// // const float2 tex_uv = video_uv * ORIG_LINEARIZEDvideo_size / ORIG_LINEARIZEDtexture_size; +// const float2 tex_uv = texcoord; + +// /* +// // The last pass (vertical scanlines) had a viewport y scale, so we can +// // use it to calculate a better runtime sigma: +// const float estimated_viewport_size_x = +// vertical_scanlines_size.y * geom_aspect_ratio_x/geom_aspect_ratio_y; + +// // Get the uv sample distance between output pixels. We're using a resize +// // blur, so arbitrary upsizing will be acceptable if filter_linearN = +// // "true," and arbitrary downsizing will be acceptable if mipmap_inputN = +// // "true" too. The blur will be much more accurate if a true 4x4 Gaussian +// // resize is used instead of tex2Dblur3x3_resize (which samples between +// // texels even for upsizing). +// const float2 dxdy_min_scale = orig_linearized_size / output_size; +// const float2 texture_size_inv = 1.0 / orig_linearized_size; + + +// float2 blur_dxdy; +// if(bloom_approx_filter > 1.5) // 4x4 true Gaussian resize +// { +// // For upsizing, we'll snap to texels and sample the nearest 4. +// const float2 dxdy_scale = max(dxdy_min_scale, float2(1.0, 1.0)); +// blur_dxdy = dxdy_scale * texture_size_inv; +// } +// else +// { +// const float2 dxdy_scale = dxdy_min_scale; +// blur_dxdy = dxdy_scale * texture_size_inv; +// } +// // tex2Dresize_gaussian4x4 needs to know a bit more than the other filters: +// const float2 tex_uv_to_pixel_scale = output_size; // * ORIG_LINEARIZEDtexture_size / ORIG_LINEARIZEDvideo_size; +// //texture_size_inv = texture_size_inv; + +// // Detecting interlacing again here lets us apply convergence offsets in +// // this pass. il_step_multiple contains the (texel, scanline) step +// // multiple: 1 for progressive, 2 for interlaced. +// const float y_step = 1.0 + enable_interlacing; +// const float2 il_step_multiple = float2(1.0, y_step); +// // Get the uv distance between (texels, same-field scanlines): +// const float2 uv_scanline_step = il_step_multiple / orig_linearized_size; + +// // Would a viewport-relative size work better for this pass? (No.) +// // PROS: +// // 1.) Instead of writing an absolute size to user-cgp-constants.h, we'd +// // write a viewport scale. That number could be used to directly scale +// // the viewport-resolution bloom sigma and/or triad size to a smaller +// // scale. This way, we could calculate an optimal dynamic sigma no +// // matter how the dot pitch is specified. +// // CONS: +// // 1.) Texel smearing would be much worse at small viewport sizes, but +// // performance would be much worse at large viewport sizes, so there +// // would be no easy way to calculate a decent scale. +// // 2.) Worse, we could no longer get away with using a constant-size blur! +// // Instead, we'd have to face all the same difficulties as the real +// // phosphor bloom, which requires static #ifdefs to decide the blur +// // size based on the expected triad size...a dynamic value. +// // 3.) Like the phosphor bloom, we'd have less control over making the blur +// // size correct for an optical blur. That said, we likely overblur (to +// // maintain brightness) more than the eye would do by itself: 20/20 +// // human vision distinguishes ~1 arc minute, or 1/60 of a degree. The +// // highest viewing angle recommendation I know of is THX's 40.04 degree +// // recommendation, at which 20/20 vision can distinguish about 2402.4 +// // lines. Assuming the "TV lines" definition, that means 1201.2 +// // distinct light lines and 1201.2 distinct dark lines can be told +// // apart, i.e. 1201.2 pairs of lines. This would correspond to 1201.2 +// // pairs of alternating lit/unlit phosphors, so 2402.4 phosphors total +// // (if they're alternately lit). That's a max of 800.8 triads. Using +// // a more popular 30 degree viewing angle recommendation, 20/20 vision +// // can distinguish 1800 lines, or 600 triads of alternately lit +// // phosphors. In contrast, we currently blur phosphors all the way +// // down to 341.3 triads to ensure full brightness. +// // 4.) Realistically speaking, we're usually just going to use bilinear +// // filtering in this pass anyway, but it only works well to limit +// // bandwidth if it's done at a small constant scale. + +// // Get the constants we need to sample: +// // const sampler2D texture = ORIG_LINEARIZED.texture; +// // const float2 tex_uv = tex_uv; +// // const float2 blur_dxdy = blur_dxdy; +// // const float2 texture_size_ = tex2Dsize(samplerBeamConvergence); +// // const float2 texture_size_inv = texture_size_inv; +// // const float2 tex_uv_to_pixel_scale = tex_uv_to_pixel_scale; +// float2 tex_uv_r, tex_uv_g, tex_uv_b; + +// if(beam_misconvergence) +// { +// const float2 convergence_offsets_r = get_convergence_offsets_r_vector(); +// const float2 convergence_offsets_g = get_convergence_offsets_g_vector(); +// const float2 convergence_offsets_b = get_convergence_offsets_b_vector(); +// tex_uv_r = tex_uv - convergence_offsets_r * uv_scanline_step; +// tex_uv_g = tex_uv - convergence_offsets_g * uv_scanline_step; +// tex_uv_b = tex_uv - convergence_offsets_b * uv_scanline_step; +// } +// // Get the blur sigma: +// const float bloom_approx_sigma = get_bloom_approx_sigma(output_size.x, +// estimated_viewport_size_x); + + +// // Sample the resized and blurred texture, and apply convergence offsets if +// // necessary. Applying convergence offsets here triples our samples from +// // 16/9/1 to 48/27/3, but faster and easier than sampling BLOOM_APPROX and +// // HALATION_BLUR 3 times at full resolution every time they're used. +// float3 out_color_r, out_color_g, out_color_b, out_color; +// if(bloom_approx_filter > 1.5) +// { +// // Use a 4x4 Gaussian resize. This is slower but technically correct. +// if(beam_misconvergence) +// { +// out_color_r = tex2Dresize_gaussian4x4(samplerBeamConvergence, tex_uv_r, +// blur_dxdy, orig_linearized_size, texture_size_inv, +// tex_uv_to_pixel_scale, bloom_approx_sigma, get_intermediate_gamma()); +// out_color_g = tex2Dresize_gaussian4x4(samplerBeamConvergence, tex_uv_g, +// blur_dxdy, orig_linearized_size, texture_size_inv, +// tex_uv_to_pixel_scale, bloom_approx_sigma, get_intermediate_gamma()); +// out_color_b = tex2Dresize_gaussian4x4(samplerBeamConvergence, tex_uv_b, +// blur_dxdy, orig_linearized_size, texture_size_inv, +// tex_uv_to_pixel_scale, bloom_approx_sigma, get_intermediate_gamma()); +// } +// else +// { +// out_color = tex2Dresize_gaussian4x4(samplerBeamConvergence, tex_uv, +// blur_dxdy, orig_linearized_size, texture_size_inv, +// tex_uv_to_pixel_scale, bloom_approx_sigma, get_intermediate_gamma()); +// } +// } +// else if(bloom_approx_filter > 0.5) +// { +// // Use a 3x3 resize blur. This is the softest option, because we're +// // blurring already blurry bilinear samples. It doesn't play quite as +// // nicely with convergence offsets, but it has its charms. +// if(beam_misconvergence) +// { +// out_color_r = tex2Dblur3x3resize(samplerBeamConvergence, tex_uv_r, +// blur_dxdy, bloom_approx_sigma, get_intermediate_gamma()); +// out_color_g = tex2Dblur3x3resize(samplerBeamConvergence, tex_uv_g, +// blur_dxdy, bloom_approx_sigma, get_intermediate_gamma()); +// out_color_b = tex2Dblur3x3resize(samplerBeamConvergence, tex_uv_b, +// blur_dxdy, bloom_approx_sigma, get_intermediate_gamma()); +// } +// else +// { +// out_color = tex2Dblur3x3resize(samplerBeamConvergence, tex_uv, blur_dxdy, get_intermediate_gamma()); +// } +// } +// else +// { +// // Use bilinear sampling. This approximates a 4x4 Gaussian resize MUCH +// // better than tex2Dblur3x3_resize for the very small sigmas we're +// // likely to use at small output resolutions. (This estimate becomes +// // too sharp above ~400x300, but the blurs break down above that +// // resolution too, unless min_allowed_viewport_triads is high enough to +// // keep bloom_approx_scale_x/min_allowed_viewport_triads < ~1.1658025.) +// if(beam_misconvergence) +// { +// out_color_r = tex2D_linearize(samplerBeamConvergence, tex_uv_r, get_intermediate_gamma()).rgb; +// out_color_g = tex2D_linearize(samplerBeamConvergence, tex_uv_g, get_intermediate_gamma()).rgb; +// out_color_b = tex2D_linearize(samplerBeamConvergence, tex_uv_b, get_intermediate_gamma()).rgb; +// } +// else +// { +// out_color = tex2D_linearize(samplerBeamConvergence, tex_uv, get_intermediate_gamma()).rgb; +// } +// } +// // Pack the colors from the red/green/blue beams into a single vector: +// if(beam_misconvergence) +// { +// out_color = float3(out_color_r.r, out_color_g.g, out_color_b.b); +// } +// */ + + +// // Encode and output the blurred image +// // Currently the bloom-approx logic is completely disabled +// const float4 input_color = tex2D_linearize(samplerBeamConvergence, tex_uv, get_intermediate_gamma()); +// color = encode_output(input_color, get_intermediate_gamma()); +// // color = tex2D(samplerBeamConvergence, tex_uv); + +// // color = encode_output(float4(out_color, 1.0), get_intermediate_gamma()); +// } + + + +static const int num_sinc_lobes = mask_sinc_lobes; + + +void approximateBloomVertVS( + in const uint id : SV_VertexID, + + out float4 position : SV_Position, + out float2 texcoord : TEXCOORD0, + + out float2 source_size_inv : TEXCOORD1, + out float downsizing_factor_y : TEXCOORD2 +) { + PostProcessVS(id, position, texcoord); + + source_size_inv = 1.0 / TEX_BEAMCONVERGENCE_SIZE; + downsizing_factor_y = float(TEX_BLOOMAPPROXVERT_HEIGHT) / TEX_BEAMCONVERGENCE_HEIGHT; +} + +void approximateBloomVertPS( in const float4 pos : SV_Position, in const float2 texcoord : TEXCOORD0, + in const float2 source_size_inv : TEXCOORD1, + in const float downsizing_factor_y : TEXCOORD2, + out float4 color : SV_Target ) { - const float2 orig_linearized_size = tex2Dsize(samplerBeamConvergence); - const float2 vertical_scanlines_size = tex2Dsize(samplerBeamConvergence); - const float2 output_size = TEX_BLOOMAPPROX_SIZE; - - // const float2 video_uv = vTexCoord * texture_size / video_size; - // const float2 tex_uv = video_uv * ORIG_LINEARIZEDvideo_size / ORIG_LINEARIZEDtexture_size; - const float2 tex_uv = texcoord; - - /* - // The last pass (vertical scanlines) had a viewport y scale, so we can - // use it to calculate a better runtime sigma: - const float estimated_viewport_size_x = - vertical_scanlines_size.y * geom_aspect_ratio_x/geom_aspect_ratio_y; - - // Get the uv sample distance between output pixels. We're using a resize - // blur, so arbitrary upsizing will be acceptable if filter_linearN = - // "true," and arbitrary downsizing will be acceptable if mipmap_inputN = - // "true" too. The blur will be much more accurate if a true 4x4 Gaussian - // resize is used instead of tex2Dblur3x3_resize (which samples between - // texels even for upsizing). - const float2 dxdy_min_scale = orig_linearized_size / output_size; - const float2 texture_size_inv = 1.0 / orig_linearized_size; - - - float2 blur_dxdy; - if(bloom_approx_filter > 1.5) // 4x4 true Gaussian resize - { - // For upsizing, we'll snap to texels and sample the nearest 4. - const float2 dxdy_scale = max(dxdy_min_scale, float2(1.0, 1.0)); - blur_dxdy = dxdy_scale * texture_size_inv; - } - else - { - const float2 dxdy_scale = dxdy_min_scale; - blur_dxdy = dxdy_scale * texture_size_inv; - } - // tex2Dresize_gaussian4x4 needs to know a bit more than the other filters: - const float2 tex_uv_to_pixel_scale = output_size; // * ORIG_LINEARIZEDtexture_size / ORIG_LINEARIZEDvideo_size; - //texture_size_inv = texture_size_inv; - - // Detecting interlacing again here lets us apply convergence offsets in - // this pass. il_step_multiple contains the (texel, scanline) step - // multiple: 1 for progressive, 2 for interlaced. - const float y_step = 1.0 + enable_interlacing; - const float2 il_step_multiple = float2(1.0, y_step); - // Get the uv distance between (texels, same-field scanlines): - const float2 uv_scanline_step = il_step_multiple / orig_linearized_size; - - // Would a viewport-relative size work better for this pass? (No.) - // PROS: - // 1.) Instead of writing an absolute size to user-cgp-constants.h, we'd - // write a viewport scale. That number could be used to directly scale - // the viewport-resolution bloom sigma and/or triad size to a smaller - // scale. This way, we could calculate an optimal dynamic sigma no - // matter how the dot pitch is specified. - // CONS: - // 1.) Texel smearing would be much worse at small viewport sizes, but - // performance would be much worse at large viewport sizes, so there - // would be no easy way to calculate a decent scale. - // 2.) Worse, we could no longer get away with using a constant-size blur! - // Instead, we'd have to face all the same difficulties as the real - // phosphor bloom, which requires static #ifdefs to decide the blur - // size based on the expected triad size...a dynamic value. - // 3.) Like the phosphor bloom, we'd have less control over making the blur - // size correct for an optical blur. That said, we likely overblur (to - // maintain brightness) more than the eye would do by itself: 20/20 - // human vision distinguishes ~1 arc minute, or 1/60 of a degree. The - // highest viewing angle recommendation I know of is THX's 40.04 degree - // recommendation, at which 20/20 vision can distinguish about 2402.4 - // lines. Assuming the "TV lines" definition, that means 1201.2 - // distinct light lines and 1201.2 distinct dark lines can be told - // apart, i.e. 1201.2 pairs of lines. This would correspond to 1201.2 - // pairs of alternating lit/unlit phosphors, so 2402.4 phosphors total - // (if they're alternately lit). That's a max of 800.8 triads. Using - // a more popular 30 degree viewing angle recommendation, 20/20 vision - // can distinguish 1800 lines, or 600 triads of alternately lit - // phosphors. In contrast, we currently blur phosphors all the way - // down to 341.3 triads to ensure full brightness. - // 4.) Realistically speaking, we're usually just going to use bilinear - // filtering in this pass anyway, but it only works well to limit - // bandwidth if it's done at a small constant scale. + + color = lanczos_downsample_vert( + samplerBeamConvergence, source_size_inv, + texcoord, downsizing_factor_y, num_sinc_lobes, + 1.0 + ); +} + +void approximateBloomHorizVS( + in const uint id : SV_VertexID, + + out float4 position : SV_Position, + out float2 texcoord : TEXCOORD0, + + out float2 source_size_inv : TEXCOORD1, + out float downsizing_factor_x : TEXCOORD2 +) { + PostProcessVS(id, position, texcoord); - // Get the constants we need to sample: -// const sampler2D texture = ORIG_LINEARIZED.texture; -// const float2 tex_uv = tex_uv; -// const float2 blur_dxdy = blur_dxdy; - // const float2 texture_size_ = tex2Dsize(samplerBeamConvergence); -// const float2 texture_size_inv = texture_size_inv; -// const float2 tex_uv_to_pixel_scale = tex_uv_to_pixel_scale; - float2 tex_uv_r, tex_uv_g, tex_uv_b; - - if(beam_misconvergence) - { - const float2 convergence_offsets_r = get_convergence_offsets_r_vector(); - const float2 convergence_offsets_g = get_convergence_offsets_g_vector(); - const float2 convergence_offsets_b = get_convergence_offsets_b_vector(); - tex_uv_r = tex_uv - convergence_offsets_r * uv_scanline_step; - tex_uv_g = tex_uv - convergence_offsets_g * uv_scanline_step; - tex_uv_b = tex_uv - convergence_offsets_b * uv_scanline_step; - } - // Get the blur sigma: - const float bloom_approx_sigma = get_bloom_approx_sigma(output_size.x, - estimated_viewport_size_x); - - - // Sample the resized and blurred texture, and apply convergence offsets if - // necessary. Applying convergence offsets here triples our samples from - // 16/9/1 to 48/27/3, but faster and easier than sampling BLOOM_APPROX and - // HALATION_BLUR 3 times at full resolution every time they're used. - float3 out_color_r, out_color_g, out_color_b, out_color; - if(bloom_approx_filter > 1.5) - { - // Use a 4x4 Gaussian resize. This is slower but technically correct. - if(beam_misconvergence) - { - out_color_r = tex2Dresize_gaussian4x4(samplerBeamConvergence, tex_uv_r, - blur_dxdy, orig_linearized_size, texture_size_inv, - tex_uv_to_pixel_scale, bloom_approx_sigma, get_intermediate_gamma()); - out_color_g = tex2Dresize_gaussian4x4(samplerBeamConvergence, tex_uv_g, - blur_dxdy, orig_linearized_size, texture_size_inv, - tex_uv_to_pixel_scale, bloom_approx_sigma, get_intermediate_gamma()); - out_color_b = tex2Dresize_gaussian4x4(samplerBeamConvergence, tex_uv_b, - blur_dxdy, orig_linearized_size, texture_size_inv, - tex_uv_to_pixel_scale, bloom_approx_sigma, get_intermediate_gamma()); - } - else - { - out_color = tex2Dresize_gaussian4x4(samplerBeamConvergence, tex_uv, - blur_dxdy, orig_linearized_size, texture_size_inv, - tex_uv_to_pixel_scale, bloom_approx_sigma, get_intermediate_gamma()); - } - } - else if(bloom_approx_filter > 0.5) - { - // Use a 3x3 resize blur. This is the softest option, because we're - // blurring already blurry bilinear samples. It doesn't play quite as - // nicely with convergence offsets, but it has its charms. - if(beam_misconvergence) - { - out_color_r = tex2Dblur3x3resize(samplerBeamConvergence, tex_uv_r, - blur_dxdy, bloom_approx_sigma, get_intermediate_gamma()); - out_color_g = tex2Dblur3x3resize(samplerBeamConvergence, tex_uv_g, - blur_dxdy, bloom_approx_sigma, get_intermediate_gamma()); - out_color_b = tex2Dblur3x3resize(samplerBeamConvergence, tex_uv_b, - blur_dxdy, bloom_approx_sigma, get_intermediate_gamma()); - } - else - { - out_color = tex2Dblur3x3resize(samplerBeamConvergence, tex_uv, blur_dxdy, get_intermediate_gamma()); - } - } - else - { - // Use bilinear sampling. This approximates a 4x4 Gaussian resize MUCH - // better than tex2Dblur3x3_resize for the very small sigmas we're - // likely to use at small output resolutions. (This estimate becomes - // too sharp above ~400x300, but the blurs break down above that - // resolution too, unless min_allowed_viewport_triads is high enough to - // keep bloom_approx_scale_x/min_allowed_viewport_triads < ~1.1658025.) - if(beam_misconvergence) - { - out_color_r = tex2D_linearize(samplerBeamConvergence, tex_uv_r, get_intermediate_gamma()).rgb; - out_color_g = tex2D_linearize(samplerBeamConvergence, tex_uv_g, get_intermediate_gamma()).rgb; - out_color_b = tex2D_linearize(samplerBeamConvergence, tex_uv_b, get_intermediate_gamma()).rgb; - } - else - { - out_color = tex2D_linearize(samplerBeamConvergence, tex_uv, get_intermediate_gamma()).rgb; - } - } - // Pack the colors from the red/green/blue beams into a single vector: - if(beam_misconvergence) - { - out_color = float3(out_color_r.r, out_color_g.g, out_color_b.b); - } - */ - - - // Encode and output the blurred image - // Currently the bloom-approx logic is completely disabled - const float4 input_color = tex2D_linearize(samplerBeamConvergence, tex_uv, get_intermediate_gamma()); - color = encode_output(input_color, get_intermediate_gamma()); - // color = tex2D(samplerBeamConvergence, tex_uv); - - // color = encode_output(float4(out_color, 1.0), get_intermediate_gamma()); + source_size_inv = 1.0 / TEX_BLOOMAPPROXVERT_SIZE; + downsizing_factor_x = float(TEX_BLOOMAPPROXHORIZ_WIDTH) / TEX_BEAMCONVERGENCE_WIDTH; +} + +void approximateBloomHorizPS( + in const float4 pos : SV_Position, + in const float2 texcoord : TEXCOORD0, + + in const float2 source_size_inv : TEXCOORD1, + in const float downsizing_factor_x : TEXCOORD2, + + out float4 color : SV_Target +) { + + color = lanczos_downsample_horiz( + samplerBloomApproxVert, source_size_inv, + texcoord, downsizing_factor_x, num_sinc_lobes, + 1.0 + ); } \ No newline at end of file diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-brightpass.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-brightpass.fxh index f8dd5d2..e395385 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-brightpass.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-brightpass.fxh @@ -66,7 +66,7 @@ void brightpassPS( // Sample BLOOM_APPROX to estimate what a straight blur of masked scanlines // would look like, so we can estimate how much energy we'll receive from // blooming neighbors: - const float3 phosphor_blur_approx = levels_contrast * tex2D_linearize(samplerBloomApprox, texcoord, get_intermediate_gamma()).rgb; + const float3 phosphor_blur_approx = levels_contrast * tex2D_linearize(samplerBloomApproxHoriz, texcoord, get_intermediate_gamma()).rgb; // Compute the blur weight for the center texel and the maximum energy we // expect to receive from neighbors: diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh index a1ae08e..f95eca2 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh @@ -26,7 +26,7 @@ #include "shared-objects.fxh" -void simulateInterpolationVS( +void simulateInterlacingVS( in const uint id : SV_VertexID, out float4 position : SV_Position, @@ -40,7 +40,7 @@ void simulateInterpolationVS( v_step = float2(0.0, 1.0 / content_size.y); } -void simulateInterpolationPS( +void simulateInterlacingPS( in const float4 pos : SV_Position, in const float2 texcoord : TEXCOORD0, in const float interlaced : TEXCOORD1, @@ -54,7 +54,7 @@ void simulateInterpolationPS( float curr_scanline_start_y = ( curr_scanline_idx * scanline_num_pixels + TEXCOORD_OFFSET ) / content_size.y; - float3 in_field_interpolated_line = get_bobbed_scanline_sample( + float3 in_field_interpolated_line = get_averaged_scanline_sample( samplerCrop, texcoord, curr_scanline_start_y, v_step.y, get_input_gamma() diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-phosphor-mask.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-phosphor-mask.fxh index 74662b3..1424782 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-phosphor-mask.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-phosphor-mask.fxh @@ -63,19 +63,22 @@ void maskResizeVertPS( else if (mask_type == 0) { color = lanczos_downsample_vert( samplerMaskGrille, source_mask_size_inv, - texcoord, downsizing_factor, num_sinc_lobes + texcoord, downsizing_factor, num_sinc_lobes, + lanczos_weight_at_center ); } else if (mask_type == 2) { color = lanczos_downsample_vert( samplerMaskShadow, source_mask_size_inv, - texcoord, downsizing_factor, num_sinc_lobes + texcoord, downsizing_factor, num_sinc_lobes, + lanczos_weight_at_center ); } else { color = lanczos_downsample_vert( samplerMaskSlot, source_mask_size_inv, - texcoord, downsizing_factor, num_sinc_lobes + texcoord, downsizing_factor, num_sinc_lobes, + lanczos_weight_at_center ); } } @@ -117,7 +120,8 @@ void maskResizeHorizPS( else { color = lanczos_downsample_horiz( samplerMaskResizeVertical, source_mask_size_inv, - texcoord, downsizing_factor, num_sinc_lobes + texcoord, downsizing_factor, num_sinc_lobes, + lanczos_weight_at_center ); } } diff --git a/reshade-shaders/Shaders/crt-royale/shaders/shared-objects.fxh b/reshade-shaders/Shaders/crt-royale/shaders/shared-objects.fxh index 37fd264..54677b8 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/shared-objects.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/shared-objects.fxh @@ -140,6 +140,7 @@ sampler2D samplerInterlaced { Texture = texInterlaced; }; #endif +/* // Pass 4 Buffer (bloomApproxPass) // Cannot be conditioned on __RENDERER__ b/c there are no // available buffers of the same size @@ -154,14 +155,44 @@ texture2D texBloomApprox { Format = RGBA16; }; sampler2D samplerBloomApprox { Texture = texBloomApprox; }; +*/ +// Pass 4a Buffer (bloomApproxVerticalPass) +// Cannot be conditioned on __RENDERER__ b/c there are no +// available buffers of the same size +// Last usage is in brightpassPass +#define TEX_BLOOMAPPROXVERT_WIDTH CONTENT_WIDTH_INTERNAL +#define TEX_BLOOMAPPROXVERT_HEIGHT 240 +#define TEX_BLOOMAPPROXVERT_SIZE int2(TEX_BLOOMAPPROXVERT_WIDTH, TEX_BLOOMAPPROXVERT_HEIGHT) +texture2D texBloomApproxVert { + Width = TEX_BLOOMAPPROXVERT_WIDTH; + Height = TEX_BLOOMAPPROXVERT_HEIGHT; + + Format = RGBA16; +}; +sampler2D samplerBloomApproxVert { Texture = texBloomApproxVert; }; + +// Pass 4b Buffer (bloomApproxHorizontalPass) +// Cannot be conditioned on __RENDERER__ b/c there are no +// available buffers of the same size +// Last usage is in brightpassPass +#define TEX_BLOOMAPPROXHORIZ_WIDTH 320 +#define TEX_BLOOMAPPROXHORIZ_HEIGHT 240 +#define TEX_BLOOMAPPROXHORIZ_SIZE int2(TEX_BLOOMAPPROXHORIZ_WIDTH, TEX_BLOOMAPPROXHORIZ_HEIGHT) +texture2D texBloomApproxHoriz { + Width = TEX_BLOOMAPPROXHORIZ_WIDTH; + Height = TEX_BLOOMAPPROXHORIZ_HEIGHT; + + Format = RGBA16; +}; +sampler2D samplerBloomApproxHoriz { Texture = texBloomApproxHoriz; }; // Pass 5 Buffer (blurVerticalPass) // Cannot be conditioned on __RENDERER__ b/c there are no // available buffers of the same size // Last usage is blurHorizontalPass -#define TEX_BLURVERTICAL_WIDTH TEX_BLOOMAPPROX_WIDTH -#define TEX_BLURVERTICAL_HEIGHT TEX_BLOOMAPPROX_HEIGHT +#define TEX_BLURVERTICAL_WIDTH TEX_BLOOMAPPROXHORIZ_WIDTH +#define TEX_BLURVERTICAL_HEIGHT TEX_BLOOMAPPROXHORIZ_HEIGHT #define TEX_BLURVERTICAL_SIZE int2(TEX_BLURVERTICAL_WIDTH, TEX_BLURVERTICAL_HEIGHT) texture2D texBlurVertical { Width = TEX_BLURVERTICAL_WIDTH; @@ -176,8 +207,8 @@ sampler2D samplerBlurVertical { Texture = texBlurVertical; }; // Cannot be conditioned on __RENDERER__ b/c there are no // available buffers of the same size // Last usage is bloomHorizontalPass -#define TEX_BLURHORIZONTAL_WIDTH TEX_BLOOMAPPROX_WIDTH -#define TEX_BLURHORIZONTAL_HEIGHT TEX_BLOOMAPPROX_HEIGHT +#define TEX_BLURHORIZONTAL_WIDTH TEX_BLOOMAPPROXHORIZ_WIDTH +#define TEX_BLURHORIZONTAL_HEIGHT TEX_BLOOMAPPROXHORIZ_HEIGHT #define TEX_BLURHORIZONTAL_SIZE int2(TEX_BLURHORIZONTAL_WIDTH, TEX_BLURHORIZONTAL_HEIGHT) texture2D texBlurHorizontal { Width = TEX_BLURHORIZONTAL_WIDTH; From ced8b98db8b6ba17ee94c592d9c7e8c377549599 Mon Sep 17 00:00:00 2001 From: Alex Gunter Date: Sat, 26 Feb 2022 01:21:26 -0600 Subject: [PATCH 12/16] Corrected lanczos logic for bloom --- reshade-shaders/Shaders/crt-royale.fx | 4 +- .../crt-royale/lib/bind-shader-params.fxh | 18 + .../shaders/crt-royale-bloom-approx.fxh | 355 +----------------- .../crt-royale/shaders/shared-objects.fxh | 4 + 4 files changed, 40 insertions(+), 341 deletions(-) diff --git a/reshade-shaders/Shaders/crt-royale.fx b/reshade-shaders/Shaders/crt-royale.fx index e04fa63..03981fb 100644 --- a/reshade-shaders/Shaders/crt-royale.fx +++ b/reshade-shaders/Shaders/crt-royale.fx @@ -109,7 +109,7 @@ technique CRT_Royale // and then threw it all away. So this is a no-op for now. // It still has a blur effect b/c its a smaller buffer. // TODO: activate the math in this pass and see what happens. - VertexShader = approximateBloomVertVS; + VertexShader = approximateBloomVS; PixelShader = approximateBloomVertPS; RenderTarget = texBloomApproxVert; @@ -120,7 +120,7 @@ technique CRT_Royale // and then threw it all away. So this is a no-op for now. // It still has a blur effect b/c its a smaller buffer. // TODO: activate the math in this pass and see what happens. - VertexShader = approximateBloomHorizVS; + VertexShader = approximateBloomVS; PixelShader = approximateBloomHorizPS; RenderTarget = texBloomApproxHoriz; diff --git a/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh b/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh index 092ed94..eedf404 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh @@ -141,6 +141,24 @@ uniform float scanline_num_pixels < ui_max = 30.0; ui_step = 1.0; > = 2.0; +/* +uniform float blur_dim_x < + ui_label = "Blur Dim X"; + ui_category = "Interlacing and Scanlines"; + ui_type = "slider"; + ui_min = 320; + ui_max = 2560; + ui_step = 10.0; +> = 2560; +uniform float blur_dim_y < + ui_label = "Blur Dim Y"; + ui_category = "Interlacing and Scanlines"; + ui_type = "slider"; + ui_min = 240; + ui_max = 1440; + ui_step = 10.0; +> = 1440; +*/ uniform float scanline_blend_gamma < ui_label = "Scanline Blend Gamma"; ui_tooltip = "Nudge this if Scanline Blend Strength changes your colors too much"; diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-bloom-approx.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-bloom-approx.fxh index ac58eec..98e9a0d 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-bloom-approx.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-bloom-approx.fxh @@ -30,382 +30,59 @@ #include "shared-objects.fxh" -/////////////////////////////////// HELPERS ////////////////////////////////// -float3 tex2Dresize_gaussian4x4(sampler2D tex, float2 tex_uv, float2 dxdy, float2 tex_size, float2 texture_size_inv, float2 tex_uv_to_pixel_scale, float sigma, float input_gamma) -{ - // Requires: 1.) All requirements of gamma-management.h must be satisfied! - // 2.) filter_linearN must == "true" in your .cgp preset. - // 3.) mipmap_inputN must == "true" in your .cgp preset if - // output_size << SRC.video_size. - // 4.) dxdy should contain the uv pixel spacing: - // dxdy = max(float2(1.0), - // SRC.video_size/output_size)/SRC.texture_size; - // 5.) texture_size == SRC.texture_size - // 6.) texture_size_inv == float2(1.0)/SRC.texture_size - // 7.) tex_uv_to_pixel_scale == output_size * - // SRC.texture_size / SRC.video_size; - // 8.) sigma is the desired Gaussian standard deviation, in - // terms of output pixels. It should be < ~0.66171875 to - // ensure the first unused sample (outside the 4x4 box) has - // a weight < 1.0/256.0. - // Returns: A true 4x4 Gaussian resize of the input. - // Description: - // Given correct inputs, this Gaussian resizer samples 4 pixel locations - // along each downsized dimension and/or 4 texel locations along each - // upsized dimension. It computes dynamic weights based on the pixel-space - // distance of each sample from the destination pixel. It is arbitrarily - // resizable and higher quality than tex2Dblur3x3_resize, but it's slower. - // TODO: Move this to a more suitable file once there are others like it. - const float denom_inv = 0.5/(sigma*sigma); - // We're taking 4x4 samples, and we're snapping to texels for upsizing. - // Find texture coords for sample 5 (second row, second column): - const float2 curr_texel = tex_uv * tex_size; - const float2 prev_texel = - floor(curr_texel - float2(under_half, under_half)) + float2(0.5, 0.5); - const float2 prev_texel_uv = prev_texel * texture_size_inv; - const float2 snap = float2((dxdy.x <= texture_size_inv.x), (dxdy.y <= texture_size_inv.y)); - const float2 sample5_downsize_uv = tex_uv - 0.5 * dxdy; - const float2 sample5_uv = lerp(sample5_downsize_uv, prev_texel_uv, snap); - // Compute texture coords for other samples: - const float2 dx = float2(dxdy.x, 0.0); - const float2 sample0_uv = sample5_uv - dxdy; - const float2 sample10_uv = sample5_uv + dxdy; - const float2 sample15_uv = sample5_uv + 2.0 * dxdy; - const float2 sample1_uv = sample0_uv + dx; - const float2 sample2_uv = sample0_uv + 2.0 * dx; - const float2 sample3_uv = sample0_uv + 3.0 * dx; - const float2 sample4_uv = sample5_uv - dx; - const float2 sample6_uv = sample5_uv + dx; - const float2 sample7_uv = sample5_uv + 2.0 * dx; - const float2 sample8_uv = sample10_uv - 2.0 * dx; - const float2 sample9_uv = sample10_uv - dx; - const float2 sample11_uv = sample10_uv + dx; - const float2 sample12_uv = sample15_uv - 3.0 * dx; - const float2 sample13_uv = sample15_uv - 2.0 * dx; - const float2 sample14_uv = sample15_uv - dx; - // Load each sample: - float3 sample0 = tex2D_linearize(tex, sample0_uv, input_gamma).rgb; - float3 sample1 = tex2D_linearize(tex, sample1_uv, input_gamma).rgb; - float3 sample2 = tex2D_linearize(tex, dx, input_gamma).rgb; - float3 sample3 = tex2D_linearize(tex, sample3_uv, input_gamma).rgb; - float3 sample4 = tex2D_linearize(tex, sample4_uv, input_gamma).rgb; - float3 sample5 = tex2D_linearize(tex, sample5_uv, input_gamma).rgb; - float3 sample6 = tex2D_linearize(tex, sample6_uv, input_gamma).rgb; - float3 sample7 = tex2D_linearize(tex, sample7_uv, input_gamma).rgb; - float3 sample8 = tex2D_linearize(tex, sample8_uv, input_gamma).rgb; - float3 sample9 = tex2D_linearize(tex, sample9_uv, input_gamma).rgb; - float3 sample10 = tex2D_linearize(tex, sample10_uv, input_gamma).rgb; - float3 sample11 = tex2D_linearize(tex, sample11_uv, input_gamma).rgb; - float3 sample12 = tex2D_linearize(tex, sample12_uv, input_gamma).rgb; - float3 sample13 = tex2D_linearize(tex, sample13_uv, input_gamma).rgb; - float3 sample14 = tex2D_linearize(tex, sample14_uv, input_gamma).rgb; - float3 sample15 = tex2D_linearize(tex, sample15_uv, input_gamma).rgb; - // Compute destination pixel offsets for each sample: - const float2 dest_pixel = tex_uv * tex_uv_to_pixel_scale; - const float2 sample0_offset = sample0_uv * tex_uv_to_pixel_scale - dest_pixel; - const float2 sample1_offset = sample1_uv * tex_uv_to_pixel_scale - dest_pixel; - const float2 sample2_offset = sample2_uv * tex_uv_to_pixel_scale - dest_pixel; - const float2 sample3_offset = sample3_uv * tex_uv_to_pixel_scale - dest_pixel; - const float2 sample4_offset = sample4_uv * tex_uv_to_pixel_scale - dest_pixel; - const float2 sample5_offset = sample5_uv * tex_uv_to_pixel_scale - dest_pixel; - const float2 sample6_offset = sample6_uv * tex_uv_to_pixel_scale - dest_pixel; - const float2 sample7_offset = sample7_uv * tex_uv_to_pixel_scale - dest_pixel; - const float2 sample8_offset = sample8_uv * tex_uv_to_pixel_scale - dest_pixel; - const float2 sample9_offset = sample9_uv * tex_uv_to_pixel_scale - dest_pixel; - const float2 sample10_offset = sample10_uv * tex_uv_to_pixel_scale - dest_pixel; - const float2 sample11_offset = sample11_uv * tex_uv_to_pixel_scale - dest_pixel; - const float2 sample12_offset = sample12_uv * tex_uv_to_pixel_scale - dest_pixel; - const float2 sample13_offset = sample13_uv * tex_uv_to_pixel_scale - dest_pixel; - const float2 sample14_offset = sample14_uv * tex_uv_to_pixel_scale - dest_pixel; - const float2 sample15_offset = sample15_uv * tex_uv_to_pixel_scale - dest_pixel; - // Compute Gaussian sample weights: - const float w0 = exp(-LENGTH_SQ(sample0_offset) * denom_inv); - const float w1 = exp(-LENGTH_SQ(sample1_offset) * denom_inv); - const float w2 = exp(-LENGTH_SQ(sample2_offset) * denom_inv); - const float w3 = exp(-LENGTH_SQ(sample3_offset) * denom_inv); - const float w4 = exp(-LENGTH_SQ(sample4_offset) * denom_inv); - const float w5 = exp(-LENGTH_SQ(sample5_offset) * denom_inv); - const float w6 = exp(-LENGTH_SQ(sample6_offset) * denom_inv); - const float w7 = exp(-LENGTH_SQ(sample7_offset) * denom_inv); - const float w8 = exp(-LENGTH_SQ(sample8_offset) * denom_inv); - const float w9 = exp(-LENGTH_SQ(sample9_offset) * denom_inv); - const float w10 = exp(-LENGTH_SQ(sample10_offset) * denom_inv); - const float w11 = exp(-LENGTH_SQ(sample11_offset) * denom_inv); - const float w12 = exp(-LENGTH_SQ(sample12_offset) * denom_inv); - const float w13 = exp(-LENGTH_SQ(sample13_offset) * denom_inv); - const float w14 = exp(-LENGTH_SQ(sample14_offset) * denom_inv); - const float w15 = exp(-LENGTH_SQ(sample15_offset) * denom_inv); - const float weight_sum_inv = 1.0/( - w0 + w1 + w2 + w3 + w4 + w5 + w6 + w7 + - w8 +w9 + w10 + w11 + w12 + w13 + w14 + w15); - // Weight and sum the samples: - const float3 sum = w0 * sample0 + w1 * sample1 + w2 * sample2 + w3 * sample3 + - w4 * sample4 + w5 * sample5 + w6 * sample6 + w7 * sample7 + - w8 * sample8 + w9 * sample9 + w10 * sample10 + w11 * sample11 + - w12 * sample12 + w13 * sample13 + w14 * sample14 + w15 * sample15; - return sum * weight_sum_inv; -} - - -// void approximateBloomPS( -// in const float4 pos : SV_Position, -// in const float2 texcoord : TEXCOORD0, - -// out float4 color : SV_Target -// ) { -// const float2 orig_linearized_size = tex2Dsize(samplerBeamConvergence); -// const float2 vertical_scanlines_size = tex2Dsize(samplerBeamConvergence); -// const float2 output_size = TEX_BLOOMAPPROX_SIZE; - -// // const float2 video_uv = vTexCoord * texture_size / video_size; -// // const float2 tex_uv = video_uv * ORIG_LINEARIZEDvideo_size / ORIG_LINEARIZEDtexture_size; -// const float2 tex_uv = texcoord; - -// /* -// // The last pass (vertical scanlines) had a viewport y scale, so we can -// // use it to calculate a better runtime sigma: -// const float estimated_viewport_size_x = -// vertical_scanlines_size.y * geom_aspect_ratio_x/geom_aspect_ratio_y; - -// // Get the uv sample distance between output pixels. We're using a resize -// // blur, so arbitrary upsizing will be acceptable if filter_linearN = -// // "true," and arbitrary downsizing will be acceptable if mipmap_inputN = -// // "true" too. The blur will be much more accurate if a true 4x4 Gaussian -// // resize is used instead of tex2Dblur3x3_resize (which samples between -// // texels even for upsizing). -// const float2 dxdy_min_scale = orig_linearized_size / output_size; -// const float2 texture_size_inv = 1.0 / orig_linearized_size; - - -// float2 blur_dxdy; -// if(bloom_approx_filter > 1.5) // 4x4 true Gaussian resize -// { -// // For upsizing, we'll snap to texels and sample the nearest 4. -// const float2 dxdy_scale = max(dxdy_min_scale, float2(1.0, 1.0)); -// blur_dxdy = dxdy_scale * texture_size_inv; -// } -// else -// { -// const float2 dxdy_scale = dxdy_min_scale; -// blur_dxdy = dxdy_scale * texture_size_inv; -// } -// // tex2Dresize_gaussian4x4 needs to know a bit more than the other filters: -// const float2 tex_uv_to_pixel_scale = output_size; // * ORIG_LINEARIZEDtexture_size / ORIG_LINEARIZEDvideo_size; -// //texture_size_inv = texture_size_inv; - -// // Detecting interlacing again here lets us apply convergence offsets in -// // this pass. il_step_multiple contains the (texel, scanline) step -// // multiple: 1 for progressive, 2 for interlaced. -// const float y_step = 1.0 + enable_interlacing; -// const float2 il_step_multiple = float2(1.0, y_step); -// // Get the uv distance between (texels, same-field scanlines): -// const float2 uv_scanline_step = il_step_multiple / orig_linearized_size; - -// // Would a viewport-relative size work better for this pass? (No.) -// // PROS: -// // 1.) Instead of writing an absolute size to user-cgp-constants.h, we'd -// // write a viewport scale. That number could be used to directly scale -// // the viewport-resolution bloom sigma and/or triad size to a smaller -// // scale. This way, we could calculate an optimal dynamic sigma no -// // matter how the dot pitch is specified. -// // CONS: -// // 1.) Texel smearing would be much worse at small viewport sizes, but -// // performance would be much worse at large viewport sizes, so there -// // would be no easy way to calculate a decent scale. -// // 2.) Worse, we could no longer get away with using a constant-size blur! -// // Instead, we'd have to face all the same difficulties as the real -// // phosphor bloom, which requires static #ifdefs to decide the blur -// // size based on the expected triad size...a dynamic value. -// // 3.) Like the phosphor bloom, we'd have less control over making the blur -// // size correct for an optical blur. That said, we likely overblur (to -// // maintain brightness) more than the eye would do by itself: 20/20 -// // human vision distinguishes ~1 arc minute, or 1/60 of a degree. The -// // highest viewing angle recommendation I know of is THX's 40.04 degree -// // recommendation, at which 20/20 vision can distinguish about 2402.4 -// // lines. Assuming the "TV lines" definition, that means 1201.2 -// // distinct light lines and 1201.2 distinct dark lines can be told -// // apart, i.e. 1201.2 pairs of lines. This would correspond to 1201.2 -// // pairs of alternating lit/unlit phosphors, so 2402.4 phosphors total -// // (if they're alternately lit). That's a max of 800.8 triads. Using -// // a more popular 30 degree viewing angle recommendation, 20/20 vision -// // can distinguish 1800 lines, or 600 triads of alternately lit -// // phosphors. In contrast, we currently blur phosphors all the way -// // down to 341.3 triads to ensure full brightness. -// // 4.) Realistically speaking, we're usually just going to use bilinear -// // filtering in this pass anyway, but it only works well to limit -// // bandwidth if it's done at a small constant scale. - -// // Get the constants we need to sample: -// // const sampler2D texture = ORIG_LINEARIZED.texture; -// // const float2 tex_uv = tex_uv; -// // const float2 blur_dxdy = blur_dxdy; -// // const float2 texture_size_ = tex2Dsize(samplerBeamConvergence); -// // const float2 texture_size_inv = texture_size_inv; -// // const float2 tex_uv_to_pixel_scale = tex_uv_to_pixel_scale; -// float2 tex_uv_r, tex_uv_g, tex_uv_b; - -// if(beam_misconvergence) -// { -// const float2 convergence_offsets_r = get_convergence_offsets_r_vector(); -// const float2 convergence_offsets_g = get_convergence_offsets_g_vector(); -// const float2 convergence_offsets_b = get_convergence_offsets_b_vector(); -// tex_uv_r = tex_uv - convergence_offsets_r * uv_scanline_step; -// tex_uv_g = tex_uv - convergence_offsets_g * uv_scanline_step; -// tex_uv_b = tex_uv - convergence_offsets_b * uv_scanline_step; -// } -// // Get the blur sigma: -// const float bloom_approx_sigma = get_bloom_approx_sigma(output_size.x, -// estimated_viewport_size_x); - - -// // Sample the resized and blurred texture, and apply convergence offsets if -// // necessary. Applying convergence offsets here triples our samples from -// // 16/9/1 to 48/27/3, but faster and easier than sampling BLOOM_APPROX and -// // HALATION_BLUR 3 times at full resolution every time they're used. -// float3 out_color_r, out_color_g, out_color_b, out_color; -// if(bloom_approx_filter > 1.5) -// { -// // Use a 4x4 Gaussian resize. This is slower but technically correct. -// if(beam_misconvergence) -// { -// out_color_r = tex2Dresize_gaussian4x4(samplerBeamConvergence, tex_uv_r, -// blur_dxdy, orig_linearized_size, texture_size_inv, -// tex_uv_to_pixel_scale, bloom_approx_sigma, get_intermediate_gamma()); -// out_color_g = tex2Dresize_gaussian4x4(samplerBeamConvergence, tex_uv_g, -// blur_dxdy, orig_linearized_size, texture_size_inv, -// tex_uv_to_pixel_scale, bloom_approx_sigma, get_intermediate_gamma()); -// out_color_b = tex2Dresize_gaussian4x4(samplerBeamConvergence, tex_uv_b, -// blur_dxdy, orig_linearized_size, texture_size_inv, -// tex_uv_to_pixel_scale, bloom_approx_sigma, get_intermediate_gamma()); -// } -// else -// { -// out_color = tex2Dresize_gaussian4x4(samplerBeamConvergence, tex_uv, -// blur_dxdy, orig_linearized_size, texture_size_inv, -// tex_uv_to_pixel_scale, bloom_approx_sigma, get_intermediate_gamma()); -// } -// } -// else if(bloom_approx_filter > 0.5) -// { -// // Use a 3x3 resize blur. This is the softest option, because we're -// // blurring already blurry bilinear samples. It doesn't play quite as -// // nicely with convergence offsets, but it has its charms. -// if(beam_misconvergence) -// { -// out_color_r = tex2Dblur3x3resize(samplerBeamConvergence, tex_uv_r, -// blur_dxdy, bloom_approx_sigma, get_intermediate_gamma()); -// out_color_g = tex2Dblur3x3resize(samplerBeamConvergence, tex_uv_g, -// blur_dxdy, bloom_approx_sigma, get_intermediate_gamma()); -// out_color_b = tex2Dblur3x3resize(samplerBeamConvergence, tex_uv_b, -// blur_dxdy, bloom_approx_sigma, get_intermediate_gamma()); -// } -// else -// { -// out_color = tex2Dblur3x3resize(samplerBeamConvergence, tex_uv, blur_dxdy, get_intermediate_gamma()); -// } -// } -// else -// { -// // Use bilinear sampling. This approximates a 4x4 Gaussian resize MUCH -// // better than tex2Dblur3x3_resize for the very small sigmas we're -// // likely to use at small output resolutions. (This estimate becomes -// // too sharp above ~400x300, but the blurs break down above that -// // resolution too, unless min_allowed_viewport_triads is high enough to -// // keep bloom_approx_scale_x/min_allowed_viewport_triads < ~1.1658025.) -// if(beam_misconvergence) -// { -// out_color_r = tex2D_linearize(samplerBeamConvergence, tex_uv_r, get_intermediate_gamma()).rgb; -// out_color_g = tex2D_linearize(samplerBeamConvergence, tex_uv_g, get_intermediate_gamma()).rgb; -// out_color_b = tex2D_linearize(samplerBeamConvergence, tex_uv_b, get_intermediate_gamma()).rgb; -// } -// else -// { -// out_color = tex2D_linearize(samplerBeamConvergence, tex_uv, get_intermediate_gamma()).rgb; -// } -// } -// // Pack the colors from the red/green/blue beams into a single vector: -// if(beam_misconvergence) -// { -// out_color = float3(out_color_r.r, out_color_g.g, out_color_b.b); -// } -// */ - - -// // Encode and output the blurred image -// // Currently the bloom-approx logic is completely disabled -// const float4 input_color = tex2D_linearize(samplerBeamConvergence, tex_uv, get_intermediate_gamma()); -// color = encode_output(input_color, get_intermediate_gamma()); -// // color = tex2D(samplerBeamConvergence, tex_uv); - -// // color = encode_output(float4(out_color, 1.0), get_intermediate_gamma()); -// } +static const int num_sinc_lobes = 3; - -static const int num_sinc_lobes = mask_sinc_lobes; - - -void approximateBloomVertVS( +void approximateBloomVS( in const uint id : SV_VertexID, out float4 position : SV_Position, out float2 texcoord : TEXCOORD0, out float2 source_size_inv : TEXCOORD1, - out float downsizing_factor_y : TEXCOORD2 + out float2 downsizing_factor : TEXCOORD2 ) { PostProcessVS(id, position, texcoord); - - source_size_inv = 1.0 / TEX_BEAMCONVERGENCE_SIZE; - downsizing_factor_y = float(TEX_BLOOMAPPROXVERT_HEIGHT) / TEX_BEAMCONVERGENCE_HEIGHT; + + source_size_inv = 1.0 / float2(TEX_BEAMCONVERGENCE_SIZE); + downsizing_factor = float2(TEX_BEAMCONVERGENCE_SIZE) / TEX_BLOOMAPPROXHORIZ_SIZE; } void approximateBloomVertPS( in const float4 pos : SV_Position, in const float2 texcoord : TEXCOORD0, - in const float2 source_size_inv : TEXCOORD1, - in const float downsizing_factor_y : TEXCOORD2, + in float2 source_size_inv : TEXCOORD1, + in float2 downsizing_factor : TEXCOORD2, out float4 color : SV_Target ) { + const float2 uv = texcoord / float2(1.0, downsizing_factor.y); + color = lanczos_downsample_vert( samplerBeamConvergence, source_size_inv, - texcoord, downsizing_factor_y, num_sinc_lobes, + uv, downsizing_factor.y, num_sinc_lobes, 1.0 ); } -void approximateBloomHorizVS( - in const uint id : SV_VertexID, - - out float4 position : SV_Position, - out float2 texcoord : TEXCOORD0, - - out float2 source_size_inv : TEXCOORD1, - out float downsizing_factor_x : TEXCOORD2 -) { - PostProcessVS(id, position, texcoord); - - source_size_inv = 1.0 / TEX_BLOOMAPPROXVERT_SIZE; - downsizing_factor_x = float(TEX_BLOOMAPPROXHORIZ_WIDTH) / TEX_BEAMCONVERGENCE_WIDTH; -} - void approximateBloomHorizPS( in const float4 pos : SV_Position, in const float2 texcoord : TEXCOORD0, - in const float2 source_size_inv : TEXCOORD1, - in const float downsizing_factor_x : TEXCOORD2, + in float2 source_size_inv : TEXCOORD1, + in float2 downsizing_factor : TEXCOORD2, out float4 color : SV_Target ) { + + const float2 uv = texcoord / float2(downsizing_factor.x, 1.0); color = lanczos_downsample_horiz( samplerBloomApproxVert, source_size_inv, - texcoord, downsizing_factor_x, num_sinc_lobes, + uv, downsizing_factor.x, num_sinc_lobes, 1.0 ); } \ No newline at end of file diff --git a/reshade-shaders/Shaders/crt-royale/shaders/shared-objects.fxh b/reshade-shaders/Shaders/crt-royale/shaders/shared-objects.fxh index 54677b8..8bb4097 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/shared-objects.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/shared-objects.fxh @@ -163,6 +163,8 @@ sampler2D samplerBloomApprox { Texture = texBloomApprox; }; // Last usage is in brightpassPass #define TEX_BLOOMAPPROXVERT_WIDTH CONTENT_WIDTH_INTERNAL #define TEX_BLOOMAPPROXVERT_HEIGHT 240 +// #define TEX_BLOOMAPPROXVERT_WIDTH CONTENT_WIDTH_INTERNAL +// #define TEX_BLOOMAPPROXVERT_HEIGHT CONTENT_HEIGHT_INTERNAL #define TEX_BLOOMAPPROXVERT_SIZE int2(TEX_BLOOMAPPROXVERT_WIDTH, TEX_BLOOMAPPROXVERT_HEIGHT) texture2D texBloomApproxVert { Width = TEX_BLOOMAPPROXVERT_WIDTH; @@ -178,6 +180,8 @@ sampler2D samplerBloomApproxVert { Texture = texBloomApproxVert; }; // Last usage is in brightpassPass #define TEX_BLOOMAPPROXHORIZ_WIDTH 320 #define TEX_BLOOMAPPROXHORIZ_HEIGHT 240 +// #define TEX_BLOOMAPPROXHORIZ_WIDTH CONTENT_WIDTH_INTERNAL +// #define TEX_BLOOMAPPROXHORIZ_HEIGHT CONTENT_HEIGHT_INTERNAL #define TEX_BLOOMAPPROXHORIZ_SIZE int2(TEX_BLOOMAPPROXHORIZ_WIDTH, TEX_BLOOMAPPROXHORIZ_HEIGHT) texture2D texBloomApproxHoriz { Width = TEX_BLOOMAPPROXHORIZ_WIDTH; From 0e796257f83acdf75048fcf25836ec47c84b26f6 Mon Sep 17 00:00:00 2001 From: Alex Gunter Date: Wed, 2 Mar 2022 02:11:31 -0600 Subject: [PATCH 13/16] Corrected beam strength overestimation for even-sized scanlines --- reshade-shaders/Shaders/crt-royale.fx | 13 ------------- .../shaders/crt-royale-electron-beams.fxh | 15 +++++++++++---- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/reshade-shaders/Shaders/crt-royale.fx b/reshade-shaders/Shaders/crt-royale.fx index 03981fb..14af3e3 100644 --- a/reshade-shaders/Shaders/crt-royale.fx +++ b/reshade-shaders/Shaders/crt-royale.fx @@ -90,19 +90,6 @@ technique CRT_Royale RenderTarget = texBeamConvergence; } // crt-royale-bloom-approx.fxh - /* - pass bloomApproxPass - { - // The original crt-royale did a bunch of math in this pass - // and then threw it all away. So this is a no-op for now. - // It still has a blur effect b/c its a smaller buffer. - // TODO: activate the math in this pass and see what happens. - VertexShader = PostProcessVS; - PixelShader = approximateBloomPS; - - RenderTarget = texBloomApprox; - } - */ pass bloomApproxPassVert { // The original crt-royale did a bunch of math in this pass diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh index f95eca2..760bfd2 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh @@ -136,9 +136,9 @@ void simulateEletronBeamsPS( // Lower on screen means larger y-coordinate const float curr_line_is_below_center = float(curr_line_texel_v > upper_center); const float shift_center = num_pixels_is_even * curr_line_is_below_center; - + const float curr_scanline_center_v = upper_center + shift_center; - const float curr_scanline_center_y = (curr_scanline_center_v + TEXCOORD_OFFSET) / orig_linearized_size.y; + // const float curr_scanline_center_y = (curr_scanline_center_v + TEXCOORD_OFFSET) / orig_linearized_size.y; // Find the center of the nearest in-field scanline const float source_offset_direction = lerp(-1, 1, curr_line_is_below_center); @@ -161,12 +161,18 @@ void simulateEletronBeamsPS( // Calculate the beam strength based upon distance from the scanline // and intensity of the sampled color - const float scanlines_wider_than_1 = float(scanline_num_pixels > 1); const float max_beam_dist_factor = 1 + float(enable_interlacing); const float max_beam_dist = max(1, max_beam_dist_factor*half_size - 1); - const float beam_dist_v = abs(curr_line_texel_v - source_scanline_center_v); + + // For even-sized scanlines, offset by 0.5; so we take distance at the center of the texel + const float beam_dist_v_offset = num_pixels_is_even * 0.5; + const float beam_dist_v_raw = curr_line_texel_v - source_scanline_center_v; + const float beam_dist_v_root = round(abs(beam_dist_v_raw) - beam_dist_v_offset); + const float beam_dist_v = sign(beam_dist_v_raw) * (beam_dist_v_root + beam_dist_v_offset); + // const float beam_dist_v = abs(curr_line_texel_v - source_scanline_center_v); const float beam_dist_y = beam_dist_v / max_beam_dist; + // const float prev_dist_offset_v = source_offset_direction * // float(beam_dist_v > 0) * max(0.5, sign(beam_dist_y)); @@ -180,6 +186,7 @@ void simulateEletronBeamsPS( // Output the corrected color color = encode_output(float4(beam_strength * levels_autodim_temp, 1), get_intermediate_gamma()); + // color = encode_output(float4(beam_dist_y, beam_dist_y, beam_dist_y, 1.0), get_lcd_gamma()); } // Gaussian Shape // Beam will be a distorted Gaussian, dependent on color brightness and hyperparameters From 55153e1cbff088e09e408f570b96e20195669f76 Mon Sep 17 00:00:00 2001 From: Alex Gunter Date: Tue, 16 Aug 2022 17:51:53 -0500 Subject: [PATCH 14/16] Added tweaks for debugging --- .../crt-royale/lib/bind-shader-params.fxh | 37 ++++++++++++++++++- .../crt-royale/lib/scanline-functions.fxh | 18 +++++---- .../shaders/crt-royale-deinterlace.fxh | 6 +-- .../shaders/crt-royale-electron-beams.fxh | 26 ++++++------- 4 files changed, 62 insertions(+), 25 deletions(-) diff --git a/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh b/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh index eedf404..e7f8a2c 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh @@ -133,14 +133,38 @@ uniform int scanline_deinterlacing_mode < "Blended Weaving\0" "Static\0"; > = 1; -uniform float scanline_num_pixels < - ui_label = "Scanline Thickness"; +uniform float scanline_num_pixels_left < + ui_label = "Scanline Thickness (LEFT)"; ui_category = "Interlacing and Scanlines"; ui_type = "slider"; ui_min = 1.0; ui_max = 30.0; ui_step = 1.0; > = 2.0; +uniform float scanline_num_pixels_right < + ui_label = "Scanline Thickness (RIGHT)"; + ui_category = "Interlacing and Scanlines"; + ui_type = "slider"; + ui_min = 1.0; + ui_max = 30.0; + ui_step = 1.0; +> = 2.0; +uniform float beam_power_adj_left < + ui_label = "Beam Power Adjustment (LEFT)"; + ui_category = "Interlacing and Scanlines"; + ui_type = "slider"; + ui_min = 0.0; + ui_max = 5.0; + ui_step = 0.001; +> = 1.0; +uniform float beam_power_adj_right < + ui_label = "Beam Power Adjustment (RIGHT)"; + ui_category = "Interlacing and Scanlines"; + ui_type = "slider"; + ui_min = 0.0; + ui_max = 5.0; + ui_step = 0.001; +> = 1.0; /* uniform float blur_dim_x < ui_label = "Blur Dim X"; @@ -449,6 +473,15 @@ uniform float border_compress < > = border_compress_static; +float scanline_num_pixels_fromtexcoord(const float2 texcoord) { + if (texcoord.x > 0.5) return scanline_num_pixels_right; + else return scanline_num_pixels_left; +} + +float beam_power_adj_fromtexcoord(const float2 texcoord) { + if (texcoord.x > 0.5) return beam_power_adj_right; + else return beam_power_adj_left; +} // Provide accessors for vector constants that pack scalar uniforms: float2 get_aspect_vector(const float geom_aspect_ratio) diff --git a/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh b/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh index 0b3bbdc..d8aa635 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh @@ -382,17 +382,18 @@ float3 get_averaged_scanline_sample( ) { // Sample `scanline_num_pixels` vertically-contiguous pixels and average them. float3 interpolated_line = 0.0; - for (int i = 0; i < scanline_num_pixels; i++) { + for (int i = 0; i < scanline_num_pixels_fromtexcoord(texcoord); i++) { float4 coord = float4(texcoord.x, scanline_start_y + i * v_step_y, 0, 0); interpolated_line += tex2Dlod_linearize(tex, coord, input_gamma).rgb; } - interpolated_line /= float(scanline_num_pixels); + interpolated_line /= float(scanline_num_pixels_fromtexcoord(texcoord)); return interpolated_line; } float get_curr_scanline_idx( + const float2 texcoord, const float texcoord_y, const float tex_size_y ) { @@ -401,18 +402,20 @@ float get_curr_scanline_idx( // thickness `scanline_num_pixels` belonging to a single field. const float curr_line_texel_y = floor(texcoord_y * tex_size_y + under_half); - return floor(curr_line_texel_y / scanline_num_pixels + FIX_ZERO(0.0)); + return floor(curr_line_texel_y / scanline_num_pixels_fromtexcoord(texcoord) + FIX_ZERO(0.0)); } float get_scanline_center( + const float2 texcoord, const float line_texel_y, const float scanline_idx ) { - const float scanline_start_y = scanline_idx * scanline_num_pixels; + const float scanline_start_y = scanline_idx * scanline_num_pixels_fromtexcoord(texcoord); - const float half_num_pixels = scanline_num_pixels / 2; + const float half_num_pixels = scanline_num_pixels_fromtexcoord(texcoord) / 2; const float half_size = floor(half_num_pixels + under_half); - const float num_pixels_is_even = float(half_size >= half_num_pixels); + // const float num_pixels_is_even = float(half_size >= half_num_pixels); + const float num_pixels_is_even = float(half_size < half_num_pixels); const float upper_center = scanline_start_y + half_size; const float shift = num_pixels_is_even * float(line_texel_y > upper_center); @@ -458,10 +461,11 @@ float curr_line_is_wrong_field(float curr_scanline_idx) } float curr_line_is_wrong_field( + const float2 texcoord, const float texcoord_y, const float tex_size_y ) { - const float curr_scanline_idx = get_curr_scanline_idx(texcoord_y, tex_size_y); + const float curr_scanline_idx = get_curr_scanline_idx(texcoord, texcoord_y, tex_size_y); return curr_line_is_wrong_field(curr_scanline_idx); } diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-deinterlace.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-deinterlace.fxh index c9cb449..3bf5bb6 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-deinterlace.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-deinterlace.fxh @@ -14,7 +14,7 @@ void deinterlaceVS( ) { PostProcessVS(id, position, texcoord); - v_step = float2(0.0, scanline_num_pixels / TEX_FREEZEFRAME_HEIGHT); + v_step = float2(0.0, scanline_num_pixels_fromtexcoord(texcoord) / TEX_FREEZEFRAME_HEIGHT); } @@ -31,7 +31,7 @@ void deinterlacePS( // If we're in the wrong field, average the current and prev samples // In this case, we're probably averaging a color with 0. if (enable_interlacing && scanline_deinterlacing_mode == 1) { - const float cur_scanline_idx = get_curr_scanline_idx(texcoord.y, CONTENT_HEIGHT_INTERNAL); + const float cur_scanline_idx = get_curr_scanline_idx(texcoord, texcoord.y, CONTENT_HEIGHT_INTERNAL); const float wrong_field = curr_line_is_wrong_field(cur_scanline_idx); const float4 cur_line_color = tex2D(samplerBeamConvergence, texcoord); @@ -51,7 +51,7 @@ void deinterlacePS( // If we're in the wrong field, average the current and prev samples // In this case, we're averaging two fully illuminated colors else if (enable_interlacing && scanline_deinterlacing_mode == 2) { - const float cur_scanline_idx = get_curr_scanline_idx(texcoord.y, CONTENT_HEIGHT_INTERNAL); + const float cur_scanline_idx = get_curr_scanline_idx(texcoord, texcoord.y, CONTENT_HEIGHT_INTERNAL); const float2 frame_and_line_field_idx = get_frame_and_line_field_idx(cur_scanline_idx); const float wrong_field = curr_line_is_wrong_field(frame_and_line_field_idx); const float field_is_odd = fmod(cur_scanline_idx, 2); diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh index 760bfd2..e2e5773 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh @@ -50,9 +50,9 @@ void simulateInterlacingPS( ) { // Sample the current line and an average of the previous/next line; // tex2D_linearize will decode CRT gamma. Don't bother branching: - float curr_scanline_idx = get_curr_scanline_idx(texcoord.y, content_size.y); + float curr_scanline_idx = get_curr_scanline_idx(texcoord, texcoord.y, content_size.y); float curr_scanline_start_y = ( - curr_scanline_idx * scanline_num_pixels + TEXCOORD_OFFSET + curr_scanline_idx * scanline_num_pixels_fromtexcoord(texcoord) + TEXCOORD_OFFSET ) / content_size.y; float3 in_field_interpolated_line = get_averaged_scanline_sample( samplerCrop, texcoord, @@ -88,7 +88,7 @@ void simulateEletronBeamsPS( ) { const float2 orig_linearized_size = tex2Dsize(samplerInterlaced); - const float wrong_field = curr_line_is_wrong_field(texcoord.y, orig_linearized_size.y); + const float wrong_field = curr_line_is_wrong_field(texcoord, texcoord.y, orig_linearized_size.y); // Digital shape // Beam will be perfectly rectangular @@ -125,11 +125,11 @@ void simulateEletronBeamsPS( // Find the texel position of the current scanline const float curr_line_texel_v = floor(texcoord.y * orig_linearized_size.y + under_half); - const float curr_scanline_idx = get_curr_scanline_idx(texcoord.y, orig_linearized_size.y); - const float curr_scanline_start_v = curr_scanline_idx * scanline_num_pixels; + const float curr_scanline_idx = get_curr_scanline_idx(texcoord, texcoord.y, orig_linearized_size.y); + const float curr_scanline_start_v = curr_scanline_idx * scanline_num_pixels_fromtexcoord(texcoord); // Find the center of the current scanline - const float half_num_pixels = scanline_num_pixels / 2; + const float half_num_pixels = scanline_num_pixels_fromtexcoord(texcoord) / 2; const float half_size = floor(half_num_pixels + under_half); const float num_pixels_is_even = float(half_size >= half_num_pixels); const float upper_center = curr_scanline_start_v + half_size; @@ -142,7 +142,7 @@ void simulateEletronBeamsPS( // Find the center of the nearest in-field scanline const float source_offset_direction = lerp(-1, 1, curr_line_is_below_center); - const float source_offset = source_offset_direction * wrong_field * scanline_num_pixels; + const float source_offset = source_offset_direction * wrong_field * scanline_num_pixels_fromtexcoord(texcoord); const float source_scanline_center_v = curr_scanline_center_v + source_offset; const float source_scanline_center_y = (source_scanline_center_v + TEXCOORD_OFFSET) / orig_linearized_size.y; @@ -179,7 +179,7 @@ void simulateEletronBeamsPS( // const float prev_dist_offset_y = prev_dist_offset_v / max_beam_dist; // const float prev_dist_y = beam_dist_y + prev_dist_offset_y; - const float3 beam_strength = get_fancy_beam_strength( + const float3 beam_strength = beam_power_adj_fromtexcoord(texcoord) * get_fancy_beam_strength( beam_dist_y, scanline_color, sigma_range, shape_range ); @@ -200,18 +200,18 @@ void simulateEletronBeamsPS( // Find the texel position of the current scanline const float curr_line_texel_v = floor(texcoord.y * orig_linearized_size.y + under_half); - const float curr_scanline_idx = get_curr_scanline_idx(texcoord.y, orig_linearized_size.y); - const float curr_scanline_start_v = curr_scanline_idx * scanline_num_pixels; + const float curr_scanline_idx = get_curr_scanline_idx(texcoord, texcoord.y, orig_linearized_size.y); + const float curr_scanline_start_v = curr_scanline_idx * scanline_num_pixels_fromtexcoord(texcoord); // Find the center of the current scanline - const float half_num_pixels = scanline_num_pixels / 2; + const float half_num_pixels = scanline_num_pixels_fromtexcoord(texcoord) / 2; const float half_size = floor(half_num_pixels + under_half); const float num_pixels_is_even = float(half_size >= half_num_pixels); const float upper_center = curr_scanline_start_v + half_size; // Lower on screen means larger y-coordinate const float curr_line_is_below_center = float(curr_line_texel_v > upper_center); const float shift_center = num_pixels_is_even * curr_line_is_below_center; - const float bounding_scanline_offset_v = (2 - wrong_field) * scanline_num_pixels; + const float bounding_scanline_offset_v = (2 - wrong_field) * scanline_num_pixels_fromtexcoord(texcoord); const float3 scanline_offsets_v = float3( -bounding_scanline_offset_v, @@ -245,7 +245,7 @@ void simulateEletronBeamsPS( // Calculate the beam strength based upon distance from the scanline // and intensity of the sampled color - const float scanlines_wider_than_1 = float(scanline_num_pixels > 1); + const float scanlines_wider_than_1 = float(scanline_num_pixels_fromtexcoord(texcoord) > 1); const float max_beam_dist_factor = 1 + float(enable_interlacing); const float max_beam_dist = max(1, max_beam_dist_factor*half_size - 1); From 298e0b0eaee7052916df625afaad62d019a9140e Mon Sep 17 00:00:00 2001 From: Alex Gunter Date: Sun, 21 Aug 2022 11:04:59 -0500 Subject: [PATCH 15/16] Lots of debugging changes, and added basis for linear scanline distance function --- .../crt-royale/lib/bind-shader-params.fxh | 23 ++- .../crt-royale/lib/scanline-functions.fxh | 27 +++- .../shaders/crt-royale-deinterlace.fxh | 2 +- .../shaders/crt-royale-electron-beams.fxh | 141 ++++++++++++++++-- 4 files changed, 171 insertions(+), 22 deletions(-) diff --git a/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh b/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh index e7f8a2c..3bd0043 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh @@ -149,6 +149,22 @@ uniform float scanline_num_pixels_right < ui_max = 30.0; ui_step = 1.0; > = 2.0; +uniform float scanline_pixel_distance < + ui_label = "Scanline Pixel Distance"; + ui_category = "Interlacing and Scanlines"; + ui_type = "slider"; + ui_min = 0.0; + ui_max = 30.0; + ui_step = 0.5; +> = 0.0; +uniform float scanline_max_embedding_dist < + ui_label = "Scanline Max Embed Dist"; + ui_category = "Interlacing and Scanlines"; + ui_type = "slider"; + ui_min = 0.1; + ui_max = 5.0; + ui_step = 0.1; +> = 1.0; uniform float beam_power_adj_left < ui_label = "Beam Power Adjustment (LEFT)"; ui_category = "Interlacing and Scanlines"; @@ -231,6 +247,8 @@ uniform int beam_shape_mode < ui_category = "Electron Beam"; ui_type = "combo"; ui_items = "Digital\0" + "Linear\0" + "Multi-Source Linear\0" "Gaussian\0" "Multi-Source Gaussian\0"; > = 1; @@ -474,8 +492,9 @@ uniform float border_compress < float scanline_num_pixels_fromtexcoord(const float2 texcoord) { - if (texcoord.x > 0.5) return scanline_num_pixels_right; - else return scanline_num_pixels_left; + const float coord_is_left = float(texcoord.x <= 0.5); + return lerp(scanline_num_pixels_right, scanline_num_pixels_left, coord_is_left); + // return coord_is_left * scanline_num_pixels_left + (1 - coord_is_left) * scanline_num_pixels_right; } float beam_power_adj_fromtexcoord(const float2 texcoord) { diff --git a/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh b/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh index d8aa635..b1dab51 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh @@ -414,8 +414,8 @@ float get_scanline_center( const float half_num_pixels = scanline_num_pixels_fromtexcoord(texcoord) / 2; const float half_size = floor(half_num_pixels + under_half); - // const float num_pixels_is_even = float(half_size >= half_num_pixels); - const float num_pixels_is_even = float(half_size < half_num_pixels); + const float num_pixels_is_even = float(half_size >= half_num_pixels); + // const float num_pixels_is_even = float(half_size < half_num_pixels); const float upper_center = scanline_start_y + half_size; const float shift = num_pixels_is_even * float(line_texel_y > upper_center); @@ -486,7 +486,7 @@ float3 get_beam_strength(float3 dist, float3 color, return color*exp(-(dist*dist)*inner_denom_inv)*outer_denom_inv; } -float3 get_fancy_beam_strength( +float3 get_gaussian_beam_strength( float dist, float3 color, const float sigma_range, @@ -509,7 +509,7 @@ float3 get_fancy_beam_strength( return scale * exp(-pow(abs(dist*alpha_inv), beta)); } -float3 get_fancy_beam_strength( +float3 get_gaussian_beam_strength( float dist, float prev_dist, float3 color, @@ -536,5 +536,24 @@ float3 get_fancy_beam_strength( return 0.5 * (strength_at_sample + strength_at_prev_sample); } +float3 get_linear_beam_strength( + float dist, + float3 color, + const float sigma_range, + const float shape_range, + const float num_pixels_is_even, + const float2 texcoord +) { + const float num_pixels = scanline_num_pixels_fromtexcoord(texcoord); + const float3 p = color * (1 - abs(dist)); + const float3 p_clamp = clamp(p, 0, color); + // const float3 odd_err = 2/num_pixels + pow((num_pixels - 1) / num_pixels, 2); + // const float3 err_factor = num_pixels_is_even * odd_err; + + // return p_clamp / err_factor; + if (num_pixels == 1) return p_clamp / 2; + else return p_clamp; +} + #endif // _SCANLINE_FUNCTIONS_H \ No newline at end of file diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-deinterlace.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-deinterlace.fxh index 3bf5bb6..8de99c5 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-deinterlace.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-deinterlace.fxh @@ -29,7 +29,7 @@ void deinterlacePS( // Sample texcoord from this frame and the previous frame // If we're in the correct field, use the current sample // If we're in the wrong field, average the current and prev samples - // In this case, we're probably averaging a color with 0. + // In this case, we're probably averaging a color with 0 and producing a brightness of 0.5. if (enable_interlacing && scanline_deinterlacing_mode == 1) { const float cur_scanline_idx = get_curr_scanline_idx(texcoord, texcoord.y, CONTENT_HEIGHT_INTERNAL); const float wrong_field = curr_line_is_wrong_field(cur_scanline_idx); diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh index e2e5773..1f4c9ee 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh @@ -71,11 +71,67 @@ void simulateEletronBeamsVS( out float4 position : SV_Position, out float2 texcoord : TEXCOORD0, - out float2 v_step : TEXCOORD1 + out float2 v_step : TEXCOORD1, + out float2 gauss_adj_factor : TEXCOORD2 ) { PostProcessVS(id, position, texcoord); v_step = float2(0.0, 1.0 / tex2Dsize(samplerInterlaced).y); + + const float sigma_range = max(beam_max_sigma, beam_min_sigma) - beam_min_sigma; + const float shape_range = max(beam_max_shape, beam_min_shape) - beam_min_shape; + const float3 beam_strength_0 = get_gaussian_beam_strength( + 0.0, float3(1, 1, 1), + sigma_range, shape_range + ).r; + + const float max_embedding_dist = scanline_max_embedding_dist; + const float beam_dist_factor = 1 + float(enable_interlacing); + const float target_num_pixels = 50; + + const float delta_target = 2 * max_embedding_dist / (beam_dist_factor * target_num_pixels); + const float delta_left = 2 * max_embedding_dist / (beam_dist_factor * scanline_num_pixels_left); + const float delta_right = 2 * max_embedding_dist / (beam_dist_factor * scanline_num_pixels_right); + + const float min_dist_target = -(max_embedding_dist - delta_target/2.0); + const float min_dist_left = -(max_embedding_dist - delta_left/2.0); + const float min_dist_right = -(max_embedding_dist - delta_right/2.0); + + float target_gauss_beam_integral = 0.0; + for (int i = 0; i < target_num_pixels; i++) { + const float d = min_dist_target + delta_target*i; + target_gauss_beam_integral += get_gaussian_beam_strength( + d, float3(1, 1, 1), + sigma_range, shape_range + ).r; + } + target_gauss_beam_integral *= delta_target; + + float approx_gauss_beam_integral_left = 0.0; + for (int i = 0; i < scanline_num_pixels_left; i++) { + const float d = min_dist_left + delta_left*i; + approx_gauss_beam_integral_left += get_gaussian_beam_strength( + d, float3(1, 1, 1), + sigma_range, shape_range + ).r; + } + approx_gauss_beam_integral_left *= delta_left; + + float approx_gauss_beam_integral_right = 0.0; + for (int i = 0; i < scanline_num_pixels_right; i++) { + const float d = min_dist_right + delta_right*i; + approx_gauss_beam_integral_right += get_gaussian_beam_strength( + d, float3(1, 1, 1), + sigma_range, shape_range + ).r; + } + approx_gauss_beam_integral_right *= delta_right; + + gauss_adj_factor = target_gauss_beam_integral / float2( + approx_gauss_beam_integral_left, + approx_gauss_beam_integral_right + ); + // gauss_adj_factor = float2(1.0, 1.0); } @@ -83,6 +139,7 @@ void simulateEletronBeamsPS( in const float4 position : SV_Position, in const float2 texcoord : TEXCOORD0, in const float2 v_step : TEXCOORD1, + in const float2 gauss_adj_factor_lr : TEXCOORD2, out float4 color : SV_Target ) { @@ -90,6 +147,9 @@ void simulateEletronBeamsPS( const float wrong_field = curr_line_is_wrong_field(texcoord, texcoord.y, orig_linearized_size.y); + const float coord_is_left = float(texcoord.x <= 0.5); + const float gauss_adj_factor = coord_is_left * gauss_adj_factor_lr.x + (1 - coord_is_left) * gauss_adj_factor_lr.y; + // Digital shape // Beam will be perfectly rectangular if (beam_shape_mode == 0) { @@ -113,10 +173,13 @@ void simulateEletronBeamsPS( // Temporarily auto-dim the output to avoid clipping. color = encode_output(float4(scanline_intensity * levels_autodim_temp, 1.0), get_intermediate_gamma()); } + // else if (beam_shape_mode == 1) { + + // } // Gaussian Shape // Beam will be a distorted Gaussian, dependent on color brightness and hyperparameters // Will only consider contribution from nearest scanline - else if (beam_shape_mode == 1) { + else if (beam_shape_mode == 3) { // Calculate {sigma, shape}_range outside of scanline_contrib so it's only // done once per pixel (not 6 times) with runtime params. Don't reuse the // vertex shader calculations, so static versions can be constant-folded. @@ -128,6 +191,14 @@ void simulateEletronBeamsPS( const float curr_scanline_idx = get_curr_scanline_idx(texcoord, texcoord.y, orig_linearized_size.y); const float curr_scanline_start_v = curr_scanline_idx * scanline_num_pixels_fromtexcoord(texcoord); + // Find the center of the current scanline + // For odd sizes, this is a texel. For even, this is between two texels + const float half_num_pixels = scanline_num_pixels_fromtexcoord(texcoord) / 2; + const float half_size = floor(half_num_pixels + under_half); + const float num_pixels_is_even = float(half_size >= half_num_pixels); + const float curr_scanline_center_v = curr_scanline_start_v + half_num_pixels - 0.5; + + /* // Find the center of the current scanline const float half_num_pixels = scanline_num_pixels_fromtexcoord(texcoord) / 2; const float half_size = floor(half_num_pixels + under_half); @@ -139,21 +210,28 @@ void simulateEletronBeamsPS( const float curr_scanline_center_v = upper_center + shift_center; // const float curr_scanline_center_y = (curr_scanline_center_v + TEXCOORD_OFFSET) / orig_linearized_size.y; + */ // Find the center of the nearest in-field scanline + const float curr_line_is_below_center = float(curr_line_texel_v > curr_scanline_center_v); const float source_offset_direction = lerp(-1, 1, curr_line_is_below_center); const float source_offset = source_offset_direction * wrong_field * scanline_num_pixels_fromtexcoord(texcoord); const float source_scanline_center_v = curr_scanline_center_v + source_offset; - const float source_scanline_center_y = (source_scanline_center_v + TEXCOORD_OFFSET) / orig_linearized_size.y; - const float2 source_scanline_center_xy = float2(texcoord.x, source_scanline_center_y); + const float source_scanline_start_v = curr_scanline_start_v + source_offset; + // const float source_scanline_center_y = (source_scanline_center_v + TEXCOORD_OFFSET) / orig_linearized_size.y; + // const float2 source_scanline_center_xy = float2(texcoord.x, source_scanline_center_y); + const float source_scanline_start_y = (source_scanline_start_v + TEXCOORD_OFFSET) / orig_linearized_size.y; + const float2 source_scanline_start_xy = float2(texcoord.x, source_scanline_start_y); // Sample the nearest in-field scanline const float3 scanline_color = sample_single_scanline_horizontal( samplerInterlaced, - source_scanline_center_xy, orig_linearized_size, + source_scanline_start_xy, orig_linearized_size, 1 / orig_linearized_size ); + // const float3 scanline_color = float3(1, 1, 1); + // const float4 scanline_color = decode_input( // tex2D(samplerInterlaced, source_scanline_center_xy), // get_intermediate_gamma() @@ -161,32 +239,65 @@ void simulateEletronBeamsPS( // Calculate the beam strength based upon distance from the scanline // and intensity of the sampled color - const float max_beam_dist_factor = 1 + float(enable_interlacing); - const float max_beam_dist = max(1, max_beam_dist_factor*half_size - 1); + // const float max_beam_dist = max(1, max_beam_dist_factor*half_size - 1); + // const float max_beam_dist = max(1, beam_dist_factor*half_num_pixels); + // const float min_beam_dist = num_pixels_is_even * 0.5; + const float beam_dist_factor = 1 + float(enable_interlacing); + const float pixel_delta = 2 * scanline_max_embedding_dist / (beam_dist_factor * scanline_num_pixels_fromtexcoord(texcoord)); + const float max_beam_dist = scanline_max_embedding_dist - pixel_delta/2.0; + const float beam_dist_denom = half_num_pixels / scanline_max_embedding_dist; + /* // For even-sized scanlines, offset by 0.5; so we take distance at the center of the texel + // TODO: Applying to odd-sized scanlines instead helps with color mismatch. Not sure why. const float beam_dist_v_offset = num_pixels_is_even * 0.5; + // const float beam_dist_v_offset = (1-num_pixels_is_even) * 0.5; + // const float beam_dist_v_offset = 0.0; const float beam_dist_v_raw = curr_line_texel_v - source_scanline_center_v; const float beam_dist_v_root = round(abs(beam_dist_v_raw) - beam_dist_v_offset); const float beam_dist_v = sign(beam_dist_v_raw) * (beam_dist_v_root + beam_dist_v_offset); // const float beam_dist_v = abs(curr_line_texel_v - source_scanline_center_v); - const float beam_dist_y = beam_dist_v / max_beam_dist; - + const float beam_dist_y = beam_power_adj_fromtexcoord(texcoord) * beam_dist_v / max_beam_dist; + */ + const float beam_dist_v = curr_line_texel_v - source_scanline_center_v; + const float beam_dist_y = scanline_max_embedding_dist * beam_dist_v / beam_dist_denom; + // const float eq_dist = float(abs(abs(beam_dist_y) - max_beam_dist) <= 1e-5); // const float prev_dist_offset_v = source_offset_direction * // float(beam_dist_v > 0) * max(0.5, sign(beam_dist_y)); // const float prev_dist_offset_y = prev_dist_offset_v / max_beam_dist; // const float prev_dist_y = beam_dist_y + prev_dist_offset_y; - const float3 beam_strength = beam_power_adj_fromtexcoord(texcoord) * get_fancy_beam_strength( + /* + const float3 beam_strength = get_lowres_gaussian_beam_strength( beam_dist_y, scanline_color, sigma_range, shape_range + ) * (1 - wrong_field); + */ + + const float3 beam_strength = get_linear_beam_strength( + beam_dist_y, scanline_color, + sigma_range, shape_range, + num_pixels_is_even, texcoord + ); + // const float3 beam_strength = scanline_color; + + const float interlacing_brightness_factor = 1 + enable_interlacing * float( + scanline_deinterlacing_mode != 1 && + scanline_deinterlacing_mode != 2 ); // Output the corrected color - color = encode_output(float4(beam_strength * levels_autodim_temp, 1), get_intermediate_gamma()); - // color = encode_output(float4(beam_dist_y, beam_dist_y, beam_dist_y, 1.0), get_lcd_gamma()); + // if (eq_dist) { + // color = encode_output(float4(1, 0, 0, 1), get_intermediate_gamma()); + // } + // else { + color = encode_output(float4(beam_strength * interlacing_brightness_factor, 1), get_intermediate_gamma()); + // } + + // color = encode_output(float4(scanline_color, 1), get_intermediate_gamma()); + // color = encode_output(float4(beam_dist_y, beam_dist_y, beam_dist_y, 1.0), get_intermediate_gamma()); } // Gaussian Shape // Beam will be a distorted Gaussian, dependent on color brightness and hyperparameters @@ -252,15 +363,15 @@ void simulateEletronBeamsPS( const float3 beam_dists_v = abs(curr_line_texel_v - scanline_centers_v); const float3 beam_dists_y = beam_dists_v / max_beam_dist; - const float3 upper_beam_strength = get_fancy_beam_strength( + const float3 upper_beam_strength = get_gaussian_beam_strength( beam_dists_y.x, upper_scanline_color, sigma_range, shape_range ); - const float3 curr_beam_strength = get_fancy_beam_strength( + const float3 curr_beam_strength = get_gaussian_beam_strength( beam_dists_y.y, curr_scanline_color, sigma_range, shape_range ); - const float3 lower_beam_strength = get_fancy_beam_strength( + const float3 lower_beam_strength = get_gaussian_beam_strength( beam_dists_y.z, lower_scanline_color, sigma_range, shape_range ); From 7236fcb45989d2fbf7672c74a099ba103b04c3b4 Mon Sep 17 00:00:00 2001 From: Alex Gunter Date: Sun, 21 Aug 2022 16:28:48 -0500 Subject: [PATCH 16/16] Normalized scanline intensities and removed split-screen effect --- .../crt-royale/lib/bind-shader-params.fxh | 78 +---- .../crt-royale/lib/scanline-functions.fxh | 35 +-- .../Shaders/crt-royale/lib/user-settings.fxh | 2 +- .../shaders/crt-royale-deinterlace.fxh | 2 +- .../shaders/crt-royale-electron-beams.fxh | 282 +++++++----------- 5 files changed, 131 insertions(+), 268 deletions(-) diff --git a/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh b/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh index 3bd0043..99c7e6d 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/bind-shader-params.fxh @@ -133,30 +133,14 @@ uniform int scanline_deinterlacing_mode < "Blended Weaving\0" "Static\0"; > = 1; -uniform float scanline_num_pixels_left < - ui_label = "Scanline Thickness (LEFT)"; +uniform float scanline_num_pixels < + ui_label = "Scanline Thickness"; ui_category = "Interlacing and Scanlines"; ui_type = "slider"; ui_min = 1.0; ui_max = 30.0; ui_step = 1.0; > = 2.0; -uniform float scanline_num_pixels_right < - ui_label = "Scanline Thickness (RIGHT)"; - ui_category = "Interlacing and Scanlines"; - ui_type = "slider"; - ui_min = 1.0; - ui_max = 30.0; - ui_step = 1.0; -> = 2.0; -uniform float scanline_pixel_distance < - ui_label = "Scanline Pixel Distance"; - ui_category = "Interlacing and Scanlines"; - ui_type = "slider"; - ui_min = 0.0; - ui_max = 30.0; - ui_step = 0.5; -> = 0.0; uniform float scanline_max_embedding_dist < ui_label = "Scanline Max Embed Dist"; ui_category = "Interlacing and Scanlines"; @@ -165,40 +149,6 @@ uniform float scanline_max_embedding_dist < ui_max = 5.0; ui_step = 0.1; > = 1.0; -uniform float beam_power_adj_left < - ui_label = "Beam Power Adjustment (LEFT)"; - ui_category = "Interlacing and Scanlines"; - ui_type = "slider"; - ui_min = 0.0; - ui_max = 5.0; - ui_step = 0.001; -> = 1.0; -uniform float beam_power_adj_right < - ui_label = "Beam Power Adjustment (RIGHT)"; - ui_category = "Interlacing and Scanlines"; - ui_type = "slider"; - ui_min = 0.0; - ui_max = 5.0; - ui_step = 0.001; -> = 1.0; -/* -uniform float blur_dim_x < - ui_label = "Blur Dim X"; - ui_category = "Interlacing and Scanlines"; - ui_type = "slider"; - ui_min = 320; - ui_max = 2560; - ui_step = 10.0; -> = 2560; -uniform float blur_dim_y < - ui_label = "Blur Dim Y"; - ui_category = "Interlacing and Scanlines"; - ui_type = "slider"; - ui_min = 240; - ui_max = 1440; - ui_step = 10.0; -> = 1440; -*/ uniform float scanline_blend_gamma < ui_label = "Scanline Blend Gamma"; ui_tooltip = "Nudge this if Scanline Blend Strength changes your colors too much"; @@ -248,21 +198,9 @@ uniform int beam_shape_mode < ui_type = "combo"; ui_items = "Digital\0" "Linear\0" - "Multi-Source Linear\0" "Gaussian\0" "Multi-Source Gaussian\0"; > = 1; -/* -uniform float beam_intensity < - ui_label = "Beam Intensity"; - ui_tooltip = "0.5 recommended for Digital Beam Shape and 0.7 for Gaussian. Adjust from there."; - ui_category = "Electron Beam"; - ui_type = "slider"; - ui_min = 0.01; - ui_max = 2.0; - ui_step = 0.01; -> = 0.7; -*/ uniform float beam_min_sigma < ui_label = "Beam Min Sigma"; ui_category = "Electron Beam"; @@ -358,7 +296,6 @@ uniform float lcd_gamma < > = lcd_gamma_static; uniform float levels_contrast < ui_label = "Levels Contrast"; - ui_tooltip = "2.0 recommended for Gaussian Beam Shapes. 1.0 for Digital."; ui_category = "Colors and Effects"; ui_type = "slider"; ui_min = 0.0; @@ -491,17 +428,6 @@ uniform float border_compress < > = border_compress_static; -float scanline_num_pixels_fromtexcoord(const float2 texcoord) { - const float coord_is_left = float(texcoord.x <= 0.5); - return lerp(scanline_num_pixels_right, scanline_num_pixels_left, coord_is_left); - // return coord_is_left * scanline_num_pixels_left + (1 - coord_is_left) * scanline_num_pixels_right; -} - -float beam_power_adj_fromtexcoord(const float2 texcoord) { - if (texcoord.x > 0.5) return beam_power_adj_right; - else return beam_power_adj_left; -} - // Provide accessors for vector constants that pack scalar uniforms: float2 get_aspect_vector(const float geom_aspect_ratio) { diff --git a/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh b/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh index b1dab51..64ce08f 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/scanline-functions.fxh @@ -382,11 +382,11 @@ float3 get_averaged_scanline_sample( ) { // Sample `scanline_num_pixels` vertically-contiguous pixels and average them. float3 interpolated_line = 0.0; - for (int i = 0; i < scanline_num_pixels_fromtexcoord(texcoord); i++) { + for (int i = 0; i < scanline_num_pixels; i++) { float4 coord = float4(texcoord.x, scanline_start_y + i * v_step_y, 0, 0); interpolated_line += tex2Dlod_linearize(tex, coord, input_gamma).rgb; } - interpolated_line /= float(scanline_num_pixels_fromtexcoord(texcoord)); + interpolated_line /= float(scanline_num_pixels); return interpolated_line; } @@ -402,7 +402,7 @@ float get_curr_scanline_idx( // thickness `scanline_num_pixels` belonging to a single field. const float curr_line_texel_y = floor(texcoord_y * tex_size_y + under_half); - return floor(curr_line_texel_y / scanline_num_pixels_fromtexcoord(texcoord) + FIX_ZERO(0.0)); + return floor(curr_line_texel_y / scanline_num_pixels + FIX_ZERO(0.0)); } float get_scanline_center( @@ -410,9 +410,9 @@ float get_scanline_center( const float line_texel_y, const float scanline_idx ) { - const float scanline_start_y = scanline_idx * scanline_num_pixels_fromtexcoord(texcoord); + const float scanline_start_y = scanline_idx * scanline_num_pixels; - const float half_num_pixels = scanline_num_pixels_fromtexcoord(texcoord) / 2; + const float half_num_pixels = scanline_num_pixels / 2; const float half_size = floor(half_num_pixels + under_half); const float num_pixels_is_even = float(half_size >= half_num_pixels); // const float num_pixels_is_even = float(half_size < half_num_pixels); @@ -510,9 +510,9 @@ float3 get_gaussian_beam_strength( } float3 get_gaussian_beam_strength( - float dist, - float prev_dist, - float3 color, + const float dist, + const float prev_dist, + const float3 color, const float sigma_range, const float shape_range ) { @@ -537,22 +537,13 @@ float3 get_gaussian_beam_strength( } float3 get_linear_beam_strength( - float dist, - float3 color, - const float sigma_range, - const float shape_range, - const float num_pixels_is_even, - const float2 texcoord + const float dist, + const float3 color, + const float num_pixels, + const bool interlaced ) { - const float num_pixels = scanline_num_pixels_fromtexcoord(texcoord); const float3 p = color * (1 - abs(dist)); - const float3 p_clamp = clamp(p, 0, color); - // const float3 odd_err = 2/num_pixels + pow((num_pixels - 1) / num_pixels, 2); - // const float3 err_factor = num_pixels_is_even * odd_err; - - // return p_clamp / err_factor; - if (num_pixels == 1) return p_clamp / 2; - else return p_clamp; + return clamp(p, 0, color); } diff --git a/reshade-shaders/Shaders/crt-royale/lib/user-settings.fxh b/reshade-shaders/Shaders/crt-royale/lib/user-settings.fxh index e47bd31..b24c0a4 100644 --- a/reshade-shaders/Shaders/crt-royale/lib/user-settings.fxh +++ b/reshade-shaders/Shaders/crt-royale/lib/user-settings.fxh @@ -178,7 +178,7 @@ static const float lcd_gamma_static = 2.2; // range [1, 5] // LEVELS MANAGEMENT: // Control the final multiplicative image contrast: -static const float levels_contrast_static = 2.0; // range [0, 4) +static const float levels_contrast_static = 1.0; // range [0, 4) // We auto-dim to avoid clipping between passes and restore brightness // later. Control the dim factor here: Lower values clip less but crush // blacks more (static only for now). diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-deinterlace.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-deinterlace.fxh index 8de99c5..a6ae004 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-deinterlace.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-deinterlace.fxh @@ -14,7 +14,7 @@ void deinterlaceVS( ) { PostProcessVS(id, position, texcoord); - v_step = float2(0.0, scanline_num_pixels_fromtexcoord(texcoord) / TEX_FREEZEFRAME_HEIGHT); + v_step = float2(0.0, scanline_num_pixels / TEX_FREEZEFRAME_HEIGHT); } diff --git a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh index 1f4c9ee..781f7f2 100644 --- a/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh +++ b/reshade-shaders/Shaders/crt-royale/shaders/crt-royale-electron-beams.fxh @@ -52,7 +52,7 @@ void simulateInterlacingPS( // tex2D_linearize will decode CRT gamma. Don't bother branching: float curr_scanline_idx = get_curr_scanline_idx(texcoord, texcoord.y, content_size.y); float curr_scanline_start_y = ( - curr_scanline_idx * scanline_num_pixels_fromtexcoord(texcoord) + TEXCOORD_OFFSET + curr_scanline_idx * scanline_num_pixels + TEXCOORD_OFFSET ) / content_size.y; float3 in_field_interpolated_line = get_averaged_scanline_sample( samplerCrop, texcoord, @@ -71,67 +71,10 @@ void simulateEletronBeamsVS( out float4 position : SV_Position, out float2 texcoord : TEXCOORD0, - out float2 v_step : TEXCOORD1, - out float2 gauss_adj_factor : TEXCOORD2 + out float2 v_step : TEXCOORD1 ) { PostProcessVS(id, position, texcoord); - v_step = float2(0.0, 1.0 / tex2Dsize(samplerInterlaced).y); - - const float sigma_range = max(beam_max_sigma, beam_min_sigma) - beam_min_sigma; - const float shape_range = max(beam_max_shape, beam_min_shape) - beam_min_shape; - const float3 beam_strength_0 = get_gaussian_beam_strength( - 0.0, float3(1, 1, 1), - sigma_range, shape_range - ).r; - - const float max_embedding_dist = scanline_max_embedding_dist; - const float beam_dist_factor = 1 + float(enable_interlacing); - const float target_num_pixels = 50; - - const float delta_target = 2 * max_embedding_dist / (beam_dist_factor * target_num_pixels); - const float delta_left = 2 * max_embedding_dist / (beam_dist_factor * scanline_num_pixels_left); - const float delta_right = 2 * max_embedding_dist / (beam_dist_factor * scanline_num_pixels_right); - - const float min_dist_target = -(max_embedding_dist - delta_target/2.0); - const float min_dist_left = -(max_embedding_dist - delta_left/2.0); - const float min_dist_right = -(max_embedding_dist - delta_right/2.0); - - float target_gauss_beam_integral = 0.0; - for (int i = 0; i < target_num_pixels; i++) { - const float d = min_dist_target + delta_target*i; - target_gauss_beam_integral += get_gaussian_beam_strength( - d, float3(1, 1, 1), - sigma_range, shape_range - ).r; - } - target_gauss_beam_integral *= delta_target; - - float approx_gauss_beam_integral_left = 0.0; - for (int i = 0; i < scanline_num_pixels_left; i++) { - const float d = min_dist_left + delta_left*i; - approx_gauss_beam_integral_left += get_gaussian_beam_strength( - d, float3(1, 1, 1), - sigma_range, shape_range - ).r; - } - approx_gauss_beam_integral_left *= delta_left; - - float approx_gauss_beam_integral_right = 0.0; - for (int i = 0; i < scanline_num_pixels_right; i++) { - const float d = min_dist_right + delta_right*i; - approx_gauss_beam_integral_right += get_gaussian_beam_strength( - d, float3(1, 1, 1), - sigma_range, shape_range - ).r; - } - approx_gauss_beam_integral_right *= delta_right; - - gauss_adj_factor = target_gauss_beam_integral / float2( - approx_gauss_beam_integral_left, - approx_gauss_beam_integral_right - ); - // gauss_adj_factor = float2(1.0, 1.0); } @@ -139,47 +82,96 @@ void simulateEletronBeamsPS( in const float4 position : SV_Position, in const float2 texcoord : TEXCOORD0, in const float2 v_step : TEXCOORD1, - in const float2 gauss_adj_factor_lr : TEXCOORD2, out float4 color : SV_Target ) { const float2 orig_linearized_size = tex2Dsize(samplerInterlaced); - const float wrong_field = curr_line_is_wrong_field(texcoord, texcoord.y, orig_linearized_size.y); - const float coord_is_left = float(texcoord.x <= 0.5); - const float gauss_adj_factor = coord_is_left * gauss_adj_factor_lr.x + (1 - coord_is_left) * gauss_adj_factor_lr.y; - // Digital shape // Beam will be perfectly rectangular if (beam_shape_mode == 0) { - // If we're in the current field, draw the beam - // wrong_field is always 0 when we aren't interlacing + // const float3 scanline_color = tex2D_linearize(samplerInterlaced, texcoord, get_intermediate_gamma()).rgb; + const float3 scanline_color = sample_single_scanline_horizontal( + samplerInterlaced, + texcoord, orig_linearized_size, + 1 / orig_linearized_size + ); + // Double the intensity when interlacing to maintain the same apparent brightness const float interlacing_brightness_factor = 1 + enable_interlacing * float( scanline_deinterlacing_mode != 1 && scanline_deinterlacing_mode != 2 ); + + // If we're in the current field, draw the beam + // wrong_field is always 0 when we aren't interlacing + const float3 beam_strength = (1 - wrong_field) * interlacing_brightness_factor * scanline_color; - // const float3 scanline_color = tex2D_linearize(samplerInterlaced, texcoord, get_intermediate_gamma()).rgb; + // Temporarily auto-dim the output to avoid clipping. + color = encode_output(float4(beam_strength * levels_autodim_temp, 1.0), get_intermediate_gamma()); + } + // Linear shape + // Beam intensity will drop off linarly with distance from center + // Works better than gaussian with narrow scanlines (about 1-6 pixels wide) + // Will only consider contribution from nearest scanline + else if (beam_shape_mode == 1) { + // Find the texel position of the current scanline + const float curr_line_texel_v = floor(texcoord.y * orig_linearized_size.y + under_half); + const float curr_scanline_idx = get_curr_scanline_idx(texcoord, texcoord.y, orig_linearized_size.y); + const float curr_scanline_start_v = curr_scanline_idx * scanline_num_pixels; + + // Find the center of the current scanline + // For odd sizes, this is a texel. For even, this is between two texels + const float half_num_pixels = scanline_num_pixels / 2; + const float half_size = floor(half_num_pixels + under_half); + const float num_pixels_is_even = float(half_size >= half_num_pixels); + const float curr_scanline_center_v = curr_scanline_start_v + half_num_pixels - 0.5; + + // Find the center of the nearest in-field scanline + const float curr_line_is_below_center = float(curr_line_texel_v > curr_scanline_center_v); + const float source_offset_direction = lerp(-1, 1, curr_line_is_below_center); + const float source_offset = source_offset_direction * wrong_field * scanline_num_pixels; + + const float source_scanline_center_v = curr_scanline_center_v + source_offset; + const float source_scanline_start_v = curr_scanline_start_v + source_offset; + const float source_scanline_center_y = (source_scanline_center_v + TEXCOORD_OFFSET) / orig_linearized_size.y; + const float2 source_scanline_center_xy = float2(texcoord.x, source_scanline_center_y); + + // Sample the nearest in-field scanline const float3 scanline_color = sample_single_scanline_horizontal( samplerInterlaced, - texcoord, orig_linearized_size, + source_scanline_center_xy, orig_linearized_size, 1 / orig_linearized_size ); - const float3 scanline_intensity = (1 - wrong_field) * interlacing_brightness_factor * scanline_color; + // Calculate the beam strength based upon distance from the scanline + // and intensity of the sampled color + const float beam_dist_factor = 1 + float(enable_interlacing); + const float pixel_delta = 2 * scanline_max_embedding_dist / (beam_dist_factor * scanline_num_pixels); + const float max_beam_dist = scanline_max_embedding_dist - pixel_delta/2.0; + const float beam_dist_denom = half_num_pixels / scanline_max_embedding_dist; + + const float beam_dist_v = curr_line_texel_v - source_scanline_center_v; + const float beam_dist_y = scanline_max_embedding_dist * beam_dist_v / beam_dist_denom; + + const float interlacing_brightness_factor = 2 + 2 * enable_interlacing * float( + scanline_deinterlacing_mode != 1 && + scanline_deinterlacing_mode != 2 + ); + const float interlacing_brightness_quotient = 1 + !enable_interlacing * float(scanline_num_pixels == 1); + const float3 beam_strength = get_linear_beam_strength( + beam_dist_y, scanline_color, + scanline_num_pixels, + enable_interlacing + ) * interlacing_brightness_factor / interlacing_brightness_quotient; - // Temporarily auto-dim the output to avoid clipping. - color = encode_output(float4(scanline_intensity * levels_autodim_temp, 1.0), get_intermediate_gamma()); + color = encode_output(float4(beam_strength * levels_autodim_temp, 1), get_intermediate_gamma()); } - // else if (beam_shape_mode == 1) { - - // } // Gaussian Shape // Beam will be a distorted Gaussian, dependent on color brightness and hyperparameters // Will only consider contribution from nearest scanline - else if (beam_shape_mode == 3) { + else if (beam_shape_mode == 2) { // Calculate {sigma, shape}_range outside of scanline_contrib so it's only // done once per pixel (not 6 times) with runtime params. Don't reuse the // vertex shader calculations, so static versions can be constant-folded. @@ -189,115 +181,56 @@ void simulateEletronBeamsPS( // Find the texel position of the current scanline const float curr_line_texel_v = floor(texcoord.y * orig_linearized_size.y + under_half); const float curr_scanline_idx = get_curr_scanline_idx(texcoord, texcoord.y, orig_linearized_size.y); - const float curr_scanline_start_v = curr_scanline_idx * scanline_num_pixels_fromtexcoord(texcoord); + const float curr_scanline_start_v = curr_scanline_idx * scanline_num_pixels; // Find the center of the current scanline // For odd sizes, this is a texel. For even, this is between two texels - const float half_num_pixels = scanline_num_pixels_fromtexcoord(texcoord) / 2; + const float half_num_pixels = scanline_num_pixels / 2; const float half_size = floor(half_num_pixels + under_half); const float num_pixels_is_even = float(half_size >= half_num_pixels); const float curr_scanline_center_v = curr_scanline_start_v + half_num_pixels - 0.5; - /* - // Find the center of the current scanline - const float half_num_pixels = scanline_num_pixels_fromtexcoord(texcoord) / 2; - const float half_size = floor(half_num_pixels + under_half); - const float num_pixels_is_even = float(half_size >= half_num_pixels); - const float upper_center = curr_scanline_start_v + half_size; - // Lower on screen means larger y-coordinate - const float curr_line_is_below_center = float(curr_line_texel_v > upper_center); - const float shift_center = num_pixels_is_even * curr_line_is_below_center; - - const float curr_scanline_center_v = upper_center + shift_center; - // const float curr_scanline_center_y = (curr_scanline_center_v + TEXCOORD_OFFSET) / orig_linearized_size.y; - */ - // Find the center of the nearest in-field scanline const float curr_line_is_below_center = float(curr_line_texel_v > curr_scanline_center_v); const float source_offset_direction = lerp(-1, 1, curr_line_is_below_center); - const float source_offset = source_offset_direction * wrong_field * scanline_num_pixels_fromtexcoord(texcoord); + const float source_offset = source_offset_direction * wrong_field * scanline_num_pixels; const float source_scanline_center_v = curr_scanline_center_v + source_offset; const float source_scanline_start_v = curr_scanline_start_v + source_offset; - // const float source_scanline_center_y = (source_scanline_center_v + TEXCOORD_OFFSET) / orig_linearized_size.y; - // const float2 source_scanline_center_xy = float2(texcoord.x, source_scanline_center_y); - const float source_scanline_start_y = (source_scanline_start_v + TEXCOORD_OFFSET) / orig_linearized_size.y; - const float2 source_scanline_start_xy = float2(texcoord.x, source_scanline_start_y); + const float source_scanline_center_y = (source_scanline_center_v + TEXCOORD_OFFSET) / orig_linearized_size.y; + const float2 source_scanline_center_xy = float2(texcoord.x, source_scanline_center_y); // Sample the nearest in-field scanline const float3 scanline_color = sample_single_scanline_horizontal( samplerInterlaced, - source_scanline_start_xy, orig_linearized_size, + source_scanline_center_xy, orig_linearized_size, 1 / orig_linearized_size ); - // const float3 scanline_color = float3(1, 1, 1); - - // const float4 scanline_color = decode_input( - // tex2D(samplerInterlaced, source_scanline_center_xy), - // get_intermediate_gamma() - // ); // Calculate the beam strength based upon distance from the scanline // and intensity of the sampled color - // const float max_beam_dist = max(1, max_beam_dist_factor*half_size - 1); - // const float max_beam_dist = max(1, beam_dist_factor*half_num_pixels); - // const float min_beam_dist = num_pixels_is_even * 0.5; const float beam_dist_factor = 1 + float(enable_interlacing); - const float pixel_delta = 2 * scanline_max_embedding_dist / (beam_dist_factor * scanline_num_pixels_fromtexcoord(texcoord)); + const float pixel_delta = 2 * scanline_max_embedding_dist / (beam_dist_factor * scanline_num_pixels); const float max_beam_dist = scanline_max_embedding_dist - pixel_delta/2.0; - const float beam_dist_denom = half_num_pixels / scanline_max_embedding_dist; - + const float beam_dist_denom = beam_dist_factor * half_num_pixels / scanline_max_embedding_dist; - /* - // For even-sized scanlines, offset by 0.5; so we take distance at the center of the texel - // TODO: Applying to odd-sized scanlines instead helps with color mismatch. Not sure why. - const float beam_dist_v_offset = num_pixels_is_even * 0.5; - // const float beam_dist_v_offset = (1-num_pixels_is_even) * 0.5; - // const float beam_dist_v_offset = 0.0; - const float beam_dist_v_raw = curr_line_texel_v - source_scanline_center_v; - const float beam_dist_v_root = round(abs(beam_dist_v_raw) - beam_dist_v_offset); - const float beam_dist_v = sign(beam_dist_v_raw) * (beam_dist_v_root + beam_dist_v_offset); - // const float beam_dist_v = abs(curr_line_texel_v - source_scanline_center_v); - const float beam_dist_y = beam_power_adj_fromtexcoord(texcoord) * beam_dist_v / max_beam_dist; - */ const float beam_dist_v = curr_line_texel_v - source_scanline_center_v; const float beam_dist_y = scanline_max_embedding_dist * beam_dist_v / beam_dist_denom; - // const float eq_dist = float(abs(abs(beam_dist_y) - max_beam_dist) <= 1e-5); - - // const float prev_dist_offset_v = source_offset_direction * - // float(beam_dist_v > 0) * max(0.5, sign(beam_dist_y)); - // const float prev_dist_offset_y = prev_dist_offset_v / max_beam_dist; - // const float prev_dist_y = beam_dist_y + prev_dist_offset_y; - /* - const float3 beam_strength = get_lowres_gaussian_beam_strength( - beam_dist_y, scanline_color, - sigma_range, shape_range - ) * (1 - wrong_field); - */ - - const float3 beam_strength = get_linear_beam_strength( - beam_dist_y, scanline_color, - sigma_range, shape_range, - num_pixels_is_even, texcoord + const float interlacing_brightness_factor = 2 - !enable_interlacing * float( + scanline_num_pixels == 1 ); - // const float3 beam_strength = scanline_color; - - const float interlacing_brightness_factor = 1 + enable_interlacing * float( - scanline_deinterlacing_mode != 1 && - scanline_deinterlacing_mode != 2 + const float interlacing_brightness_quotient = 1 + enable_interlacing * float( + scanline_deinterlacing_mode == 1 || + scanline_deinterlacing_mode == 2 ); - // Output the corrected color - // if (eq_dist) { - // color = encode_output(float4(1, 0, 0, 1), get_intermediate_gamma()); - // } - // else { - color = encode_output(float4(beam_strength * interlacing_brightness_factor, 1), get_intermediate_gamma()); - // } - - // color = encode_output(float4(scanline_color, 1), get_intermediate_gamma()); - // color = encode_output(float4(beam_dist_y, beam_dist_y, beam_dist_y, 1.0), get_intermediate_gamma()); + const float3 beam_strength = get_gaussian_beam_strength( + beam_dist_y, scanline_color, + sigma_range, shape_range + ) * interlacing_brightness_factor / interlacing_brightness_quotient; + + color = encode_output(float4(beam_strength * levels_autodim_temp, 1), get_intermediate_gamma()); } // Gaussian Shape // Beam will be a distorted Gaussian, dependent on color brightness and hyperparameters @@ -312,24 +245,28 @@ void simulateEletronBeamsPS( // Find the texel position of the current scanline const float curr_line_texel_v = floor(texcoord.y * orig_linearized_size.y + under_half); const float curr_scanline_idx = get_curr_scanline_idx(texcoord, texcoord.y, orig_linearized_size.y); - const float curr_scanline_start_v = curr_scanline_idx * scanline_num_pixels_fromtexcoord(texcoord); + const float curr_scanline_start_v = curr_scanline_idx * scanline_num_pixels; // Find the center of the current scanline - const float half_num_pixels = scanline_num_pixels_fromtexcoord(texcoord) / 2; + // For odd sizes, this is a texel. For even, this is between two texels + const float half_num_pixels = scanline_num_pixels / 2; const float half_size = floor(half_num_pixels + under_half); const float num_pixels_is_even = float(half_size >= half_num_pixels); - const float upper_center = curr_scanline_start_v + half_size; - // Lower on screen means larger y-coordinate - const float curr_line_is_below_center = float(curr_line_texel_v > upper_center); - const float shift_center = num_pixels_is_even * curr_line_is_below_center; - const float bounding_scanline_offset_v = (2 - wrong_field) * scanline_num_pixels_fromtexcoord(texcoord); + const float curr_scanline_center_v = curr_scanline_start_v + half_num_pixels - 0.5; + + // Find the center of the nearest in-field scanline + const float curr_line_is_below_center = float(curr_line_texel_v > curr_scanline_center_v); + const float source_offset_direction = lerp(-1, 1, curr_line_is_below_center); + const float source_offset = source_offset_direction * wrong_field * scanline_num_pixels; + + const float bounding_scanline_offset_v = (2 - wrong_field) * scanline_num_pixels; const float3 scanline_offsets_v = float3( -bounding_scanline_offset_v, 0, bounding_scanline_offset_v ); - const float3 scanline_centers_v = upper_center + shift_center + scanline_offsets_v; + const float3 scanline_centers_v = curr_scanline_center_v + scanline_offsets_v; const float3 scanline_centers_y = ( scanline_centers_v + TEXCOORD_OFFSET ) / orig_linearized_size.y; @@ -356,12 +293,21 @@ void simulateEletronBeamsPS( // Calculate the beam strength based upon distance from the scanline // and intensity of the sampled color - const float scanlines_wider_than_1 = float(scanline_num_pixels_fromtexcoord(texcoord) > 1); - const float max_beam_dist_factor = 1 + float(enable_interlacing); - const float max_beam_dist = max(1, max_beam_dist_factor*half_size - 1); + const float beam_dist_factor = 1 + float(enable_interlacing); + const float pixel_delta = 2 * scanline_max_embedding_dist / (beam_dist_factor * scanline_num_pixels); + const float max_beam_dist = scanline_max_embedding_dist - pixel_delta/2.0; + const float beam_dist_denom = beam_dist_factor * half_num_pixels / scanline_max_embedding_dist; + + const float3 beam_dists_v = curr_line_texel_v - scanline_centers_v; + const float3 beam_dists_y = scanline_max_embedding_dist * beam_dists_v / beam_dist_denom; - const float3 beam_dists_v = abs(curr_line_texel_v - scanline_centers_v); - const float3 beam_dists_y = beam_dists_v / max_beam_dist; + const float interlacing_brightness_factor = 2 - !enable_interlacing * float( + scanline_num_pixels == 1 + ); + const float interlacing_brightness_quotient = 1 + enable_interlacing * float( + scanline_deinterlacing_mode == 1 || + scanline_deinterlacing_mode == 2 + ); const float3 upper_beam_strength = get_gaussian_beam_strength( beam_dists_y.x, upper_scanline_color, @@ -379,10 +325,10 @@ void simulateEletronBeamsPS( upper_beam_strength + (1 - wrong_field) * curr_beam_strength + lower_beam_strength - ); + ) * interlacing_brightness_factor / interlacing_brightness_quotient; // Output the corrected color - color = encode_output(float4(beam_strength * levels_autodim_temp, 1), get_intermediate_gamma()); + color = encode_output(float4(beam_strength * levels_autodim_temp, 1), get_intermediate_gamma()); } }