Feature: pixelated projection screen

This commit is contained in:
Dejvino 2025-12-30 17:50:49 +00:00
parent c98d4890eb
commit eb8e74273d

View File

@ -17,6 +17,7 @@ uniform sampler2D videoTexture;
uniform float u_effect_type;
uniform float u_effect_strength;
uniform float u_time;
uniform float u_opacity;
varying vec2 vUv;
float random(vec2 st) {
@ -24,23 +25,36 @@ float random(vec2 st) {
}
void main() {
vec2 uv = vUv;
vec4 color = texture2D(videoTexture, uv);
// LED Grid Setup
float ledCountX = 128.0;
float ledCountY = 72.0; // 16:9 Aspect Ratio
vec2 gridUV = vec2(vUv.x * ledCountX, vUv.y * ledCountY);
vec2 cell = fract(gridUV);
vec2 pixelatedUV = (floor(gridUV) + 0.5) / vec2(ledCountX, ledCountY);
vec4 color = texture2D(videoTexture, pixelatedUV);
// Effect 1: Static/Noise (Power On/Off)
if (u_effect_type > 0.0) {
float noise = random(uv + u_time);
float noise = random(pixelatedUV + u_time);
vec3 noiseColor = vec3(noise);
color.rgb = mix(color.rgb, noiseColor, u_effect_strength);
}
gl_FragColor = color;
float dist = distance(cell, vec2(0.5));
float mask = 1.0 - smoothstep(0.35, 0.45, dist);
float brightness = max(color.r, max(color.g, color.b));
float contentAlpha = smoothstep(0.05, 0.15, brightness);
gl_FragColor = vec4(color.rgb, contentAlpha * mask * u_opacity);
}
`;
const visualizerFragmentShader = `
uniform float u_time;
uniform float u_beat;
uniform float u_opacity;
varying vec2 vUv;
vec3 hsv2rgb(vec3 c) {
@ -50,17 +64,27 @@ vec3 hsv2rgb(vec3 c) {
}
void main() {
vec2 uv = vUv;
float dist = length(uv - 0.5);
float ledCountX = 128.0;
float ledCountY = 72.0;
vec2 gridUV = vec2(vUv.x * ledCountX, vUv.y * ledCountY);
vec2 cell = fract(gridUV);
vec2 uv = (floor(gridUV) + 0.5) / vec2(ledCountX, ledCountY);
float dist = distance(cell, vec2(0.5));
float mask = 1.0 - smoothstep(0.35, 0.45, dist);
float d = length(uv - 0.5);
float angle = atan(uv.y - 0.5, uv.x - 0.5);
float wave = sin(dist * 20.0 - u_time * 2.0);
float wave = sin(d * 20.0 - u_time * 2.0);
float beatWave = sin(angle * 5.0 + u_time) * u_beat;
float hue = fract(u_time * 0.1 + dist * 0.2);
float hue = fract(u_time * 0.1 + d * 0.2);
float val = 0.5 + 0.5 * sin(wave + beatWave);
float contentAlpha = smoothstep(0.1, 0.3, val);
gl_FragColor = vec4(hsv2rgb(vec3(hue, 0.8, val)), 1.0);
gl_FragColor = vec4(hsv2rgb(vec3(hue, 0.8, val)), contentAlpha * mask * u_opacity);
}
`;
@ -86,6 +110,7 @@ export class ProjectionScreen extends SceneFeature {
onComplete: null
};
state.originalScreenIntensity = 2.0;
state.screenOpacity = 1.0;
// Ensure video element exists
if (!state.videoElement) {
@ -161,11 +186,13 @@ export class ProjectionScreen extends SceneFeature {
state.tvScreen.material = new THREE.ShaderMaterial({
uniforms: {
u_time: { value: 0.0 },
u_beat: { value: 0.0 }
u_beat: { value: 0.0 },
u_opacity: { value: state.screenOpacity }
},
vertexShader: screenVertexShader,
fragmentShader: visualizerFragmentShader,
side: THREE.DoubleSide
side: THREE.DoubleSide,
transparent: true
});
state.screenLight.intensity = state.originalScreenIntensity;
}
@ -194,10 +221,12 @@ export function turnTvScreenOn() {
u_effect_type: { value: 0.0 },
u_effect_strength: { value: 0.0 },
u_time: { value: 0.0 },
u_opacity: { value: state.screenOpacity !== undefined ? state.screenOpacity : 0.7 }
},
vertexShader: screenVertexShader,
fragmentShader: screenFragmentShader,
side: THREE.DoubleSide
side: THREE.DoubleSide,
transparent: true
});
state.tvScreen.material.needsUpdate = true;