mirror of
https://github.com/WinampDesktop/winamp.git
synced 2025-05-19 02:05:43 -04:00
347 lines
15 KiB
HLSL
347 lines
15 KiB
HLSL
#ifndef _ELECTRON_BEAMS_H
|
|
#define _ELECTRON_BEAMS_H
|
|
|
|
///////////////////////////// GPL LICENSE NOTICE /////////////////////////////
|
|
|
|
// crt-royale: A full-featured CRT shader, with cheese.
|
|
// Copyright (C) 2014 TroggleMonkey <trogglemonkey@gmx.com>
|
|
//
|
|
// crt-royale-reshade: A port of TroggleMonkey's crt-royale from libretro to ReShade.
|
|
// Copyright (C) 2020 Alex Gunter <akg7634@gmail.com>
|
|
//
|
|
// 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/scanline-functions.fxh"
|
|
|
|
#include "content-box.fxh"
|
|
#include "shared-objects.fxh"
|
|
|
|
|
|
void calculateBeamDistsVS(
|
|
in uint id : SV_VertexID,
|
|
|
|
out float4 position : SV_Position,
|
|
out float2 texcoord : TEXCOORD0
|
|
) {
|
|
const float compute_mask_factor = frame_count % 60 == 0 || overlay_active > 0;
|
|
|
|
texcoord.x = (id == 2) ? compute_mask_factor*2.0 : 0.0;
|
|
texcoord.y = (id == 1) ? 2.0 : 0.0;
|
|
position = float4(texcoord * float2(2, -2) + float2(-1, 1), 0, 1);
|
|
}
|
|
|
|
|
|
void calculateBeamDistsPS(
|
|
in float4 position : SV_Position,
|
|
in float2 texcoord : TEXCOORD0,
|
|
|
|
out float4 beam_strength : SV_Target
|
|
) {
|
|
InterpolationFieldData interpolation_data = precalc_interpolation_field_data(texcoord);
|
|
|
|
// We have to subtract off the texcoord offset to make sure we're using domain [0, 1]
|
|
const float color_corrected = texcoord.x - 1.0 / TEX_BEAMDIST_WIDTH;
|
|
|
|
// Digital shape
|
|
// Beam will be perfectly rectangular
|
|
[branch]
|
|
if (beam_shape_mode == 0) {
|
|
// Double the intensity when interlacing to maintain the same apparent brightness
|
|
const float interlacing_brightness_factor = 1 + float(
|
|
enable_interlacing &&
|
|
(scanline_deinterlacing_mode != 2) &&
|
|
(scanline_deinterlacing_mode != 3)
|
|
);
|
|
const float raw_beam_strength = (1 - interpolation_data.scanline_parity * enable_interlacing) * interlacing_brightness_factor * levels_autodim_temp;
|
|
|
|
beam_strength = float4(color_corrected * raw_beam_strength, 0, 0, 1);
|
|
}
|
|
// 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) {
|
|
const float beam_dist_y = triangle_wave(texcoord.y, interpolation_data.triangle_wave_freq);
|
|
|
|
const bool scanline_is_wider_than_1 = scanline_thickness > 1;
|
|
const bool deinterlacing_mode_requires_boost = (
|
|
enable_interlacing &&
|
|
(scanline_deinterlacing_mode != 2) &&
|
|
(scanline_deinterlacing_mode != 3)
|
|
);
|
|
|
|
const float interlacing_brightness_factor = (1 + scanline_is_wider_than_1) * (1 + deinterlacing_mode_requires_boost);
|
|
// const float raw_beam_strength = (1 - beam_dist_y) * (1 - interpolation_data.scanline_parity * enable_interlacing) * interlacing_brightness_factor * levels_autodim_temp;
|
|
// const float raw_beam_strength = (1 - beam_dist_y);
|
|
const float raw_beam_strength = saturate(-beam_dist_y * rcp(linear_beam_thickness) + 1);
|
|
const float adj_beam_strength = raw_beam_strength * (1 - interpolation_data.scanline_parity * enable_interlacing) * interlacing_brightness_factor * levels_autodim_temp;
|
|
|
|
beam_strength = float4(color_corrected * adj_beam_strength, 0, 0, 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 == 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.
|
|
const float sigma_range = max(gaussian_beam_max_sigma, gaussian_beam_min_sigma) - gaussian_beam_min_sigma;
|
|
const float shape_range = max(gaussian_beam_max_shape, gaussian_beam_min_shape) - gaussian_beam_min_shape;
|
|
|
|
const float beam_dist_factor = 1 + float(enable_interlacing);
|
|
const float freq_adj = interpolation_data.triangle_wave_freq * rcp(beam_dist_factor);
|
|
// The conditional 0.25*f offset ensures the interlaced scanlines align with the non-interlaced ones as in the other beam shapes
|
|
const float frame_offset = enable_interlacing * (!interpolation_data.field_parity * 0.5 + 0.25) * rcp(freq_adj);
|
|
const float beam_dist_y = triangle_wave((texcoord.y - frame_offset), freq_adj) * rcp(linear_beam_thickness);
|
|
|
|
const float interlacing_brightness_factor = 1 + float(
|
|
!enable_interlacing &&
|
|
(scanline_thickness > 1)
|
|
) + float(
|
|
enable_interlacing &&
|
|
(scanline_deinterlacing_mode != 2) &&
|
|
(scanline_deinterlacing_mode != 3)
|
|
);
|
|
const float raw_beam_strength = get_gaussian_beam_strength(
|
|
beam_dist_y, color_corrected,
|
|
sigma_range, shape_range
|
|
) * interlacing_brightness_factor * levels_autodim_temp;
|
|
|
|
beam_strength = float4(raw_beam_strength, 0, 0, 1);
|
|
}
|
|
// 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(gaussian_beam_max_sigma, gaussian_beam_min_sigma) - gaussian_beam_min_sigma;
|
|
const float shape_range = max(gaussian_beam_max_shape, gaussian_beam_min_shape) - gaussian_beam_min_shape;
|
|
|
|
const float beam_dist_factor = (1 + float(enable_interlacing));
|
|
const float freq_adj = interpolation_data.triangle_wave_freq * rcp(beam_dist_factor);
|
|
// The conditional 0.25*f offset ensures the interlaced scanlines align with the non-interlaced ones as in the other beam shapes
|
|
const float frame_offset = enable_interlacing * (!interpolation_data.field_parity * 0.5 + 0.25) * rcp(freq_adj);
|
|
const float curr_beam_dist_y = triangle_wave(texcoord.y - frame_offset, freq_adj) * rcp(linear_beam_thickness);
|
|
const float upper_beam_dist_y = (sawtooth_incr_wave(texcoord.y - frame_offset, freq_adj)*2 + 1) * rcp(linear_beam_thickness);
|
|
const float lower_beam_dist_y = 4 * rcp(linear_beam_thickness) - upper_beam_dist_y;
|
|
|
|
const float upper_beam_strength = get_gaussian_beam_strength(
|
|
upper_beam_dist_y, color_corrected,
|
|
sigma_range, shape_range
|
|
);
|
|
const float curr_beam_strength = get_gaussian_beam_strength(
|
|
curr_beam_dist_y, color_corrected,
|
|
sigma_range, shape_range
|
|
);
|
|
const float lower_beam_strength = get_gaussian_beam_strength(
|
|
lower_beam_dist_y, color_corrected,
|
|
sigma_range, shape_range
|
|
);
|
|
|
|
const float interlacing_brightness_factor = 1 + float(
|
|
!enable_interlacing &&
|
|
(scanline_thickness > 1)
|
|
) + float(
|
|
enable_interlacing &&
|
|
(scanline_deinterlacing_mode != 2) &&
|
|
(scanline_deinterlacing_mode != 3)
|
|
);
|
|
const float3 raw_beam_strength = float3(curr_beam_strength, upper_beam_strength, lower_beam_strength) * interlacing_brightness_factor * levels_autodim_temp;
|
|
|
|
beam_strength = float4(raw_beam_strength, 1);
|
|
}
|
|
}
|
|
|
|
|
|
void simulateEletronBeamsVS(
|
|
in uint id : SV_VertexID,
|
|
|
|
out float4 position : SV_Position,
|
|
out float2 texcoord : TEXCOORD0,
|
|
out float4 runtime_bin_shapes : TEXCOORD1
|
|
) {
|
|
#if ENABLE_PREBLUR
|
|
PostProcessVS(id, position, texcoord);
|
|
#else
|
|
// texcoord.x = (id == 0 || id == 2) ? content_left : content_right;
|
|
// texcoord.y = (id < 2) ? content_lower : content_upper;
|
|
// position.x = (id == 0 || id == 2) ? -1 : 1;
|
|
// position.y = (id < 2) ? -1 : 1;
|
|
// position.zw = 1;
|
|
contentCropVS(id, position, texcoord);
|
|
#endif
|
|
|
|
bool screen_is_landscape = geom_rotation_mode == 0 || geom_rotation_mode == 2;
|
|
|
|
// Mode 0: size of pixel in [0, 1] = pixel_dims / viewport_size
|
|
// Mode 1: size of pixel in [0, 1] = viewport_size / grid_dims
|
|
// float2 runtime_pixel_size = (pixel_grid_mode == 0) ? pixel_size * rcp(content_size) : rcp(pixel_grid_resolution);
|
|
float2 runtime_pixel_size = rcp(content_size);
|
|
float2 runtime_scanline_shape = lerp(
|
|
float2(scanline_thickness, 1),
|
|
float2(1, scanline_thickness),
|
|
screen_is_landscape
|
|
) * rcp(content_size);
|
|
|
|
runtime_bin_shapes = float4(runtime_pixel_size, runtime_scanline_shape);
|
|
}
|
|
|
|
void simulateEletronBeamsPS(
|
|
in float4 position : SV_Position,
|
|
in float2 texcoord : TEXCOORD0,
|
|
in float4 runtime_bin_shapes : TEXCOORD1,
|
|
|
|
out float4 color : SV_Target
|
|
) {
|
|
bool screen_is_landscape = geom_rotation_mode == 0 || geom_rotation_mode == 2;
|
|
float2 rotated_coord = lerp(texcoord.yx, texcoord, screen_is_landscape);
|
|
float scale = lerp(CONTENT_WIDTH, CONTENT_HEIGHT, screen_is_landscape);
|
|
|
|
// InterpolationFieldData interpolation_data = precalc_interpolation_field_data(rotated_coord);
|
|
|
|
// // We have to subtract off the texcoord offset to make sure we're using domain [0, 1]
|
|
// const float color_corrected = rotated_coord.x - 1.0 / scale;
|
|
|
|
|
|
InterpolationFieldData interpolation_data = calc_interpolation_field_data(rotated_coord, scale);
|
|
const float ypos = (rotated_coord.y * interpolation_data.triangle_wave_freq + interpolation_data.field_parity) * 0.5;
|
|
|
|
float2 texcoord_scanlined = round_coord(texcoord, 0, runtime_bin_shapes.zw);
|
|
|
|
// Sample from the neighboring scanline when in the wrong field
|
|
[branch]
|
|
if (interpolation_data.wrong_field && screen_is_landscape) {
|
|
const float coord_moved_up = texcoord_scanlined.y <= texcoord.y;
|
|
const float direction = lerp(-1, 1, coord_moved_up);
|
|
texcoord_scanlined.y += direction * scanline_thickness * rcp(content_size.y);
|
|
}
|
|
else if (interpolation_data.wrong_field) {
|
|
const float coord_moved_up = texcoord_scanlined.x <= texcoord.x;
|
|
const float direction = lerp(-1, 1, coord_moved_up);
|
|
texcoord_scanlined.x += direction * scanline_thickness * rcp(content_size.x);
|
|
}
|
|
|
|
// Now we apply pixellation and cropping
|
|
// float2 texcoord_pixellated = round_coord(
|
|
// texcoord_scanlined,
|
|
// pixel_grid_offset * rcp(content_size),
|
|
// runtime_bin_shapes.xy
|
|
// );
|
|
float2 texcoord_pixellated = texcoord_scanlined;
|
|
|
|
const float2 texcoord_uncropped = texcoord_pixellated;
|
|
#if ENABLE_PREBLUR
|
|
// If the pre-blur pass ran, then it's already handled cropping.
|
|
// const float2 texcoord_uncropped = texcoord_pixellated;
|
|
#define source_sampler samplerPreblurHoriz
|
|
#else
|
|
// const float2 texcoord_uncropped = texcoord_pixellated * content_scale + content_offset;
|
|
#define source_sampler ReShade::BackBuffer
|
|
#endif
|
|
|
|
[branch]
|
|
if (beam_shape_mode < 3) {
|
|
const float4 scanline_color = tex2Dlod_linearize(
|
|
source_sampler,
|
|
texcoord_uncropped,
|
|
get_input_gamma()
|
|
);
|
|
|
|
const float beam_strength_r = tex2D_nograd(samplerBeamDist, float2(scanline_color.r, ypos)).x;
|
|
const float beam_strength_g = tex2D_nograd(samplerBeamDist, float2(scanline_color.g, ypos)).x;
|
|
const float beam_strength_b = tex2D_nograd(samplerBeamDist, float2(scanline_color.b, ypos)).x;
|
|
const float4 beam_strength = float4(beam_strength_r, beam_strength_g, beam_strength_b, 1);
|
|
|
|
color = beam_strength;
|
|
}
|
|
else {
|
|
const float2 offset = float2(0, scanline_thickness) * (1 + enable_interlacing) * rcp(content_size);
|
|
|
|
const float4 curr_scanline_color = tex2Dlod_linearize(
|
|
source_sampler,
|
|
texcoord_uncropped,
|
|
get_input_gamma()
|
|
);
|
|
const float4 upper_scanline_color = tex2Dlod_linearize(
|
|
source_sampler,
|
|
texcoord_uncropped - offset,
|
|
get_input_gamma()
|
|
);
|
|
const float4 lower_scanline_color = tex2Dlod_linearize(
|
|
source_sampler,
|
|
texcoord_uncropped + offset,
|
|
get_input_gamma()
|
|
);
|
|
|
|
const float curr_beam_strength_r = tex2D_nograd(samplerBeamDist, float2(curr_scanline_color.r, ypos)).x;
|
|
const float curr_beam_strength_g = tex2D_nograd(samplerBeamDist, float2(curr_scanline_color.g, ypos)).x;
|
|
const float curr_beam_strength_b = tex2D_nograd(samplerBeamDist, float2(curr_scanline_color.b, ypos)).x;
|
|
|
|
const float upper_beam_strength_r = tex2D_nograd(samplerBeamDist, float2(upper_scanline_color.r, ypos)).y;
|
|
const float upper_beam_strength_g = tex2D_nograd(samplerBeamDist, float2(upper_scanline_color.g, ypos)).y;
|
|
const float upper_beam_strength_b = tex2D_nograd(samplerBeamDist, float2(upper_scanline_color.b, ypos)).y;
|
|
|
|
const float lower_beam_strength_r = tex2D_nograd(samplerBeamDist, float2(lower_scanline_color.r, ypos)).z;
|
|
const float lower_beam_strength_g = tex2D_nograd(samplerBeamDist, float2(lower_scanline_color.g, ypos)).z;
|
|
const float lower_beam_strength_b = tex2D_nograd(samplerBeamDist, float2(lower_scanline_color.b, ypos)).z;
|
|
|
|
color = float4(
|
|
curr_beam_strength_r + upper_beam_strength_r + lower_beam_strength_r,
|
|
curr_beam_strength_g + upper_beam_strength_g + lower_beam_strength_g,
|
|
curr_beam_strength_b + upper_beam_strength_b + lower_beam_strength_b,
|
|
1
|
|
);
|
|
}
|
|
}
|
|
|
|
void beamConvergenceVS(
|
|
in uint id : SV_VertexID,
|
|
|
|
out float4 position : SV_Position,
|
|
out float2 texcoord : TEXCOORD0,
|
|
out float run_convergence : TEXCOORD1
|
|
) {
|
|
PostProcessVS(id, position, texcoord);
|
|
const uint3 x_flag = convergence_offset_x != 0;
|
|
const uint3 y_flag = convergence_offset_y != 0;
|
|
run_convergence = dot(x_flag, 1) + dot(y_flag, 1);
|
|
}
|
|
|
|
void beamConvergencePS(
|
|
in float4 position : SV_Position,
|
|
in float2 texcoord : TEXCOORD0,
|
|
in float run_convergence : TEXCOORD1,
|
|
|
|
out float4 color : SV_TARGET
|
|
) {
|
|
// [branch]
|
|
if (!run_convergence) {
|
|
color = tex2D(samplerElectronBeams, texcoord - float2(0, scanline_offset * rcp(content_size.y)));
|
|
}
|
|
else {
|
|
const float3 offset_sample = sample_rgb_scanline(
|
|
samplerElectronBeams, texcoord - float2(0, scanline_offset * rcp(content_size.y)),
|
|
TEX_ELECTRONBEAMS_SIZE, rcp(TEX_ELECTRONBEAMS_SIZE)
|
|
);
|
|
|
|
color = float4(offset_sample, 1);
|
|
}
|
|
}
|
|
|
|
#endif // _ELECTRON_BEAMS_H |