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);
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));
}
gl_FragColor = vec4(finalColor, mask);
gl_FragColor = vec4(finalColor, mask * u_opacity);
}
`;
@ -129,6 +129,7 @@ export class ProjectionScreen extends SceneFeature {
projectionScreenInstance = this;
this.isVisualizerActive = false;
this.screens = [];
this.blackoutLerp = 0;
this.colorBuffer = new Float32Array(16 * 3);
this.standbyState = {
@ -158,6 +159,7 @@ export class ProjectionScreen extends SceneFeature {
};
state.originalScreenIntensity = 2.0;
state.screenOpacity = 0.5;
state.screenOpacityBlackout = 1.0;
// Ensure video element exists
if (!state.videoElement) {
@ -245,6 +247,22 @@ export class ProjectionScreen extends SceneFeature {
update(deltaTime) {
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) {
this.renderStandbyFrame();
}
@ -272,10 +290,7 @@ export class ProjectionScreen extends SceneFeature {
if (this.isVisualizerActive) {
const beat = (state.music && state.music.beatIntensity) ? state.music.beatIntensity : 0.0;
state.screenLight.intensity = state.originalScreenIntensity * (0.5 + beat * 0.5);
if (state.blackoutMode) {
state.screenLight.intensity = 0;
}
state.screenLight.intensity *= dimFactor;
// Update color buffer
const colors = state.config.lightBarColors;
@ -297,13 +312,26 @@ export class ProjectionScreen extends SceneFeature {
if (s.mesh.material.uniforms.u_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() {
@ -554,11 +582,6 @@ export function updateScreenEffect() {
if (material.uniforms.u_effect_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 (material.uniforms.u_effect_type) material.uniforms.u_effect_type.value = 0.0;