stack-overflow-game / src /pipelines /TrinitronPipeline.js
broadfield-dev's picture
Upload 51 files
78475cb verified
import Phaser from 'phaser';
const fragShader = `
precision mediump float;
uniform sampler2D uMainSampler;
uniform vec2 resolution;
uniform float time;
varying vec2 outTexCoord;
#define PI 3.14159265359
vec4 permute(vec4 t) {
return mod(((t * 34.0) + 1.0) * t, 289.0);
}
float noise3d(vec3 p) {
vec3 a = floor(p);
vec3 d = p - a;
d = d * d * (3.0 - 2.0 * d);
vec4 b = a.xxyy + vec4(0.0, 1.0, 0.0, 1.0);
vec4 k1 = permute(b.xyxy);
vec4 k2 = permute(k1.xyxy + b.zzww);
vec4 c = k2 + a.zzzz;
vec4 k3 = permute(c);
vec4 k4 = permute(c + 1.0);
vec4 o1 = fract(k3 * (1.0 / 41.0));
vec4 o2 = fract(k4 * (1.0 / 41.0));
vec4 o3_interp_z = o2 * d.z + o1 * (1.0 - d.z);
vec2 o4_interp_xy = o3_interp_z.yw * d.x + o3_interp_z.xz * (1.0 - d.x);
return o4_interp_xy.y * d.y + o4_interp_xy.x * (1.0 - d.y);
}
void main() {
float brightness = 2.5;
float red_balance = 1.0;
float green_balance = 0.85;
float blue_balance = 1.0;
float phosphorWidth = 2.50;
float phosphorHeight = 4.50;
float internalHorizontalGap = 1.0;
float columnGap = 0.2;
float verticalCellGap = 0.2;
float phosphorPower = 0.9;
float cell_noise_variation_amount = 0.025;
float cell_noise_scale_xy = 240.0;
float cell_noise_speed = 24.0;
float curvature_amount = 0.0;
vec2 fragCoord = gl_FragCoord.xy;
vec2 uv = outTexCoord;
vec2 centered_uv_output = uv - 0.5;
float r = length(centered_uv_output);
float distort_factor = 1.0 + curvature_amount * r * r;
vec2 centered_uv_source = centered_uv_output * distort_factor;
vec2 source_uv = centered_uv_source + 0.5;
vec2 fragCoord_warped = source_uv * resolution;
bool is_on_original_flat_screen = source_uv.x >= 0.0 && source_uv.x <= 1.0 &&
source_uv.y >= 0.0 && source_uv.y <= 1.0;
if (!is_on_original_flat_screen) {
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
return;
}
float fullCellWidth = 3.0 * phosphorWidth + 3.0 * internalHorizontalGap + columnGap;
float fullRowHeight = phosphorHeight + verticalCellGap;
float logical_cell_index_x = floor(fragCoord_warped.x / fullCellWidth);
float shift_y_offset = 0.0;
if (mod(logical_cell_index_x, 2.0) != 0.0) {
shift_y_offset = fullRowHeight / 2.0;
}
float effective_y_warped = fragCoord_warped.y + shift_y_offset;
float logical_row_index = floor(effective_y_warped / fullRowHeight);
float uv_cell_x = mod(fragCoord_warped.x, fullCellWidth);
if (uv_cell_x < 0.0) {
uv_cell_x += fullCellWidth;
}
float uv_row_y = mod(effective_y_warped, fullRowHeight);
if (uv_row_y < 0.0) {
uv_row_y += fullRowHeight;
}
vec3 video_color = texture2D(uMainSampler, source_uv).rgb;
video_color.r *= red_balance;
video_color.g *= green_balance;
video_color.b *= blue_balance;
vec3 final_color = vec3(0.0);
bool in_column_gap = uv_cell_x >= (3.0 * phosphorWidth + 3.0 * internalHorizontalGap);
bool in_vertical_gap = uv_row_y >= phosphorHeight;
if (!in_column_gap && !in_vertical_gap) {
float uv_cell_x_within_block = uv_cell_x;
vec3 phosphor_base_color = vec3(0.0);
float video_component_intensity = 0.0;
float current_phosphor_startX_in_block = -1.0;
float current_x_tracker = 0.0;
if (uv_cell_x_within_block >= current_x_tracker && uv_cell_x_within_block < current_x_tracker + phosphorWidth) {
phosphor_base_color = vec3(1.0, 0.0, 0.0);
video_component_intensity = video_color.r;
current_phosphor_startX_in_block = current_x_tracker;
}
current_x_tracker += phosphorWidth + internalHorizontalGap;
if (uv_cell_x_within_block >= current_x_tracker && uv_cell_x_within_block < current_x_tracker + phosphorWidth) {
phosphor_base_color = vec3(0.0, 1.0, 0.0);
video_component_intensity = video_color.g;
current_phosphor_startX_in_block = current_x_tracker;
}
current_x_tracker += phosphorWidth + internalHorizontalGap;
if (uv_cell_x_within_block >= current_x_tracker && uv_cell_x_within_block < current_x_tracker + phosphorWidth) {
phosphor_base_color = vec3(0.0, 0.0, 1.0);
video_component_intensity = video_color.b;
current_phosphor_startX_in_block = current_x_tracker;
}
if (current_phosphor_startX_in_block >= 0.0) {
float x_in_phosphor = (uv_cell_x_within_block - current_phosphor_startX_in_block) / phosphorWidth;
float horizontal_intensity_factor = pow(sin(x_in_phosphor * PI), phosphorPower);
float y_in_phosphor_band = uv_row_y / phosphorHeight;
float vertical_intensity_factor = (phosphorHeight > 0.0) ? pow(sin(y_in_phosphor_band * PI), phosphorPower) : 1.0;
float total_intensity_factor = horizontal_intensity_factor * vertical_intensity_factor;
final_color = phosphor_base_color * video_component_intensity * total_intensity_factor;
}
}
vec3 noise_pos = vec3(logical_cell_index_x * cell_noise_scale_xy,
logical_row_index * cell_noise_scale_xy,
time * cell_noise_speed);
vec3 cell_noise_rgb;
cell_noise_rgb.r = noise3d(noise_pos);
cell_noise_rgb.g = noise3d(noise_pos + vec3(19.0, 0.0, 0.0));
cell_noise_rgb.b = noise3d(noise_pos + vec3(0.0, 13.0, 0.0));
cell_noise_rgb = cell_noise_rgb * 2.0 - 1.0;
final_color += cell_noise_rgb * cell_noise_variation_amount;
final_color *= brightness;
float edge_darken_strength = 0.1;
float vignette_factor = 1.0 - dot(centered_uv_output, centered_uv_output) * edge_darken_strength * 2.0;
vignette_factor = clamp(vignette_factor, 0.0, 1.0);
final_color *= vignette_factor;
final_color = clamp(final_color, 0.0, 1.0);
gl_FragColor = vec4(final_color, 1.0);
}
`;
export default class TrinitronPipeline extends Phaser.Renderer.WebGL.Pipelines.PostFXPipeline {
constructor(game) {
super({
name: 'TrinitronPipeline',
game: game,
renderTarget: true,
fragShader: fragShader,
uniforms: [
'uMainSampler',
'resolution',
'time'
]
});
}
onPreRender() {
// Use the actual canvas display size (after scaling), not the game resolution
const canvas = this.game.canvas;
const displayWidth = canvas.width;
const displayHeight = canvas.height;
this.set2f('resolution', displayWidth, displayHeight);
this.set1f('time', this.game.loop.time / 1000);
}
}