Files
retro-flight-sim/js/retro.js
T
mac-container-dev 76bad46d09 Initial commit: Retro 90s flight simulator MVP
Loading screen, main menu, 3D flight sim with CRT post-processing,
procedural terrain, airport with buildings, low-poly aircraft,
flight physics, HUD instruments, and sound.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-05 10:43:22 +00:00

109 lines
3.1 KiB
JavaScript

import * as THREE from 'three';
export class RetroEffect {
constructor(renderer, width, height) {
this.renderer = renderer;
this.width = width;
this.height = height;
this.renderTarget = new THREE.WebGLRenderTarget(width, height, {
minFilter: THREE.NearestFilter,
magFilter: THREE.NearestFilter,
});
this.scene = new THREE.Scene();
this.camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
this.quad = new THREE.Mesh(
new THREE.PlaneGeometry(2, 2),
new THREE.ShaderMaterial({
uniforms: {
tDiffuse: { value: null },
resolution: { value: new THREE.Vector2(width, height) },
time: { value: 0 },
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = vec4(position, 0.0, 1.0);
}
`,
fragmentShader: `
uniform sampler2D tDiffuse;
uniform vec2 resolution;
uniform float time;
varying vec2 vUv;
vec2 barrelDistortion(vec2 uv, float amount) {
vec2 center = uv - 0.5;
float dist = dot(center, center);
return uv + center * dist * amount;
}
vec3 quantize(vec3 color, float levels) {
return floor(color * levels + 0.5) / levels;
}
void main() {
vec2 distortedUv = barrelDistortion(vUv, 0.25);
// Clamp distorted UV
distortedUv = clamp(distortedUv, 0.0, 1.0);
// Chromatic aberration
float aberration = 0.0015;
vec2 dir = distortedUv - 0.5;
float dist = length(dir);
float r = texture2D(tDiffuse, distortedUv + dir * aberration).r;
vec2 gbSample = distortedUv - dir * aberration * 0.5;
float g = texture2D(tDiffuse, gbSample).g;
float b = texture2D(tDiffuse, distortedUv - dir * aberration).b;
vec3 color = vec3(r, g, b);
// Scanline
float scanline = sin(vUv.y * resolution.y * 3.14159) * 0.08;
color -= scanline;
// Quantize to EGA-like palette
color = quantize(color, 16.0);
// Vignette
float vignette = 1.0 - dist * 0.6;
vignette = smoothstep(0.0, 1.0, vignette);
color *= vignette;
// Subtle phosphor warmth
color.r += 0.01;
color.g += 0.005;
// CRT curvature darkening at edges
float edgeDarken = smoothstep(0.3, 1.2, dist);
color *= 1.0 - edgeDarken * 0.3;
gl_FragColor = vec4(color, 1.0);
}
`,
})
);
this.scene.add(this.quad);
}
update(time) {
this.quad.material.uniforms.time.value = time;
}
render(scene, camera) {
// Render scene to offscreen target
this.renderer.setRenderTarget(this.renderTarget);
this.renderer.render(scene, camera);
// Render post-processed quad to screen
this.renderer.setRenderTarget(null);
this.quad.material.uniforms.tDiffuse.value = this.renderTarget.texture;
this.renderer.render(this.scene, this.camera);
}
}