Feature: blackout raises opacity of projection screen to simulate strong relative light

This commit is contained in:
Dejvino 2026-01-05 21:48:02 +00:00
parent cf6dda2d35
commit eef13e301c

View File

@ -55,7 +55,7 @@ void main() {
float mask = 1.0 - smoothstep(0.35 - edgeSoftness, 0.45 + edgeSoftness, dist); float mask = 1.0 - smoothstep(0.35 - edgeSoftness, 0.45 + edgeSoftness, dist);
mask = mix(mask, 1.0, blurFactor); mask = mix(mask, 1.0, blurFactor);
gl_FragColor = vec4(color.rgb, mask); gl_FragColor = vec4(color.rgb, mask * u_opacity);
} }
`; `;
@ -117,7 +117,7 @@ void main() {
finalColor = hsv2rgb(vec3(hue, 0.8, val)); finalColor = hsv2rgb(vec3(hue, 0.8, val));
} }
gl_FragColor = vec4(finalColor, mask); gl_FragColor = vec4(finalColor, mask * u_opacity);
} }
`; `;
@ -129,6 +129,7 @@ export class ProjectionScreen extends SceneFeature {
projectionScreenInstance = this; projectionScreenInstance = this;
this.isVisualizerActive = false; this.isVisualizerActive = false;
this.screens = []; this.screens = [];
this.blackoutLerp = 0;
this.colorBuffer = new Float32Array(16 * 3); this.colorBuffer = new Float32Array(16 * 3);
this.standbyState = { this.standbyState = {
@ -158,6 +159,7 @@ export class ProjectionScreen extends SceneFeature {
}; };
state.originalScreenIntensity = 2.0; state.originalScreenIntensity = 2.0;
state.screenOpacity = 0.5; state.screenOpacity = 0.5;
state.screenOpacityBlackout = 1.0;
// Ensure video element exists // Ensure video element exists
if (!state.videoElement) { if (!state.videoElement) {
@ -245,6 +247,22 @@ export class ProjectionScreen extends SceneFeature {
update(deltaTime) { update(deltaTime) {
updateScreenEffect(); updateScreenEffect();
// --- Blackout Transition Logic ---
const targetBlackout = state.blackoutMode ? 1.0 : 0.0;
const enterBlackoutSpeed = 0.5; // slow light up
const exitBlackoutSpeed = 5.0; // quick fade out
if (this.blackoutLerp < targetBlackout) {
this.blackoutLerp = Math.min(targetBlackout, this.blackoutLerp + deltaTime * enterBlackoutSpeed);
} else if (this.blackoutLerp > targetBlackout) {
this.blackoutLerp = Math.max(targetBlackout, this.blackoutLerp - deltaTime * exitBlackoutSpeed);
}
const normalOpacity = state.screenOpacity !== undefined ? state.screenOpacity : 0.7;
const blackoutOpacity = state.screenOpacityBlackout !== undefined ? state.screenOpacityBlackout : 0.2;
const currentOpacity = THREE.MathUtils.lerp(normalOpacity, blackoutOpacity, this.blackoutLerp);
const dimFactor = 1.0 - this.blackoutLerp;
if (this.standbyState.active) { if (this.standbyState.active) {
this.renderStandbyFrame(); this.renderStandbyFrame();
} }
@ -272,10 +290,7 @@ export class ProjectionScreen extends SceneFeature {
if (this.isVisualizerActive) { if (this.isVisualizerActive) {
const beat = (state.music && state.music.beatIntensity) ? state.music.beatIntensity : 0.0; const beat = (state.music && state.music.beatIntensity) ? state.music.beatIntensity : 0.0;
state.screenLight.intensity = state.originalScreenIntensity * (0.5 + beat * 0.5); state.screenLight.intensity = state.originalScreenIntensity * (0.5 + beat * 0.5);
state.screenLight.intensity *= dimFactor;
if (state.blackoutMode) {
state.screenLight.intensity = 0;
}
// Update color buffer // Update color buffer
const colors = state.config.lightBarColors; const colors = state.config.lightBarColors;
@ -297,13 +312,26 @@ export class ProjectionScreen extends SceneFeature {
if (s.mesh.material.uniforms.u_colorCount) { if (s.mesh.material.uniforms.u_colorCount) {
s.mesh.material.uniforms.u_colorCount.value = colorCount; s.mesh.material.uniforms.u_colorCount.value = colorCount;
} }
if (s.mesh.material.uniforms.u_opacity) {
const targetOpacity = state.blackoutMode ? 0.1 : state.screenOpacity;
s.mesh.material.uniforms.u_opacity.value = targetOpacity;
}
} }
}); });
} else {
// Video Mode
state.screenLight.intensity = state.originalScreenIntensity * dimFactor;
} }
// Apply Opacity to all screens (Visualizer or Video)
this.screens.forEach(s => {
const material = s.mesh.material;
if (material && material.uniforms) {
if (material.uniforms.u_opacity) {
material.uniforms.u_opacity.value = currentOpacity;
}
// Ensure time is updated for video shader effects even if visualizer is off
if (!this.isVisualizerActive && material.uniforms.u_time) {
material.uniforms.u_time.value = state.clock.getElapsedTime();
}
}
});
} }
onPartyStart() { onPartyStart() {
@ -555,11 +583,6 @@ export function updateScreenEffect() {
material.uniforms.u_effect_strength.value = strength; material.uniforms.u_effect_strength.value = strength;
} }
if (material.uniforms.u_opacity) {
const targetOpacity = state.blackoutMode ? 0.1 : (state.screenOpacity !== undefined ? state.screenOpacity : 0.7);
material.uniforms.u_opacity.value = targetOpacity;
}
if (progress >= 1.0) { if (progress >= 1.0) {
if (material.uniforms.u_effect_type) material.uniforms.u_effect_type.value = 0.0; if (material.uniforms.u_effect_type) material.uniforms.u_effect_type.value = 0.0;
if (material.uniforms.u_effect_strength) material.uniforms.u_effect_strength.value = 0.0; if (material.uniforms.u_effect_strength) material.uniforms.u_effect_strength.value = 0.0;