Duckstation/data/resources/shaders/reshade/Shaders/crt-royale/shaders/phosphor-mask.fxh

211 lines
7.5 KiB
HLSL

#ifndef _PHOSPHOR_MASK_H
#define _PHOSPHOR_MASK_H
///////////////////////////////// MIT LICENSE ////////////////////////////////
// Copyright (C) 2022 Alex Gunter
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include "../lib/bind-shader-params.fxh"
#include "../lib/phosphor-mask-calculations.fxh"
#include "shared-objects.fxh"
// Split into 64 segments that overlap a little bit
static const float num_segments = 64;
static const float segment_offset = 0.015625; // 1/64
static const float segment_width = 0.0234375; // 1/128
void generatePhosphorMaskVS(
in uint id : SV_VertexID,
out float4 position : SV_Position,
out float2 texcoord : TEXCOORD0,
out float2 viewport_frequency_factor: TEXCOORD1,
out float2 mask_pq_x : TEXCOORD2,
out float2 mask_pq_y : TEXCOORD3
) {
const float screen_segment_idx = frame_count % num_segments;
const float left_coord = lerp(segment_offset * screen_segment_idx, 0, overlay_active > 0);
const float right_coord = lerp(left_coord + segment_width, 1, overlay_active > 0);
const float pos_center = 2 * (left_coord + 0.5 * segment_width - 0.5);
const float pos_left = lerp(pos_center - segment_width, -1, overlay_active > 0);
const float pos_right = lerp(pos_center + segment_width, 1, overlay_active > 0);
#if _DX9_ACTIVE
texcoord.x = (id == 1 || id == 3) ? right_coord : left_coord;
texcoord.y = (id > 1) ? 1 : 0;
position.x = (id == 1 || id == 3) ? pos_right : pos_left;
position.y = (id > 1) ? -1 : 1;
position.zw = 1;
#else
texcoord.x = (id & 1) ? right_coord : left_coord;
texcoord.y = (id & 2) ? 1 : 0;
position.x = (id & 1) ? pos_right : pos_left;
position.y = (id & 2) ? -1 : 1;
position.zw = 1;
#endif
viewport_frequency_factor = calc_phosphor_viewport_frequency_factor();
// We don't alter these based on screen rotation because they're independent of screen dimensions.
float edge_norm_tx;
float edge_norm_ty;
[flatten]
switch (mask_type) {
case 0:
edge_norm_tx = grille_edge_norm_t;
break;
case 1:
edge_norm_tx = slot_edge_norm_tx;
edge_norm_ty = slot_edge_norm_ty;
break;
case 2:
edge_norm_tx = shadow_edge_norm_tx;
edge_norm_ty = shadow_edge_norm_ty;
break;
case 3:
edge_norm_tx = smallgrille_edge_norm_t;
break;
case 4:
edge_norm_tx = smallslot_edge_norm_tx;
edge_norm_ty = smallslot_edge_norm_ty;
break;
default:
edge_norm_tx = smallshadow_edge_norm_tx;
edge_norm_ty = smallshadow_edge_norm_ty;
break;
}
const float2 thickness_scaled = linearize_phosphor_thickness_param(phosphor_thickness);
const float mask_p_x = exp(-calculate_phosphor_p_value(edge_norm_tx, thickness_scaled.x, phosphor_sharpness.x));
const float mask_p_y = exp(-calculate_phosphor_p_value(edge_norm_ty, thickness_scaled.y, phosphor_sharpness.y));
mask_pq_x = float2(mask_p_x, phosphor_sharpness.x);
mask_pq_y = float2(mask_p_y, phosphor_sharpness.y);
}
void generatePhosphorMaskPS(
in float4 pos : SV_Position,
in float2 texcoord : TEXCOORD0,
in float2 viewport_frequency_factor: TEXCOORD1,
in float2 mask_pq_x : TEXCOORD2,
in float2 mask_pq_y : TEXCOORD3,
out float4 color : SV_Target
) {
[branch]
if (geom_rotation_mode == 1 || geom_rotation_mode == 3) {
texcoord = texcoord.yx;
viewport_frequency_factor = viewport_frequency_factor.yx;
}
float3 phosphor_color;
[branch]
if (mask_type == 0) {
phosphor_color = get_phosphor_intensity_grille(
texcoord,
viewport_frequency_factor,
mask_pq_x
);
}
else if (mask_type == 1) {
phosphor_color = get_phosphor_intensity_slot(
texcoord,
viewport_frequency_factor,
mask_pq_x,
mask_pq_y
);
}
else if (mask_type == 2) {
phosphor_color = get_phosphor_intensity_shadow(
texcoord,
viewport_frequency_factor,
float2(mask_pq_x.y, mask_pq_y.y)
);
}
else if (mask_type == 3) {
phosphor_color = get_phosphor_intensity_grille_small(
texcoord,
viewport_frequency_factor,
mask_pq_x
);
}
else if (mask_type == 4) {
phosphor_color = get_phosphor_intensity_slot_small(
texcoord,
viewport_frequency_factor,
mask_pq_x,
mask_pq_y
);
}
else {
phosphor_color = get_phosphor_intensity_shadow_small(
texcoord,
viewport_frequency_factor,
mask_pq_x,
mask_pq_y
);
}
color = float4(phosphor_color, 1.0);
}
void applyComputedPhosphorMaskPS(
in float4 pos : SV_Position,
in float2 texcoord : TEXCOORD0,
out float4 color : SV_Target
) {
bool use_deinterlacing_tex = enable_interlacing && (
scanline_deinterlacing_mode == 2 || scanline_deinterlacing_mode == 3
);
float3 scanline_color_dim;
[branch]
if (use_deinterlacing_tex) scanline_color_dim = tex2D(samplerDeinterlace, texcoord).rgb;
else scanline_color_dim = tex2D(samplerBeamConvergence, texcoord).rgb;
const float3 phosphor_color = tex2D(samplerPhosphorMask, texcoord).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, float3(1, 1, 1)) / 3.0;
const float3 halation_intensity_dim = halation_intensity_dim_scalar;
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_color;
color = float4(phosphor_emission_dim, 1.0);
}
#endif // _PHOSPHOR_MASK_H