mirror of
https://github.com/italicsjenga/slang-shaders.git
synced 2024-11-24 16:41:31 +11:00
1413 lines
60 KiB
Plaintext
1413 lines
60 KiB
Plaintext
#version 450
|
|
|
|
/* This pass:
|
|
* Composes the previous passes
|
|
* Does masks, spot, bezel, vignette, background image (anything else?)
|
|
*/
|
|
|
|
#define DEBUG_TEXTURE Original
|
|
//#define DEBUG_TEXTURE colortools_and_ntsc_pass
|
|
//#define DEBUG_PRINT_VALUE
|
|
//#define DEBUG_QUAD_SPLIT
|
|
//#define DEBUG_DUAL_SPLIT_X
|
|
//#define DEBUG_DUAL_SPLIT_Y
|
|
//#define DEBUG_DUAL_SPLIT_Y_GAMMA
|
|
//#define DEBUG_DUAL_SPLIT_MIRROR_X
|
|
//#define DEBUG_DUAL_SPLIT_MIRROR_X_GAMMA
|
|
//#define DEBUG_SHOW_CLIP
|
|
|
|
|
|
#include "config.inc"
|
|
|
|
#pragma stage vertex
|
|
layout(location = 0) in vec4 Position;
|
|
layout(location = 1) in vec2 TexCoord;
|
|
layout(location = 0) out vec2 vTexCoord;
|
|
layout(location = 1) out vec2 vOutputCoord;
|
|
layout(location = 2) out vec2 spot_offset;
|
|
layout(location = 3) out float vIsRotated;
|
|
//Scanlines:
|
|
layout(location = 4) out float vIsInterlaced;
|
|
layout(location = 5) out float vScanlinePeriod;
|
|
layout(location = 6) out float vScanlineAlternateOffset;
|
|
layout(location = 7) out float vMax_inLum;
|
|
//Vignette, spot:
|
|
layout(location = 8) out float vIn_aspect;
|
|
//
|
|
layout(location = 9) out float vDynamicSeed;
|
|
layout(location = 10) out float vBEZEL_INNER_ZOOM_adapted;
|
|
layout(location = 11) out float vDo_Tate;
|
|
layout(location = 12) out vec3 vDotMat_Grid_Color;
|
|
layout(location = 13) out vec4 vPG_offsets_and_size;
|
|
layout(location = 14) out vec2 vPG_freq_base_screen;
|
|
layout(location = 15) out vec2 vPG_freq_base_screen_unfloored;
|
|
layout(location = 16) out vec2 vPG_period_multiplier;
|
|
layout(location = 17) out vec2 vPG_OriginalSize_tated;
|
|
layout(location = 18) out vec2 vPG_OutputSize_tated;
|
|
layout(location = 19) out vec2 vPG_OutputCoord_tated;
|
|
layout(location = 20) out float vDotMat_Grid_Sharpness;
|
|
layout(location = 21) out float vfprintvalue;
|
|
layout(location = 22) out float vPG_mask_height;
|
|
layout(location = 23) out float vDeltaRenderOk;
|
|
layout(location = 24) out float vDo_pixelgrid_h;
|
|
layout(location = 25) out float vDo_Curvature;
|
|
layout(location = 26) out float vPG_offsets_adjust_for_stagger;
|
|
|
|
|
|
layout(location = 31) out float I_am_a_placeholder_keep_reading;
|
|
//..to remind you that location = 31 is the last accepted by vulkan
|
|
//or Retroarch crashes or nvidia starts to give Xids.
|
|
//unsure if the issue is with retroarch or with vulkan specs.
|
|
//If in short, consider to pack more datas into vec2,3,4.
|
|
//But bear in mind that exceding the limit leads to
|
|
//slowdowns.
|
|
|
|
#include "includes/functions.include.slang"
|
|
|
|
#define PI_15 4.71238898038469 //pi*1.5
|
|
|
|
|
|
vec2 get_zooms_modifier() {
|
|
// This function is meant to live in vertex shader; its purpose is to
|
|
// give the final scale factor by taking in consideration various zoom modifiers.
|
|
//It works by calculating the new 0.0 and 1.0 coords then measuring the new distance between them
|
|
|
|
// WARNING: This has to be in sync with the code, so everytime a scaling flow is modified, this needs to be updated. WARNING
|
|
// WARNING: This has to be in sync with the code, so everytime a scaling flow is modified, this needs to be updated. WARNING
|
|
// WARNING: This has to be in sync with the code, so everytime a scaling flow is modified, this needs to be updated. WARNING
|
|
|
|
vec2 co_scaled_min = vec2(0.0);
|
|
vec2 co_scaled_max = vec2(1.0);
|
|
|
|
if ( need_NO_integer_scale() ) {
|
|
co_scaled_min = get_scaled_coords_aspect(co_scaled_min, global.FinalViewportSize, vIn_aspect, bool(vIsRotated));
|
|
co_scaled_max = get_scaled_coords_aspect(co_scaled_max, global.FinalViewportSize, vIn_aspect, bool(vIsRotated));
|
|
} else {
|
|
co_scaled_min = integer_scale(co_scaled_min, vIn_aspect, bool(vIsRotated), GAME_GEOM_INT_SCALE-1.0 ) + vec2( GAME_GEOM_OFF_FIX /10000);
|
|
co_scaled_max = integer_scale(co_scaled_max, vIn_aspect, bool(vIsRotated), GAME_GEOM_INT_SCALE-1.0 ) + vec2( GAME_GEOM_OFF_FIX /10000);
|
|
}
|
|
|
|
|
|
co_scaled_min = (zoom(co_scaled_min + vec2(-GLOBAL_OFFX, -GLOBAL_OFFY), GLOBAL_ZOOM ) * DO_GLOBAL_SHZO) +
|
|
(co_scaled_min * (1-DO_GLOBAL_SHZO) );
|
|
co_scaled_max = (zoom(co_scaled_max + vec2(-GLOBAL_OFFX, -GLOBAL_OFFY), GLOBAL_ZOOM ) * DO_GLOBAL_SHZO) +
|
|
(co_scaled_max * (1-DO_GLOBAL_SHZO) );
|
|
|
|
if (DO_BEZEL == 1.0) {
|
|
co_scaled_min = zoomout_coords(co_scaled_min, -vBEZEL_INNER_ZOOM_adapted , 1.0);
|
|
co_scaled_max = zoomout_coords(co_scaled_max, -vBEZEL_INNER_ZOOM_adapted , 1.0);
|
|
}
|
|
if (DO_GAME_GEOM_OVERRIDE == 1.0) {
|
|
co_scaled_min = content_geom_override(co_scaled_min, GAME_GEOM_ASPECT, vIn_aspect, GAME_GEOM_VSHIFT, GAME_GEOM_HSHIFT, GAME_GEOM_ZOOM);
|
|
co_scaled_max = content_geom_override(co_scaled_max, GAME_GEOM_ASPECT, vIn_aspect, GAME_GEOM_VSHIFT, GAME_GEOM_HSHIFT, GAME_GEOM_ZOOM);
|
|
}
|
|
|
|
//vfprintvalue = 1/(co_scaled_max.y - co_scaled_min.y);
|
|
return 1 / vec2(co_scaled_max.x - co_scaled_min.x,
|
|
co_scaled_max.y - co_scaled_min.y );
|
|
|
|
}
|
|
|
|
void main() {
|
|
gl_Position = global.MVP * Position;
|
|
bool bIsRotated = is_rotated();
|
|
vIsRotated = float(bIsRotated);
|
|
|
|
if ( (TATE == 1.0 && bIsRotated) || TATE == 2.0)
|
|
vDo_Tate = 1.0;
|
|
else
|
|
vDo_Tate = 0.0;
|
|
//vDo_Tate = float(TATE+vIsRotated > 1.001); //<<-- reported problems with amd (?)
|
|
|
|
vIn_aspect = get_in_aspect();
|
|
|
|
//Calculate vTexcoord as fractional or integer scaled?
|
|
if ( need_NO_integer_scale() )
|
|
vTexCoord = get_scaled_coords_aspect(TexCoord,global.FinalViewportSize, vIn_aspect, bool(vIsRotated));
|
|
else
|
|
vTexCoord = integer_scale(TexCoord, vIn_aspect, bool(vIsRotated), GAME_GEOM_INT_SCALE-1.0 ) + vec2( GAME_GEOM_OFF_FIX /10000);
|
|
|
|
|
|
|
|
//if (DO_GLOBAL_SHZO >0.5)
|
|
// vTexCoord = zoom(vTexCoord + vec2(-GLOBAL_OFFX, -GLOBAL_OFFY), GLOBAL_ZOOM );
|
|
//..unbranched previous
|
|
vTexCoord = (zoom(vTexCoord + vec2(-GLOBAL_OFFX, -GLOBAL_OFFY), GLOBAL_ZOOM ) * DO_GLOBAL_SHZO) +
|
|
(vTexCoord * (1-DO_GLOBAL_SHZO) );
|
|
|
|
vOutputCoord = TexCoord ;
|
|
|
|
|
|
vec2 vFragCoord = vec2( floor(vOutputCoord.x * params.OutputSize.x),
|
|
floor(vOutputCoord.y * params.OutputSize.y));
|
|
|
|
|
|
|
|
vBEZEL_INNER_ZOOM_adapted = get_BEZEL_INNER_ZOOM() * DO_BEZEL;
|
|
|
|
|
|
//SPOT
|
|
spot_offset = offsets_from_float(S_POSITION+420.0,40);
|
|
spot_offset = spot_offset / 10.0 + vec2(0.0,1.0);
|
|
|
|
//Help scanline/pixelgrid code too:
|
|
bool bIs_Interlaced = is_interlaced();
|
|
vIsInterlaced = float(bIs_Interlaced);
|
|
|
|
//Scanline period:
|
|
vScanlinePeriod = 1.0;
|
|
vScanlineAlternateOffset = 0.0;
|
|
|
|
if (bIs_Interlaced) {
|
|
if (params.FrameCount % 2 == 0.0) vScanlineAlternateOffset = PI_15;
|
|
vScanlinePeriod = 0.5;
|
|
}
|
|
|
|
//Calculate the maximum possible brightness of the input color by taking glow,
|
|
//contrast and brightness into account. This is needed so that scanline generation
|
|
//can map the proper input range and strictly obey scanline thickness constraints.
|
|
vMax_inLum = max( 1.0, DO_CCORRECTION * apply_contrast_brightness(1.0, CONTRAST, BRIGHTNESS)) *
|
|
max( 1.0, mix(1.0, IN_GLOW_POWER, DO_CCORRECTION));
|
|
|
|
|
|
//Generate a seed that changes over time for temporal random noise
|
|
vDynamicSeed = mod(params.FrameCount, 30.0001);
|
|
|
|
|
|
//Calc dotmat grid color
|
|
vDotMat_Grid_Color = vec3(DOT_M_G_BRT);
|
|
if (DO_DOT_MATRIX + DO_CCORRECTION > 1.1) {
|
|
vDotMat_Grid_Color = color_tools(vec3(DOT_M_G_BRT), kelvin2rgb(TEMPERATURE));
|
|
//Since we modify grid brightness via a specific use parameter, explicitely multiply it by that:
|
|
vDotMat_Grid_Color *= DOT_M_G_BRT ;
|
|
}
|
|
|
|
//Pixelgrid: get mask type; .rgb contains layout, .a contains the size.
|
|
vPG_offsets_and_size = PG_get_hmask_preset() ;
|
|
#define PG_H_COUNT vPG_offsets_and_size.a
|
|
|
|
//Pixelgrid: Calc freq_base_screen
|
|
//Tate ?
|
|
vPG_OutputSize_tated = params.OutputSize.xy;
|
|
vPG_OutputCoord_tated = vOutputCoord.xy + vec2(0.00001);
|
|
vPG_OriginalSize_tated = params.OriginalSize.xy;
|
|
|
|
if (vDo_Tate == 1.0) {
|
|
vPG_OutputSize_tated = params.OutputSize.yx;
|
|
vPG_OutputCoord_tated = vOutputCoord.yx;
|
|
vPG_OriginalSize_tated = params.OriginalSize.yx;
|
|
}
|
|
|
|
// screen coords needs flooring, but unfortunately floor() does not work well in vertex shader, so calc as much as possible without floor()
|
|
vPG_period_multiplier = vec2(PIXELGRID_MUL_X, 1.0);
|
|
vPG_freq_base_screen = pi * vec2(1/PG_H_COUNT, 0.5);
|
|
|
|
//the following unfloored coords are needed by sin/cos later to understand if we are on an even or odd mask
|
|
vPG_freq_base_screen_unfloored = pi * vec2(1/PG_H_COUNT, 0.5) * (vPG_OutputCoord_tated * vPG_OutputSize_tated);
|
|
|
|
|
|
//This is needed to make sure even/odd staggering period matches betweeb floored (screen 1X)
|
|
//and unfloored coords (core or non 1x mask size).
|
|
//The issue visible when staggering the slotmask. FIXME: Not yet 100% perfect
|
|
vPG_offsets_adjust_for_stagger = 0.0;
|
|
if (PIXELGRID_SIZE_W == 0.0 || PIXELGRID_MUL_X != 1.0) {
|
|
//vec4 unfloored_offsets_fix = vec4(1.5,0.5,1.0,0.0);
|
|
vec4 unfloored_offsets_fix = vec4(1.6, 0.81, 0.54, 0.81);
|
|
//vPG_offsets_adjust_for_stagger = (pi*0.5)/PG_H_COUNT;
|
|
vPG_offsets_adjust_for_stagger = unfloored_offsets_fix[int(PG_H_COUNT)-1];
|
|
}
|
|
|
|
|
|
//scale offsets to be used by sin/cos:
|
|
vPG_offsets_and_size.rgb *= (pi / PG_H_COUNT);
|
|
|
|
//Pixelgrid: adapt period multiplier
|
|
if (vPG_period_multiplier.x < 0.0) vPG_period_multiplier.x = 1/-vPG_period_multiplier.x;
|
|
if (vPG_period_multiplier.y < 0.0) vPG_period_multiplier.y = 1/-vPG_period_multiplier.y;
|
|
|
|
|
|
//Pixelgrid: adapt mask height
|
|
vPG_mask_height = PIXELGRID_Y_MASK_HEIGHT;
|
|
if (vPG_mask_height < 0.0) vPG_mask_height = 1/-vPG_mask_height;
|
|
|
|
//Pixelgrid: do horizontal if enabled, but skip it if user doesn't want it on interlaced content.
|
|
vDo_pixelgrid_h = float(DO_PIXELGRID_H > 0.0 && !(PIXELGRID_INTR_DISABLE_Y==1.0 && vIsInterlaced==1.0));
|
|
|
|
|
|
vDo_Curvature = DO_CURVATURE * (GEOM_WARP_X + GEOM_WARP_Y);
|
|
|
|
//Dot matrix: measure the final picture size to choose a right sharpness level
|
|
//For simplicity and (my) mental health, take only y into account.
|
|
vDotMat_Grid_Sharpness = DOT_M_G_SHARP;
|
|
if (DOT_M_G_SHARP == 0.0) {
|
|
float zooms_modifier = get_zooms_modifier().y;
|
|
float dotmat_insize = global.flick_and_noise_passSize.y;
|
|
float dotmat_outsize = global.FinalViewportSize.y * zooms_modifier;
|
|
vfprintvalue = dotmat_outsize / dotmat_insize;
|
|
vDotMat_Grid_Sharpness = (dotmat_outsize / dotmat_insize);
|
|
vDotMat_Grid_Sharpness = vDotMat_Grid_Sharpness * 3.6 - 3.1;
|
|
vDotMat_Grid_Sharpness = clamp(vDotMat_Grid_Sharpness, 0.1, 20.0);
|
|
//vfprintvalue = vDotMat_Grid_Sharpness;
|
|
}
|
|
// Delta render, mandatory conditions:
|
|
vDeltaRenderOk = float( ( params.FrameCount % int(DELTA_RENDER_FORCE_REFRESH) != 0.0 ) && // - We are in a frame that is not forced for full refresh
|
|
( vIsInterlaced != 1.0 || PIXELGRID_INTR_DISABLE_Y == 1.0 || DO_PIXELGRID_H == 0.0) // - screen is not interlaced or we disabled scanlines on interlaced content
|
|
);
|
|
}
|
|
|
|
|
|
|
|
#pragma stage fragment
|
|
layout(location = 0) in vec2 vTexCoord;
|
|
layout(location = 1) in vec2 vOutputCoord;
|
|
layout(location = 2) in vec2 spot_offset;
|
|
layout(location = 3) in float vIsRotated;
|
|
layout(location = 4) in float vIsInterlaced;
|
|
layout(location = 5) in float vScanlinePeriod;
|
|
layout(location = 6) in float vScanlineAlternateOffset;
|
|
layout(location = 7) in float vMax_inLum;
|
|
layout(location = 8) in float vIn_aspect;
|
|
layout(location = 9) in float vDynamicSeed;
|
|
layout(location = 10) in float vBEZEL_INNER_ZOOM_adapted;
|
|
layout(location = 11) in float vDo_Tate;
|
|
layout(location = 12) in vec3 vDotMat_Grid_Color;
|
|
layout(location = 13) in vec4 vPG_offsets_and_size;
|
|
layout(location = 14) in vec2 vPG_freq_base_screen;
|
|
layout(location = 15) in vec2 vPG_freq_base_screen_unfloored;
|
|
layout(location = 16) in vec2 vPG_period_multiplier;
|
|
layout(location = 17) in vec2 vPG_OriginalSize_tated;
|
|
layout(location = 18) in vec2 vPG_OutputSize_tated;
|
|
layout(location = 19) in vec2 vPG_OutputCoord_tated;
|
|
layout(location = 20) in float vDotMat_Grid_Sharpness;
|
|
layout(location = 21) in float vfprintvalue;
|
|
layout(location = 22) in float vPG_mask_height;
|
|
layout(location = 23) in float vDeltaRenderOk;
|
|
layout(location = 24) in float vDo_pixelgrid_h;
|
|
layout(location = 25) in float vDo_Curvature;
|
|
layout(location = 26) in float vPG_offsets_adjust_for_stagger;
|
|
|
|
|
|
|
|
|
|
layout(location = 0) out vec4 FragColor;
|
|
|
|
|
|
layout(set = 0, binding = 1) uniform sampler2D backdrop;
|
|
layout(set = 0, binding = 2) uniform sampler2D bloom_pass_final;
|
|
layout(set = 0, binding = 3) uniform sampler2D reflected_blurred_pass;
|
|
layout(set = 0, binding = 4) uniform sampler2D ambi_temporal_pass;
|
|
layout(set = 0, binding = 5) uniform sampler2D avglum_pass;
|
|
layout(set = 0, binding = 6) uniform sampler2D monitor_body_straight;
|
|
layout(set = 0, binding = 7) uniform sampler2D monitor_body_curved;
|
|
layout(set = 0, binding = 8) uniform sampler2D bg_under;
|
|
layout(set = 0, binding = 9) uniform sampler2D bg_over;
|
|
//layout(set = 0, binding = 10) uniform sampler2D shift_and_bleed_pass;
|
|
layout(set = 0, binding = 11) uniform sampler2D in_glow_pass;
|
|
layout(set = 0, binding = 12) uniform sampler2D halo_pass;
|
|
layout(set = 0, binding = 13) uniform sampler2D final_passFeedback;
|
|
layout(set = 0, binding = 15) uniform sampler2D DEBUG_TEXTURE;
|
|
|
|
|
|
|
|
#define RECT01 vec4(0.0, 0.0, 1.0, 1.0)
|
|
#define HALF_PI 1.5707963267949
|
|
#define QUARTER_PI 0.785398163397448
|
|
|
|
#include "includes/functions.include.slang"
|
|
|
|
|
|
vec2 vOutputCoord_adapted;
|
|
vec2 get_vOutputCoord_adapted() {
|
|
return vOutputCoord_adapted;
|
|
}
|
|
|
|
|
|
vec3 fn_pixel_nightify(vec3 color_in, float strength, vec3 ambilight) {
|
|
if (strength == 0.0) return color_in;
|
|
|
|
color_in = clamp(color_in, 0.0, 1.0);
|
|
|
|
vec3 color_hsv_in = rgb2hsv(color_in);
|
|
|
|
//If there is an ambientlight, then the strangth has to be lowered:
|
|
vec3 strength_vec3 = vec3(strength - ambilight);
|
|
|
|
strength_vec3 = scale_to_range_vec3(strength_vec3, 0.0, 1.0);
|
|
|
|
vec3 color_hsv_min = color_hsv_in;
|
|
color_hsv_min.yz = scale_to_range_vec2(color_hsv_min.yz, -0.1, 0.1);
|
|
|
|
vec3 color_rgb_min = hsv2rgb(color_hsv_min);
|
|
|
|
|
|
vec3 pixel_out = mix(color_in, color_rgb_min, strength_vec3);
|
|
|
|
//It could make sense to higher the contrast when ambient light hits the picture,
|
|
//Maybe this could be used independently on the nightify feature?
|
|
/*vec3 new_contrast = ambilight;
|
|
vec3 new_brightness = ambilight*0.0;
|
|
pixel_out = scale_to_range_vec3(pixel_out, -new_contrast, 1+new_contrast) + new_brightness;*/
|
|
|
|
return pixel_out;
|
|
}
|
|
|
|
|
|
|
|
float morph_shape_full(float shape, float power, float steep ) {
|
|
float lum_scaled = power;
|
|
float l = lum_scaled; // (already clamped)
|
|
if (lum_scaled <= 0.5+steep) {
|
|
float l1 = pow(l, 4) * 15;
|
|
shape = pow(shape, 1/sqrt(l1));
|
|
} else {
|
|
float l2 = (l-0.5)*2.0;
|
|
shape = mix(shape, 1.0, l2); //shape = shape * (1-l2) + l2;
|
|
}
|
|
return shape;
|
|
}
|
|
|
|
vec3 morph_shape_full_vec3(vec3 shape, vec3 l, float steep, float gamma ) {
|
|
vec3 l_pow = pow(l,vec3(gamma));
|
|
vec3 l2 = min(l_pow * 16, 1+steep);
|
|
vec3 s1 = pow(shape, 1/sqrt(l2));
|
|
vec3 s2 = (1-s1) * l_pow;
|
|
return s1+(s2/(1+steep));
|
|
|
|
}
|
|
|
|
/*vec3 morph_shape_full_vec3(vec3 shape, vec3 l, float steep ) {
|
|
vec3 l_pow = pow(l,vec3(4.2));
|
|
vec3 l2 = l_pow * 16;
|
|
l2 = clamp(l2, 0.0, 1+steep);
|
|
vec3 s1 = pow(shape, 1/sqrt(l2));
|
|
vec3 s2 = (1-s1) * l_pow;
|
|
return s1+(s2/(l+steep));
|
|
}*/
|
|
|
|
/*vec3 morph_shape_full_vec3(vec3 shape, vec3 l, float steep ) {
|
|
vec3 l_pow = pow(l,vec3(4.1));
|
|
vec3 l2 = l_pow * 16;
|
|
l2 = clamp(l2, 0.0,1.0);
|
|
vec3 s1 = pow(shape, 1/sqrt(l2));
|
|
vec3 s2 = (1-s1) * l_pow * (l*l);
|
|
return s1+(s2/(1+steep));
|
|
}*/
|
|
|
|
vec3 morph_shape(vec3 shape, vec3 power, float steep, float gamma ) {
|
|
return morph_shape_full_vec3(shape, power, steep, gamma);
|
|
|
|
return vec3(
|
|
morph_shape_full(shape.x, power.x, steep),
|
|
morph_shape_full(shape.y, power.y, steep),
|
|
morph_shape_full(shape.z, power.z, steep)
|
|
);
|
|
|
|
|
|
//Use this to compare different methods
|
|
if (params.FrameCount % 2 == 0.0)
|
|
return morph_shape_full_vec3(shape, power, steep, gamma);
|
|
else
|
|
return vec3(
|
|
morph_shape_full(shape.x, power.x, steep),
|
|
morph_shape_full(shape.y, power.y, steep),
|
|
morph_shape_full(shape.z, power.z, steep)
|
|
);
|
|
}
|
|
|
|
/*float morph_shape(float shape, float power, float steep ) {
|
|
return morph_shape_full(shape, power, steep);
|
|
}*/
|
|
|
|
|
|
|
|
|
|
vec3 downsample( sampler2D tex, vec2 uv, vec4 sourcesize, float sharpness_add ) {
|
|
vec2 sharpness = vec2(1.0)+sharpness_add;
|
|
vec2 scale = sourcesize.xy * sharpness;
|
|
|
|
vec2 iuv = floor(uv * scale);
|
|
vec2 bottomleft = iuv;
|
|
vec2 bottomright = ( iuv + vec2(1.0,0.0)) / scale;
|
|
vec2 topleft = ( iuv + vec2(0.0,1.0)) / scale;
|
|
vec2 topright = ( iuv + vec2(1.0,1.0)) / scale;
|
|
|
|
bottomleft /= scale;
|
|
|
|
vec2 dist = (uv - bottomleft)*scale;
|
|
|
|
vec3 bl = texture(tex, bottomleft).xyz ;
|
|
vec3 br = texture(tex, bottomright).xyz ;
|
|
vec3 tl = texture(tex, topleft).xyz ;
|
|
vec3 tr = texture(tex, topright).xyz ;
|
|
|
|
vec3 tA = mix( bl, br, dist.x );
|
|
vec3 tB = mix( tl, tr, dist.x );
|
|
return mix( tA, tB, dist.y );
|
|
|
|
}
|
|
|
|
vec3 downsample_x( sampler2D tex, vec2 uv, vec4 sourcesize, float sharpness_add ) {
|
|
|
|
float sharpness = 1.0+sharpness_add;
|
|
|
|
vec2 scale = vec2(sourcesize.x * sharpness, 1.0);
|
|
|
|
vec2 iuv = vec2( floor(uv.x * scale.x), uv.y);
|
|
|
|
vec2 bottomleft = iuv;
|
|
vec2 bottomright = ( iuv + vec2(1.0,0.0)) / scale;
|
|
|
|
bottomleft /= scale;
|
|
|
|
vec2 dist = (uv - bottomleft)*scale;
|
|
|
|
vec3 bl = texture(tex, bottomleft).xyz ;
|
|
vec3 br = texture(tex, bottomright).xyz ;
|
|
|
|
return mix( bl, br, dist.x );
|
|
|
|
}
|
|
|
|
vec3 smoothstep_fast(vec3 edge0, vec3 edge1, vec3 x) {
|
|
|
|
vec3 t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
|
|
//return cos(t * 3.14159265358979323846) * -0.5 + 0.5;
|
|
return mix(vec3(0.0), vec3(1.0), t);
|
|
}
|
|
|
|
vec4 fn_pixel_grid(vec2 in_coords, vec3 pixel_in, float min_inLum, float max_inLum ) {
|
|
/*
|
|
This would provide an additional method to alter scanlines "inertia", but has a cost of 4fps
|
|
when using smoothstep_fast and 13fps(!) when using standard smoothstep.
|
|
It could replace glow_x for tight blurs.
|
|
*/
|
|
|
|
////vec3 lum_h = downsample(in_glow_pass, in_coords, global.flick_and_noise_passSize, 0.0).rgb;
|
|
//vec3 lum_h = downsample_x(in_glow_pass, in_coords, global.flick_and_noise_passSize, 0.0).rgb;
|
|
//lum_h *= smoothstep_fast( vec3(SERVICE1-0.1) ,vec3(SERVICE1), lum_h);
|
|
//pixel_in = max(pixel_in, lum_h);
|
|
|
|
|
|
//Scanlines: compute each phosphor height according to input luminance, puttin it here helpas parallelism a bit.
|
|
float extragain_h = max(PIXELGRID_MAX_H, 1.0); //Handle
|
|
vec3 phosphor_height = map_range(pixel_in*extragain_h, min_inLum, max_inLum, PIXELGRID_MIN_H, clamp(PIXELGRID_MAX_H, PIXELGRID_MIN_H, 1.0));
|
|
|
|
|
|
//Tate ? (other outputsize,originalsize and outputcoords "tated" in vertex shader.)
|
|
if (vDo_Tate == 1.0) in_coords.xy = in_coords.yx;
|
|
//in_coords = in_coords - global.flick_and_noise_passSize.zw*0.5; //<-- needed if for whatever reason you want to draw scanlines and vgaps in first_pass
|
|
|
|
//Get preset masks on ".rgb" and mask size on ".a" , mask and period for every phosphor done in vertex shader.
|
|
float PG_h_count = vPG_offsets_and_size.a;
|
|
vec3 PG_offsets = vPG_offsets_and_size.rgb;
|
|
|
|
vec2 freq_base_core = pi * in_coords * vPG_OriginalSize_tated ;
|
|
|
|
//Screen coords:
|
|
//The following unfloored coords are needed by sin/cos later to understand if we are on an even or odd mask
|
|
//vec2 freq_base_screen_unfloored = pi * vec2(1/PG_h_count, 0.5) * (vPG_OutputCoord_tated * vPG_OutputSize_tated);
|
|
vec2 freq_base_screen_unfloored = vPG_freq_base_screen_unfloored; //<-- missing muls because done to vertex shader.
|
|
|
|
//Screen coords needs flooring, but we need to disable flooring if using non 1x screen coords
|
|
// y screen coords appear to work just fine even if we always floor them,
|
|
// you can use "float(fract(1/vPG_mask_height) < 0.01"as the y mix parameter instead of 1.0 if in doubt.
|
|
#define SCREEN_Y_MIX 1.0 //<-- Always floor screen y coords (seems to work good everytime)
|
|
//#define SCREEN_Y_MIX float(fract(1/vPG_mask_height) < 0.01) //<-- only floor screen y coords when non integer multipliers
|
|
vec2 freq_base_screen = mix( vPG_freq_base_screen_unfloored,
|
|
vPG_freq_base_screen * floor(vPG_OutputCoord_tated * vPG_OutputSize_tated ),
|
|
vec2( float(PIXELGRID_MUL_X==1.0), SCREEN_Y_MIX )
|
|
);
|
|
|
|
//Switch between core and screen sizes (x and y)
|
|
vec2 freq_base = mix( freq_base_core, freq_base_screen, vec2(PIXELGRID_SIZE_W, 0.0));
|
|
vec2 freq_base_unfloored = mix( freq_base_core, freq_base_screen_unfloored, vec2(PIXELGRID_SIZE_W, 0.0));
|
|
|
|
|
|
//Apply multiplier:
|
|
freq_base /= vPG_period_multiplier;
|
|
freq_base_unfloored /= vPG_period_multiplier;
|
|
|
|
vec3 freq_rgb = vec3(freq_base.x) - PG_offsets;
|
|
|
|
|
|
//The following is needed by scanlines and vertical mask
|
|
//sin() tell use if the current cell position is odd or even:
|
|
|
|
//FIXME: is_even should be a single phosphor property.
|
|
//If i manage to find a way to do it, i could use
|
|
//decon_stagger.r,g,b to stagger single phosphors.
|
|
//And that way, i could blend staggered triads together
|
|
//without "cuts" between them.
|
|
//FIXME FIXME: Unsurer if the previous statement is still valid.
|
|
|
|
//Moving it up there provides a small speed up due to
|
|
//increased parallalism, I guess.
|
|
float sin_check_offset = sin(freq_base_unfloored.x + vPG_offsets_adjust_for_stagger);
|
|
float is_even = step(sin_check_offset, 0.0);
|
|
|
|
|
|
//Scanlines and fake slotmask =========================================================================
|
|
vec3 rgb_h = vec3(1.0);
|
|
float dedot_mix_inverted = 1.0;
|
|
|
|
//Pixelgrid: do horizontal if enabled, but skip it if user doesn't want it on interlaced content.
|
|
//if (DO_PIXELGRID_H > 0.0 && !(PIXELGRID_INTR_DISABLE_Y==1.0 && vIsInterlaced==1.0)) { //103.1
|
|
if (vDo_pixelgrid_h > 0.5) {
|
|
|
|
//Since we emulate scanlines, take into account current scanline phase:
|
|
float interlacing_adapt_period = vScanlinePeriod;
|
|
|
|
//We can offset trias to emulate fake core level slotmask by applying the optional offset to emulate the slotmask on even cells
|
|
float triad_stagger_offset = is_even * pi * (PIXELGRID_OFFSET_CORE);
|
|
//get 3 sines with applied the optional staggered offset for slotmask, and single phosphors staggering for y deconvergence.
|
|
vec3 decon_stagger = vec3( PIXELGRID_DECON_R_H, PIXELGRID_DECON_G_H, PIXELGRID_DECON_B_H) ; //* (pixel_in/max_inLum) ;
|
|
|
|
#ifdef MOIRE_MITIGATION
|
|
#define IS_EVEN float(sin(vPG_freq_base_screen_unfloored.x * vPG_offsets_and_size.a) > 0.5)
|
|
float moire_mitigation_sweet_spot = IS_EVEN * MOIRE_SWEET_SPOT;
|
|
vec3 rgb_h_sin = sin( (freq_base_core.y * interlacing_adapt_period) + triad_stagger_offset - decon_stagger + vScanlineAlternateOffset + moire_mitigation_sweet_spot );
|
|
#else
|
|
vec3 rgb_h_sin = sin( (freq_base_core.y * interlacing_adapt_period) + triad_stagger_offset - decon_stagger + vScanlineAlternateOffset);
|
|
#endif
|
|
|
|
//make it positve with doubled frequency:
|
|
rgb_h_sin = (rgb_h_sin * rgb_h_sin);
|
|
|
|
//Compute dedot mix here for use in h mask and vmask2 later
|
|
dedot_mix_inverted = (1-rgb_h_sin.r) * PIXELGRID_H_DEDOT ;
|
|
dedot_mix_inverted = dedot_mix_inverted * (max(max(pixel_in.r, pixel_in.g), pixel_in.b)/max_inLum);
|
|
//dedot_mix_inverted = dedot_mix_inverted * dot(pixel_in, vec3(0.33333) )/max_inLum;
|
|
dedot_mix_inverted = 1-dedot_mix_inverted;
|
|
|
|
// Compute each phosphor height according to input luminance,
|
|
// Moved outside the branch to help parallelism a bit
|
|
//float extragain_h = max(PIXELGRID_MAX_H, 1.0); //Handle
|
|
//vec3 phosphor_height = map_range(pixel_in*extragain_h, min_inLum, max_inLum, PIXELGRID_MIN_H, clamp(PIXELGRID_MAX_H, PIXELGRID_MIN_H, 1.0));
|
|
|
|
|
|
//Finally get 3 sines out of the previous one by applying height modifiers
|
|
rgb_h = morph_shape(rgb_h_sin, phosphor_height, PIXELGRID_NO_INTERBLEED_H, PIXELGRID_GAMMA_H);
|
|
|
|
rgb_h = mix(vec3(1.0), rgb_h, DO_PIXELGRID_H);
|
|
rgb_h = clamp(rgb_h, 0.0,1.0);
|
|
|
|
|
|
/* float punch_y = sin(freq_base_core.y);
|
|
punch_y = pow(punch_y,32.0);
|
|
float punch_x = sin(freq_base.x);
|
|
punch_x = pow(punch_x,32.0);
|
|
rgb_h +=vec3(punch_x * punch_y ) * SERVICE1 * 50;
|
|
//return vec4(punch_x * punch_y );*/
|
|
|
|
//Scanline "mangler"
|
|
//float t = 0.2;
|
|
//t = - (-0.6 * 0.3 - 0.12);
|
|
//t = 0.3;
|
|
//float t = SERVICE1;
|
|
//rgb_h = smoothstep(t, t+0.2, phosphor_height) * rgb_h;
|
|
|
|
//Mask Gamma "out"
|
|
//rgb_h = pow(rgb_h, vec3(PIXELGRID_GAMMA_OUT_H));
|
|
//if (phosphor_height.r < 0.1) rgb_h = vec3(0.0);
|
|
}
|
|
|
|
|
|
|
|
//Horizontal Triad Mask: ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
vec3 rgb_w = vec3(1.0);
|
|
vec3 rgb_grille = vec3(0.0);
|
|
|
|
if (DO_PIXELGRID_W > 0.0) { //4%
|
|
//get 3 sines in black/white
|
|
rgb_w = cos(freq_rgb);
|
|
//make them always positive and double frequency:
|
|
rgb_w = (rgb_w * rgb_w);
|
|
//"export" naked vertical grille
|
|
rgb_grille = (rgb_w * rgb_w) * (rgb_w * rgb_w);
|
|
//Scale Max width according to pre-gains:
|
|
vec3 phosphor_width = map_range(pixel_in, min_inLum, max_inLum, PIXELGRID_MIN_W, PIXELGRID_MAX_W);
|
|
//Get final phosphor width:
|
|
rgb_w = morph_shape(rgb_w, phosphor_width, PIXELGRID_NO_INTERBLEED_W, PIXELGRID_GAMMA_W); //2%
|
|
|
|
//Dedot rgb mask between h mask.
|
|
//This work only when there are dots and have the countereffect when there are not.
|
|
//This happen because flattening the rgb mask will make unflattened rgb triads
|
|
//more evident, so this has to be used only when needed.
|
|
//rgb_w = mix(rgb_h, rgb_w, dedot_mix_inverted);
|
|
rgb_w = mix(phosphor_width, rgb_w, dedot_mix_inverted);
|
|
|
|
//lower strength via user parameter?
|
|
rgb_w = mix(vec3(1.0), rgb_w, DO_PIXELGRID_W);
|
|
}
|
|
|
|
//1%
|
|
//Mask strength modifiers for hmask, performs way better outside the main if then ^^ up there ^^
|
|
|
|
/*
|
|
vec3 rgb_w_weakness = (pixel_in * PIXELGRID_HMASK_NO_BRIGHT) ;
|
|
rgb_w_weakness = clamp(rgb_w_weakness,0.0,1.0);
|
|
|
|
rgb_w = mix( rgb_w, vec3(1.0), rgb_w_weakness);
|
|
rgb_w = mix( vec3(1.0), rgb_w, DO_PIXELGRID_W);
|
|
//The following should be equivalent to ^ ^ but it seems slower.
|
|
// rgb_w = mix( vec3(1.0), rgb_w, DO_PIXELGRID_W * (1-rgb_w_weakness) );
|
|
*/
|
|
|
|
//Vertical mask .:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.
|
|
// can be used for slotmask,aperturegrille, not scanlines.
|
|
//...well technically yes, but they would have a boxed shape.
|
|
vec3 darklines_mask=vec3(1.0);
|
|
float PG_spark;
|
|
vec3 pixel_in_unsparkled = pixel_in;
|
|
if (PIXELGRID_Y_MASK > 0.0) {
|
|
//Switch reference coords according to user pref:
|
|
float freq_cell_y = mix(freq_base_core.y, freq_base_screen.y, PIXELGRID_Y_MASK_COORDS);
|
|
|
|
//mask phosphors heigth
|
|
#define PH_MASK_HEIGHT (freq_cell_y * vPG_mask_height)
|
|
//phosphors mask stagger
|
|
#define PH_MASH_OFFSET (PIXELGRID_Y_MASK_OFFSET * pi * 0.5 )
|
|
|
|
//Use abs(cos) (later 1-abs(cos) to get horizontal gaps, use pow to make them steep.
|
|
//Stagger them by PH_MASH_OFFSET on even triads
|
|
|
|
float mask_shape = abs(cos( PIXELGRID_Y_MASK_SHIFT*pi + PH_MASK_HEIGHT + (PH_MASH_OFFSET * is_even)) ) ;
|
|
mask_shape = pow(mask_shape, PIXELGRID_Y_MASK_STEEP);
|
|
|
|
// We can make the stagger dark line less pronunced
|
|
// on its sides so that lateral staggered color
|
|
// can bleed over.
|
|
// but you really need to have God eyes to notice any improvement, if any imho.
|
|
// Also, this would probably alter the colors (?)
|
|
// So keep it disabled.
|
|
// float Bleed_force = SERVICE1; //0.02 seems a realistic value.
|
|
// float side_visibility = pow(pow(sin_check_offset,2.0),Bleed_force);
|
|
// mask_shape = mask_shape* side_visibility;
|
|
// return vec4(mask_shape);
|
|
|
|
//Get per channel input luminance by clamping pixel_in and applying hmask
|
|
vec3 lum=clamp(pixel_in * rgb_w, 0.0, 1.0);
|
|
//Adapt the luminance effect via input parameter
|
|
vec3 lum_adapted = (1-lum*PIXELGRID_Y_MASK_ON_WHITE);
|
|
//Calc final mask visibility as the minimum between tha configured visibility and luminosity effect
|
|
vec3 V=min( lum_adapted, vec3(PIXELGRID_Y_MASK) );
|
|
//apply user visibility modifier to the mask
|
|
darklines_mask = 1-(mask_shape*V);
|
|
//Try to get a more brillant/sparkling look by highering the darklines
|
|
//effect (sub) and pushing the source color, useful for slotmasks.
|
|
PG_spark = PIXELGRID_Y_SPARK * PIXELGRID_Y_MASK;
|
|
darklines_mask -= PG_spark;
|
|
|
|
pixel_in *= 1 + ((PG_spark*4) * dedot_mix_inverted);
|
|
|
|
//Dedot darklines mask:
|
|
darklines_mask = mix(vec3(1.0), darklines_mask, dedot_mix_inverted );
|
|
}
|
|
|
|
|
|
//Finally put H mask, Scanlines and darklines masks togheter:
|
|
//vec3 mask = rgb_w * rgb_h * darklines_mask;
|
|
vec3 mask = min( (rgb_w * rgb_h), darklines_mask * rgb_h); //<- this way it does not interfree with scanline height!
|
|
//FIXME: test with coore or fuzzy coords how it works when using variable width phosphors, maybe
|
|
//it would need to be: vec3 mask = min((rgb_w * rgb_h), darklines_mask * rgb_h * rgb_w); =
|
|
|
|
|
|
//mask posterization experiment:
|
|
//mask = floor(mask*16.0)/16.0;
|
|
//mask = pow(mask, vec3(0.5));
|
|
|
|
//Apply Overmask:
|
|
mask = PIXELGRID_OVERMASK * (mask - 0.5) + 0.5;
|
|
pixel_in = mix(pixel_in, pixel_in * PIXELGRID_OVERMASK, PIXELGRID_OVERMASK);
|
|
|
|
//Apply the mask to pixel_in and clamp the minimum to the unexcited grille.
|
|
vec3 mask_and_grille = max(mask * pixel_in, rgb_grille * PIXELGRID_BASAL_GRID*0.0025);
|
|
|
|
//return it with alpha channel containing the mask itself, so that halo can selectively light it.
|
|
return vec4( mask_and_grille , dot(rgb_h, vec3(0.3333)));
|
|
}
|
|
|
|
|
|
vec4 fn_pixel_dotmat(vec2 coords, vec3 pixel_in) {
|
|
|
|
//Base angle, depends on original size:
|
|
vec2 angle_base = coords * pi * params.OriginalSize.xy; //FIXME here we want .yy to make integer scaling
|
|
|
|
//Zoom to debug:
|
|
angle_base/=DOT_M_MULTIPLY;
|
|
|
|
//Set the grid sharpness from vertex shader:
|
|
float s_grid = vDotMat_Grid_Sharpness;
|
|
|
|
|
|
//Generate bw grid:
|
|
vec2 grid = cos(angle_base);
|
|
grid = grid * grid;
|
|
grid = pow(grid, vec2(s_grid));
|
|
grid = 1 - grid;
|
|
|
|
|
|
//Combine gridx and gridy into a single one
|
|
//float fgrid = min(grid.x, grid.y);
|
|
float fgrid = grid.x* grid.y;
|
|
|
|
//Modify strength for "paint on background" option
|
|
|
|
//Smoothly fadeout grid on background
|
|
//The fadeout size:
|
|
float fade_size = 0.3;
|
|
|
|
//We invert the smooth logic if threshold is negative. this allow to fadeout
|
|
//the grid on bright or dark backgrounds.
|
|
float lum_scaled = dot(pixel_in, vec3(0.33333));
|
|
lum_scaled = clamp(lum_scaled, 0.0, 1.0);
|
|
lum_scaled = mix_step(1-lum_scaled, lum_scaled, float(DOT_M_G_TRESH > 0.0));
|
|
|
|
float smooth_threshold = abs(DOT_M_G_TRESH);
|
|
vec2 smooth_range = vec2(smooth_threshold, smooth_threshold+fade_size);
|
|
float grid_smoothstep = 1- smoothstep(smooth_range.x, smooth_range.y, lum_scaled);
|
|
|
|
//Adapt grid strength
|
|
float grid_str = DOT_M_G_STR * grid_smoothstep;
|
|
//Apply strength modifier
|
|
float fgrid_adpt = mix(1.0, fgrid, grid_str);
|
|
|
|
//Output is the mix of the gap and the rgb masked pixel in.
|
|
//In the alpha channel we put the grid because it could be selectiveli brightened by halo
|
|
return vec4(
|
|
mix(vDotMat_Grid_Color, pixel_in, fgrid_adpt),
|
|
fgrid_adpt
|
|
);
|
|
|
|
}
|
|
|
|
float get_clamped_white_reference(vec3 pixel_in){
|
|
float white_reference = max(max(pixel_in.r,pixel_in.g),pixel_in.b);
|
|
// white_reference = min(white_reference,1.0); //Clamp here for both vmask and darklines.
|
|
return white_reference;
|
|
}
|
|
|
|
vec3 pixel_backdrop_image() {
|
|
vec2 backdrop_offset=vec2(BACKDROP_OFFX,BACKDROP_OFFY);
|
|
|
|
vec2 backdrop_tex_size = textureSize(backdrop, 0);
|
|
float backdrop_lod = log2(backdrop_tex_size.y / global.FinalViewportSize.y);
|
|
|
|
vec2 backdrop_coords = get_scaled_coords_aspect(
|
|
vOutputCoord+backdrop_offset,
|
|
global.FinalViewportSize,
|
|
backdrop_tex_size.x/backdrop_tex_size.y,
|
|
bool(vIsRotated));
|
|
|
|
backdrop_coords=zoom(backdrop_coords, BACKDROP_ZOOM);
|
|
return textureLod(backdrop, backdrop_coords, backdrop_lod).rgb;
|
|
}
|
|
|
|
|
|
vec4 textureLod_wrap(sampler2D tex, vec2 co, float lod, float wrap_mode) {
|
|
#ifdef ALLOW_BG_IMAGE_TEXTURE_WRAP_IN_SHADER
|
|
/*
|
|
// Mirrored repeat, once, useless since is done by default
|
|
if (co.x > 1.0 || co.x < 0.0)
|
|
co.x = 1- mod(co.x, 1.0);
|
|
if (co.y > 1.0 || co.y < 0.0)
|
|
co.y = 1- mod(co.y, 1.0);
|
|
*/
|
|
|
|
if (wrap_mode == 1.0) {
|
|
//Clamp to border, black.
|
|
bool bOutside = (co.x < 0.0 || co.x > 1.0 || co.y < 0.0 || co.y > 1.0 ) ;
|
|
if (bOutside) return vec4(0.0,0.0,0.0,1.0);
|
|
} else if (wrap_mode == 2.0) {
|
|
//Clamp to edge:
|
|
co = clamp(co, 0.00, 1.0);
|
|
} else if (wrap_mode == 3.0) {
|
|
//Repeat no mirror:
|
|
co = mod(co, 1.0);
|
|
}
|
|
#endif
|
|
return textureLod(tex, co, lod);
|
|
}
|
|
|
|
|
|
float pixel_blank_alternate(float strength) {
|
|
/*
|
|
vec3 pixel_strobe = pixel_out.rgb * mod(params.FrameCount, 2);
|
|
float max_c = max(pixel_out.r, max(pixel_out.g, pixel_out.b));
|
|
float max_c_adpt = 1 - (max_c * 0.5);
|
|
pixel_out.rgb = mix (pixel_out.rgb, pixel_strobe, ALT_BLANK_STRENGTH * max_c_adpt ) ;
|
|
*/
|
|
|
|
// Emulate the low crt persistance by only drawing odd/even lines
|
|
// on odd/even frames
|
|
|
|
|
|
|
|
float line = vTexCoord.y * params.OutputSize.y;
|
|
float l_period_half = ALT_BLANK_PERIOD / 2;
|
|
|
|
|
|
//Use another alg that affects less dark colors for negative strength values.
|
|
/* if (strength < 0.0) {
|
|
strength *= -1;
|
|
float max_c = max(source.r, max(source.g, source.b));
|
|
float max_c_adpt = 1 - (max_c * 0.5);
|
|
strength *= max_c_adpt;
|
|
}*/
|
|
|
|
//If the frame has been completely blanked, rendered will be 0.0
|
|
//rendered will be returned and can be used to skip
|
|
float rendered;
|
|
|
|
if (mod(float(params.FrameCount),2.0 ) == 1) {
|
|
if (mod(line,ALT_BLANK_PERIOD) > l_period_half) {
|
|
return strength;
|
|
}
|
|
} else {
|
|
if (mod(line,ALT_BLANK_PERIOD) <= l_period_half) {
|
|
return strength;
|
|
}
|
|
}
|
|
return 1.0;
|
|
}
|
|
|
|
|
|
vec3 bezel_color(float lum) {
|
|
//Colorize bezel frame
|
|
vec3 col = vec3(BEZEL_R,BEZEL_G,BEZEL_B) + lum;
|
|
float con_max = 0.5 * BEZEL_CON + 0.5;
|
|
col = scale_to_range_vec3(col, -con_max+1, con_max);
|
|
return clamp(col,0.0,1.0);
|
|
}
|
|
|
|
|
|
float fuzzyrect(vec2 uv, vec2 size, float radius, float blur) {
|
|
vec2 hSize = size / 2.0 - radius;
|
|
float d = length(max(abs(uv - vec2(0.5)),hSize)-hSize);
|
|
return smoothstep(-radius-blur, -radius+blur, -d);
|
|
}
|
|
|
|
float create_ambi_colorize_shade(vec2 co) {
|
|
float blur = AMBI_OVER_BEZEL_SIZE;
|
|
vec2 size = vec2(1.0, 2 - AMBI_OVER_BEZEL_AR_CORRECTION)-blur;
|
|
float radius = 0.0;
|
|
|
|
return 1 - min ( fuzzyrect(co, size, radius, blur) * 2, 1.0);
|
|
}
|
|
|
|
|
|
|
|
vec2 get_scaled_coords_for_bezel() {
|
|
//This function is here because compiler gets mad if i calc coords_for_bezel
|
|
//outside the main branch "if DO_BEZEL then compose_bezel_over"
|
|
//performances falls down for no apparent reason.
|
|
//But still, i need to access it twice in the code.
|
|
//So this is a function that ensures me that i always calc it the same way.
|
|
|
|
vec2 co = vTexCoord;
|
|
|
|
co = zoomout_coords(co, -BEZEL_FRAME_ZOOM, 1.0);
|
|
co.y = zoom1D(co.y, BEZEL_ASPECT_CORRECTION);
|
|
|
|
if (DO_TILT == 1.0)
|
|
return tilt(co, vIsRotated, vec2(TILT_X, TILT_Y) * TILT_BEZEL_K);
|
|
else
|
|
return co;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
vec4 fn_pixel_fgbg_image(sampler2D smp) {
|
|
vec2 fg_image_offset=vec2(BG_IMAGE_OFFX,BG_IMAGE_OFFY);
|
|
vec2 tex_size = textureSize(smp, 0); // * BG_ZOOM;
|
|
float bg_over_lod = log2(tex_size.y / global.FinalViewportSize.y);
|
|
|
|
if (BG_IMAGE_ROTATION > 0.0 || ( BG_IMAGE_ROTATION < 0.0 && bool(vIsRotated) ) ) tex_size.xy = tex_size.yx;
|
|
|
|
vec2 back_coords = get_scaled_coords_aspect(vOutputCoord + fg_image_offset, global.FinalViewportSize, tex_size.x/tex_size.y, bool(vIsRotated));
|
|
|
|
if (BG_IMAGE_ROTATION < 0.0 && bool(vIsRotated) ) {
|
|
//handle automatic rotation of bg image for rotated games
|
|
back_coords.xy = back_coords.yx;
|
|
back_coords.y = 1 - back_coords.y;
|
|
} else if (BG_IMAGE_ROTATION > 0.0) {
|
|
//rotate as user prefs
|
|
back_coords.xy = back_coords.yx;
|
|
if (BG_IMAGE_ROTATION == 1.0) back_coords.y = 1 - back_coords.y;
|
|
if (BG_IMAGE_ROTATION == 2.0) back_coords.x = 1 - back_coords.x;
|
|
}
|
|
back_coords=zoom(back_coords, BG_IMAGE_ZOOM);
|
|
vec4 pixel_bgover = textureLod_wrap(smp, back_coords, bg_over_lod, BG_IMAGE_WRAP_MODE);
|
|
return pixel_bgover;
|
|
}
|
|
|
|
vec3 light_over_image(vec3 light, vec3 image, float black_illumination) {
|
|
//Simulates illumination.
|
|
//It works by adding the light on the image.
|
|
//It will add less light on dark colors (
|
|
|
|
//mitigate clipping by lowering light on bright images:
|
|
light = light * (1- (max(image.r,image.g),image.b) ) ;
|
|
|
|
|
|
vec3 light_on_black = black_illumination * light;
|
|
vec3 modulated_on_black = image.rgb + ( (light - 0.0 ) * image.rgb ) + light_on_black;
|
|
|
|
return modulated_on_black;
|
|
}
|
|
|
|
vec3 ambi_blend_image(vec4 image, vec3 ambi, float blend_mode) {
|
|
//mix or add ambient light with image, also allow force colorization in add mode.
|
|
if (DO_AMBILIGHT == 0.0) return image.rgb;
|
|
|
|
// Fake a transparent image when force colorization is requested
|
|
// So that we can use the same code used for alpha blend.
|
|
// Also multiply AMBI_BG_IMAGE_FORCE * AMBI_BG_IMAGE_BLEND_MODE to skip
|
|
// force colorization when mode blend mode is not "ADD".
|
|
float image_alpha_adapted = image.a - (AMBI_BG_IMAGE_FORCE * AMBI_BG_IMAGE_BLEND_MODE);
|
|
|
|
if (AMBI_BG_IMAGE_BLEND_MODE == 0.0) {
|
|
image.rgb = mix(ambi.rgb, image.rgb, image_alpha_adapted);
|
|
} else {
|
|
//image.rgb = image.rgb + (ambi.rgb * (1 - image_alpha_adapted));
|
|
/*float light_on_black = 0.3;
|
|
vec3 ambi_alpha_masked = ambi.rgb * (1 - image_alpha_adapted);
|
|
vec3 ambi_on_black = light_on_black * ambi_alpha_masked;
|
|
|
|
image.rgb = image.rgb + ((ambi_alpha_masked-ambi_on_black) * image.rgb) + ambi_on_black;*/
|
|
|
|
float black_illumination = 0.5;
|
|
vec3 ambi_alpha_masked = ambi.rgb * (1 - image_alpha_adapted);
|
|
image.rgb = light_over_image(ambi_alpha_masked, image.rgb, AMBI_ADD_ON_BLACK) ;
|
|
|
|
}
|
|
return image.rgb;
|
|
|
|
/*image.rgb = (AMBI_BG_IMAGE_BLEND_MODE == 0.0) ?
|
|
mix(ambi.rgb, image.rgb, image_alpha_adapted)
|
|
:
|
|
image.rgb + (ambi.rgb * (1 - image_alpha_adapted));
|
|
|
|
return image.rgb;*/
|
|
}
|
|
|
|
float gaussian_coefficient(float x, float sigma) {
|
|
//restituisce un coefficiente gaussiano per x compreso tra 0 ed 1
|
|
float coefficient = 1.0 / sqrt(2.0 * 3.14159265358979323846 * sigma);
|
|
float exponent = -((x * x) / (2.0 * sigma));
|
|
return coefficient * exp(exponent);
|
|
}
|
|
|
|
|
|
|
|
|
|
vec3 fn_pixel_content(vec2 coords) {
|
|
|
|
vec3 pixel_out;
|
|
vec3 pixel_glowed;
|
|
float dot_mat_or_pixelgrid = 1.0; //init dot grid or scanline mask to 1.0
|
|
|
|
//Black frame insertion, made static, enable it in config.inc.
|
|
#ifdef ALLOW_ALT_BLANK
|
|
float alt_blank_power = 1.0;
|
|
//Calculate if line has to be blanked
|
|
if (DO_ALT_BLANK == 1.0 ) alt_blank_power = pixel_blank_alternate( 1 - ALT_BLANK_STRENGTH);
|
|
|
|
//If we completely blanked the line, there is no need to process it further.
|
|
if (alt_blank_power == 0.0) return vec3(0.0);
|
|
#endif
|
|
|
|
//#define USE_QUILEZ
|
|
|
|
#ifdef USE_QUILEZ
|
|
pixel_glowed = texture(in_glow_pass, coords_QULEZ(coords, global.flick_and_noise_passSize)).rgb;
|
|
pixel_out = pixel_glowed;
|
|
#else
|
|
pixel_glowed = texture(in_glow_pass, coords).rgb;
|
|
pixel_out = pixel_glowed;
|
|
#endif
|
|
|
|
|
|
//Pixel grid
|
|
if (DO_PIXELGRID == 1.0) {
|
|
vec4 pixel_grid = fn_pixel_grid(coords, pixel_out, 0.0, vMax_inLum);
|
|
//grid mask only is needed by halo to selectively light the grid.
|
|
dot_mat_or_pixelgrid = pixel_grid.a;
|
|
pixel_out = pixel_grid.rgb;
|
|
}
|
|
//Dot mask
|
|
if (DO_DOT_MATRIX == 1.0) {
|
|
vec4 pixel_dotmat = fn_pixel_dotmat(coords, pixel_out);
|
|
//grid mask only is needed by halo to selectively light the grid.
|
|
dot_mat_or_pixelgrid = pixel_dotmat.a;
|
|
//rgb channel goes to pixel_out
|
|
pixel_out = pixel_dotmat.rgb;
|
|
|
|
}
|
|
|
|
//Halo
|
|
vec3 pixel_haloed = vec3(0.0);;
|
|
|
|
if (DO_HALO == 1.0 ) {
|
|
pixel_haloed = texture(halo_pass,coords).rgb;
|
|
//Halo only on scanlines:
|
|
pixel_out += pixel_haloed * dot_mat_or_pixelgrid ;
|
|
//Halo over scanlines gap too:
|
|
pixel_out += pixel_haloed * (HALO_VS_SCAN) * (1 - dot_mat_or_pixelgrid) ;
|
|
}
|
|
|
|
// Apply gamma out:
|
|
if (DO_CCORRECTION == 1.0)
|
|
pixel_out = pow(max(pixel_out, vec3(0.0)),vec3(GAMMA_OUT));
|
|
|
|
//Bloom
|
|
/* if (DO_BLOOM == 1.0 ) {
|
|
vec3 bloomed=texture(bloom_pass_final, coords).rgb ;
|
|
if (BLOOM_BYPASS != 1.0 ) {
|
|
vec3 lum = clamp(pixel_glowed, 0.0,1.0);
|
|
vec3 darkness=(1-lum) * (1-lum);
|
|
bloomed = mix( bloomed * darkness , bloomed, BLOOM_OVER_WHITE);
|
|
}
|
|
pixel_out = bloomed + float(BLOOM_BYPASS < 0.5) * pixel_out;
|
|
}*/
|
|
//Bloom
|
|
if (DO_BLOOM == 1.0 ) {
|
|
vec3 bloomed=texture(bloom_pass_final, coords).rgb ;
|
|
pixel_out = bloomed + float(BLOOM_BYPASS < 0.5) * pixel_out;
|
|
}
|
|
|
|
//Black frame insertion, made static, enable it in config.inc.
|
|
#ifdef ALLOW_ALT_BLANK
|
|
if (DO_ALT_BLANK == 1.0 )
|
|
//apply the blank power we calculated earlier in the code.
|
|
pixel_out.rgb = clamp(pixel_out.rgb,0.0,1.0) * alt_blank_power;
|
|
#endif
|
|
|
|
return pixel_out;
|
|
}
|
|
|
|
|
|
vec4 fn_pixel_bezel(vec2 coords_for_bezel, vec2 coords_for_mirror, float nightify_str, vec3 pixel_ambilight) {
|
|
|
|
//Can we skip Blank outside border and inner blank tube?
|
|
vec4 inner_blank_rect = vec4(1-BEZEL_TUBE_BLANK_SIZE, 1-BEZEL_TUBE_BLANK_SIZE, BEZEL_TUBE_BLANK_SIZE, BEZEL_TUBE_BLANK_SIZE);
|
|
if (is_first_outside_rect(coords_for_bezel, RECT01) ||
|
|
is_first_inside_rect(coords_for_bezel, inner_blank_rect)
|
|
) return vec4(0.0);
|
|
|
|
vec4 bezel_in;
|
|
|
|
//Sample main bezel texture:
|
|
#ifndef BEZEL_RES
|
|
vec2 bezel_lut_size = textureSize(monitor_body_curved, 0); //no need to branch here if we assume straight and curved textures are the same size. //FIXME?
|
|
#else
|
|
vec2 bezel_lut_size = BEZEL_RES;
|
|
#endif
|
|
float bezel_frame_lod = log2(bezel_lut_size.y * (BEZEL_FRAME_ZOOM+1.0) /global.FinalViewportSize.y);
|
|
|
|
if (BEZEL_USE_STRAIGHT < 0.5)
|
|
bezel_in = textureLod(monitor_body_curved,coords_for_bezel,bezel_frame_lod);
|
|
else
|
|
bezel_in = textureLod(monitor_body_straight,coords_for_bezel,bezel_frame_lod);
|
|
|
|
|
|
//Exit if the bezel is completely transparent (the tube)
|
|
if (bezel_in.a == 0.0) return vec4(0.0);
|
|
|
|
//Colorize the bezel (bezel.r expresses the luminance)
|
|
vec3 bezel_out = bezel_color(bezel_in.r);
|
|
|
|
float lut_specular = bezel_in.g * BEZEL_SPCL_STRENGTH;
|
|
float reflection_modifier = 0.0;
|
|
vec4 pixel_mirrored = vec4(0.0);
|
|
|
|
// Calculate Reflections, can be skipped if blue channel is 0:
|
|
if (bezel_in.b > 0.0) {
|
|
//Reflections: Calculate the bezel roughness to apply to the reflecting area. (bezel_in.g expresses the specularity)
|
|
float roughness = random_fast( 1/1080.0 * BEZEL_ROUGHNESS, vTexCoord );
|
|
roughness *= (1 - min(lut_specular * 10, 1.0)); // <-roughness over specular area looks bad.
|
|
|
|
//Reflections:
|
|
reflection_modifier = bezel_in.b; //bezel_in.b expresses how much the area is reflective
|
|
|
|
//Sample the reflection pass with small offset to simulate the roughness
|
|
pixel_mirrored = texture(reflected_blurred_pass, coords_for_mirror + roughness);
|
|
float fcorners_shade = 1 - corners_shade(coords_for_bezel, 1.0) * BEZEL_CORNER_DARK;
|
|
|
|
//Show less reflections in the corners
|
|
pixel_mirrored.rgb *= fcorners_shade;
|
|
|
|
//Push it over the specular areas and apply the reflection modifier
|
|
pixel_mirrored.rgb = pixel_mirrored.rgb * (1 + lut_specular);
|
|
|
|
}
|
|
|
|
//Yes, we already passed ambilight as parameter, but if geometry content is overridden
|
|
//then ambientlight may have not been sampled yet due to some skip logic made in the main()
|
|
if (DO_AMBILIGHT == 1.0)
|
|
pixel_ambilight = texture(ambi_temporal_pass, vOutputCoord).rgb;
|
|
|
|
//Apply nightification, the strength is modulated by ambient light.
|
|
//Use a shaded box to apply ambilight on external borders only
|
|
float ambi_colorize_shade = create_ambi_colorize_shade(coords_for_bezel);
|
|
|
|
|
|
vec3 ambi_over_bezel = ( (AMBI_OVER_BEZEL * pixel_ambilight) *
|
|
(bezel_in.a * ambi_colorize_shade) *
|
|
(1-reflection_modifier) );
|
|
|
|
bezel_out.rgb = fn_pixel_nightify(bezel_out.rgb, nightify_str, ambi_over_bezel ) ;
|
|
|
|
|
|
//Apply reflections
|
|
bezel_out += (pixel_mirrored.rgb * reflection_modifier);
|
|
|
|
//Apply ambient light over the bezel
|
|
//bezel_out = bezel_out + ambi_over_bezel;
|
|
bezel_out = light_over_image(ambi_over_bezel, bezel_out, AMBI_ADD_ON_BLACK);
|
|
|
|
//Diffuse the light over specular areas, we use a mipmap with low precision.
|
|
if (lut_specular > 0.0) {
|
|
vec4 pixel_avglum = texture(avglum_pass, coords_for_mirror);
|
|
bezel_out = bezel_out + (pixel_avglum.rgb * lut_specular) ;
|
|
}
|
|
|
|
return vec4(bezel_out, bezel_in.a);
|
|
}
|
|
|
|
|
|
void main() {
|
|
vec3 pixel_out = vec3(0.0);
|
|
|
|
float canvas_busy = 0.0; //<-- this allow for paint over not painted areas (spares gpu cycles)
|
|
|
|
//Initial content coords
|
|
vec2 co_content = vTexCoord;
|
|
|
|
//Tilt?
|
|
if (DO_TILT == 1.0)
|
|
co_content = tilt(co_content, vIsRotated, vec2(TILT_X, TILT_Y));
|
|
|
|
|
|
//Precalc Bezel coords, since it modifies content coords.
|
|
vec2 co_bezel = vec2(0.0);
|
|
if (DO_BEZEL == 1.0) {
|
|
co_content = zoomout_coords(co_content, -vBEZEL_INNER_ZOOM_adapted , 1.0);
|
|
co_bezel = get_scaled_coords_for_bezel();
|
|
}
|
|
|
|
|
|
//Curvature
|
|
//Curvature has to come after inner zoom or bezel curved border will not match content
|
|
//curved border when inner zoom changes.
|
|
if (vDo_Curvature > 0.0) {
|
|
co_content = Warp_koko(co_content, vec2(GEOM_WARP_X, GEOM_WARP_Y), 0.5);
|
|
}
|
|
|
|
//Mirror coords needs to be calculated here, before geom override, but after curvature.
|
|
//It is still not perfect but a reasonable tradeoff by now.
|
|
vec2 co_mirror = zoom(co_content, 1/BEZEL_REFL_ZOOMOUT_ROOM);
|
|
|
|
//Apply other content coords modifiers
|
|
if (DO_GAME_GEOM_OVERRIDE == 1.0)
|
|
co_content = content_geom_override(co_content, GAME_GEOM_ASPECT, vIn_aspect, GAME_GEOM_VSHIFT, GAME_GEOM_HSHIFT, GAME_GEOM_ZOOM);
|
|
|
|
//Dynamic lum dependant full screen zoom?
|
|
if (DO_DYNZOOM == 1.0)
|
|
co_content = zoom(co_content, get_dyn_zoom(avglum_pass) );
|
|
|
|
//Create an alpha mask to write content into, it holds opacity info that will be used to compose:
|
|
if (DO_CURVATURE == 1.0) {
|
|
canvas_busy = fn_border(co_content);
|
|
} else {
|
|
canvas_busy = float(is_first_inside_rect(co_content, RECT01)); //FIXME: is step() faster?
|
|
}
|
|
|
|
if (DELTA_RENDER == 1.0) {
|
|
bool reuse_old = bool( texture(in_glow_pass, co_content).a * canvas_busy * vDeltaRenderOk ) ;
|
|
if (reuse_old) {
|
|
FragColor = texture(final_passFeedback, vOutputCoord) ;
|
|
return;
|
|
}
|
|
}
|
|
|
|
vec3 pixel_ambi = vec3(0.0);
|
|
vec3 pixel_under_content = vec3(0.0);
|
|
//Draw content only over the alpha mask, and sample ambientlight outside it to spare gpu cycles.
|
|
if (canvas_busy > 0.5) {
|
|
pixel_out = fn_pixel_content(co_content) * canvas_busy;
|
|
} else {
|
|
|
|
//Can we halve refresh rate in the outside border to spare some cycles?
|
|
if ( HALVE_BORDER_UPDATE == 1.0 ) {
|
|
if ((params.FrameCount % 2) != 1.0) {
|
|
pixel_out = texture(final_passFeedback, vOutputCoord).rgb;
|
|
FragColor = vec4(pixel_out, 1.0);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//Ambient light
|
|
if (DO_AMBILIGHT == 1.0) {
|
|
pixel_ambi = texture(ambi_temporal_pass, vOutputCoord).rgb;
|
|
if (DO_BG_IMAGE != 1.0)
|
|
pixel_ambi += random_fast( (NOISEPOWER * NOISEPOWER_AMBI_MUL) * ((1-pixel_ambi.g) * (pixel_ambi.g*1 - pixel_ambi.r*0.125 -pixel_ambi.b*0.125 ) ) ,
|
|
vTexCoord * vDynamicSeed ) ;
|
|
|
|
float ambi_noise_power = (NOISEPOWER * NOISEPOWER_AMBI_MUL * (1-pixel_ambi.g) ) * //only middle green channel
|
|
dot( pixel_ambi, vec3(-0.5,1.0,-0.5) ); //denoise only greenish colors
|
|
pixel_ambi += random_fast( ambi_noise_power, vTexCoord * vDynamicSeed ) ;
|
|
|
|
pixel_under_content = pixel_ambi * (1-canvas_busy);
|
|
}
|
|
}
|
|
|
|
//This represents the inner bezel alpha (when using bezel)
|
|
//or the pixel_content canvas (when not using bezel)
|
|
//It is used to draw spot on the whole inner tube even when toe content does not
|
|
//fill the whole tube.
|
|
float canvas_bezel_screen = canvas_busy;
|
|
|
|
//Draw Bezel
|
|
vec4 pixel_bezel;
|
|
//SKIP LOGIC is inside fn_pixel_bezel
|
|
if (DO_BEZEL == 1.0) {
|
|
pixel_bezel = fn_pixel_bezel(co_bezel, co_mirror, BG_IMAGE_NIGHTIFY, pixel_ambi);
|
|
//If we used a smooth_border, canvas_busy is it, but since the content is in the bezel,
|
|
//we can safely use it to smooth/darken the game border
|
|
pixel_out *= canvas_busy;
|
|
pixel_out = mix(pixel_out, pixel_bezel.rgb, pixel_bezel.a);
|
|
|
|
//Update alpha mask.
|
|
//We can't use the bezel alpha channel to update the alpha mask since it is transparent ù
|
|
//on the tube and we don't want to paint anything there, so use a dumb rect and add it to canvas_busy:
|
|
float rect_bezel = float(is_first_inside_rect(co_bezel, vec4(BEZEL_SHADOW_SIZE, BEZEL_SHADOW_SIZE, 1.0 - BEZEL_SHADOW_SIZE, 1.0 - BEZEL_SHADOW_SIZE)));
|
|
canvas_busy = max(pixel_bezel.a, rect_bezel);
|
|
//FragColor = vec4(canvas_busy); return; // <- uncomment to debug BEZEL_SHADOW_SIZE
|
|
|
|
//Intersect rect_bezel and bezel alpha to get the inner tube alpha
|
|
canvas_bezel_screen = (1 - pixel_bezel.a) * rect_bezel;
|
|
}
|
|
|
|
if (canvas_bezel_screen > 0.5) {
|
|
//Vignette
|
|
float vignette = 1.0;
|
|
if (DO_VIGNETTE == 1.0) {
|
|
//float dist = length(vec2((co_content.x-0.5)*vIn_aspect, co_content.y-0.5));
|
|
//vignette = smoothstep(V_SIZE,0.0,dist)*V_POWER;
|
|
vec2 sinco = (co_content-0.5) / V_SIZE ;
|
|
vignette = cos(sinco.x) * cos(sinco.y) * V_POWER;
|
|
}
|
|
|
|
//Spot
|
|
float spot = 0.0;
|
|
if (DO_SPOT == 1.0) {
|
|
float dist = length(vec2((co_content.x-0.5)*vIn_aspect, co_content.y-0.5)+spot_offset);
|
|
spot = smoothstep(S_SIZE,0.0,dist)*S_POWER;
|
|
}
|
|
|
|
float spot_vignette_noise = 0.0;
|
|
if (DO_VIGNETTE + DO_SPOT > 0.0) {
|
|
spot_vignette_noise = random_fast(NOISEPOWER, vOutputCoord ) ;
|
|
}
|
|
|
|
pixel_out = (pixel_out*vignette) + (spot + spot_vignette_noise)*canvas_bezel_screen;
|
|
|
|
}
|
|
//Background image have, no need to paint if not in the outer border:
|
|
if (DO_BG_IMAGE == 1.0 && BG_IMAGE_OVER == 0.0 && canvas_busy < 1.0) {
|
|
vec4 pixel_bg_image = fn_pixel_fgbg_image(bg_under);
|
|
|
|
//pixel_bg_image.rgb = scale_to_range_vec3(pixel_bg_image.rgb, -pixel_ambi, 1+pixel_ambi);
|
|
vec3 anti_nightify_ambi = (pixel_ambi * AMBI_BG_IMAGE_BLEND_MODE) *
|
|
max(AMBI_BG_IMAGE_FORCE, 1-pixel_bg_image.a) ;
|
|
|
|
|
|
pixel_bg_image.rgb = fn_pixel_nightify(pixel_bg_image.rgb, BG_IMAGE_NIGHTIFY, anti_nightify_ambi );
|
|
pixel_bg_image.rgb = ambi_blend_image(pixel_bg_image, pixel_ambi, AMBI_BG_IMAGE_BLEND_MODE);
|
|
|
|
pixel_under_content = pixel_bg_image.rgb;
|
|
}
|
|
|
|
|
|
//Smooth the image corners: canvas_busy (the alpha mask) is a white rect with shaded borders.
|
|
pixel_out = mix (pixel_under_content, pixel_out, canvas_busy);
|
|
|
|
|
|
//Backdrop
|
|
if (DO_BACKDROP == 1.0)
|
|
pixel_out += pixel_backdrop_image();
|
|
|
|
|
|
//Foreground image
|
|
if (DO_BG_IMAGE + BG_IMAGE_OVER == 2.0) {
|
|
vec4 pixel_fg_image = fn_pixel_fgbg_image(bg_over);
|
|
// if geometry content is overridden, ambientlight may have not been sample yet due to some skip logic
|
|
if (DO_AMBILIGHT == 1.0 && (pixel_fg_image.a+AMBI_BG_IMAGE_FORCE) > 0.0 )
|
|
pixel_ambi = texture(ambi_temporal_pass, vOutputCoord).rgb;
|
|
|
|
vec3 anti_nightify_ambi = (pixel_ambi * AMBI_BG_IMAGE_BLEND_MODE) *
|
|
max(AMBI_BG_IMAGE_FORCE, 1-pixel_fg_image.a) ;
|
|
|
|
pixel_fg_image.rgb = fn_pixel_nightify(pixel_fg_image.rgb, BG_IMAGE_NIGHTIFY, anti_nightify_ambi );
|
|
|
|
pixel_out = mix(pixel_out, pixel_fg_image.rgb, pixel_fg_image.a);
|
|
|
|
if (DO_AMBILIGHT + AMBI_BG_IMAGE_BLEND_MODE == 2.0) {
|
|
float ambi_mask = create_ambi_colorize_shade(co_bezel);
|
|
float fg_image_alpha_adapted = max(pixel_fg_image.a - AMBI_BG_IMAGE_FORCE, 0.0);
|
|
//pixel_out = pixel_out + (pixel_ambi.rgb * (ambi_mask) * (1- fg_image_alpha_adapted));
|
|
vec3 light = pixel_ambi.rgb * (ambi_mask) * (1- fg_image_alpha_adapted);
|
|
pixel_out = light_over_image(light, pixel_out, AMBI_ADD_ON_BLACK);
|
|
}
|
|
}
|
|
|
|
|
|
//Debug functions:
|
|
#ifdef DEBUG_QUAD_SPLIT
|
|
if ( vOutputCoord.x < 0.5 && vOutputCoord.y > 0.5 || vOutputCoord.x > 0.5 && vOutputCoord.y < 0.5 )
|
|
pixel_out = texture(DEBUG_TEXTURE,vOutputCoord).rgb;
|
|
#endif
|
|
|
|
#ifdef DEBUG_DUAL_SPLIT_Y
|
|
if (vTexCoord.y < 0.5) pixel_out = texture(DEBUG_TEXTURE,vTexCoord).rgb;
|
|
#endif
|
|
|
|
#ifdef DEBUG_DUAL_SPLIT_Y_GAMMA
|
|
if (vTexCoord.y < 0.5) pixel_out = pow(texture(DEBUG_TEXTURE,vTexCoord).rgb, vec3(1.08));
|
|
#endif
|
|
|
|
#ifdef DEBUG_DUAL_SPLIT_X
|
|
if (vTexCoord.x > 0.5) pixel_out = texture(DEBUG_TEXTURE,vTexCoord).rgb;
|
|
#endif
|
|
|
|
#ifdef DEBUG_DUAL_SPLIT_MIRROR_X
|
|
if (vTexCoord.x > 0.5) pixel_out = texture(DEBUG_TEXTURE,vec2(1-vTexCoord.x, vTexCoord.y) ).rgb;
|
|
#endif
|
|
|
|
#ifdef DEBUG_DUAL_SPLIT_MIRROR_X_GAMMA
|
|
if (vTexCoord.x > 0.5) pixel_out = pow(texture(DEBUG_TEXTURE,vec2(1-vTexCoord.x, vTexCoord.y) ).rgb, vec3(1.11));
|
|
#endif
|
|
|
|
#ifdef DEBUG_PRINT_VALUE
|
|
float maxdigits = 10.0;
|
|
float decimalplaces = 2.0;
|
|
float fvalue = vfprintvalue;
|
|
vec2 vFragCoord = vec2( floor(vOutputCoord.x * params.OutputSize.x),
|
|
floor(vOutputCoord.y * params.OutputSize.y));
|
|
pixel_out += PrintValueVec3( vTexCoord, vFragCoord, fvalue, maxdigits, decimalplaces );
|
|
#endif
|
|
|
|
#ifdef DEBUG_SHOW_CLIP
|
|
float clip = max(max(pixel_out.r, pixel_out.g), pixel_out.b) ;
|
|
if (clip > 1.0)
|
|
pixel_out = 1-pixel_out;//;-clip;
|
|
#endif
|
|
|
|
FragColor = vec4(pixel_out, 1.0);
|
|
|
|
//FragColor = texture(halo_pass, vOutputCoord);
|
|
}
|