fix(drone): resolve drone feed fog and flicker issues

- Add preserveDrawingBuffer to enable automated test screenshots
- Neutralize scene fog during drone render pass to prevent misted aerial view
- Render drone feed every frame instead of alternating with ground view
- Update visor styling with scanline effect and improved borders
- Add debug getters for WebGL renderer and scene information
This commit is contained in:
2026-11-01 01:00:56 -03:00
parent 68cb4eb9ad
commit a43176a68e
16 changed files with 54 additions and 7 deletions
+47 -4
View File
@@ -30,7 +30,9 @@ scene.fog = new THREE.Fog(0x87ceeb, 120, 600);
const camera = new THREE.PerspectiveCamera(72, 1, 0.1, 2000);
camera.position.set(0, 1.7, 0);
const renderer = new THREE.WebGLRenderer({ antialias: true });
// preserveDrawingBuffer: lets automated tests/screenshots read real
// frame pixels (the PIP visor proof); cost is negligible here.
const renderer = new THREE.WebGLRenderer({ antialias: true, preserveDrawingBuffer: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
@@ -4347,6 +4349,18 @@ function renderDroneView() {
drone.cam.aspect = w / h;
drone.cam.updateProjectionMatrix();
// Neutralise the city fog for the drone feed so the aerial view is
// never misted ("embassado"). Only the fog uniform values change for
// this pass — three refreshes them per render, so no material is
// recompiled and the ground view keeps its weather haze.
const fog = scene.fog;
const fogNear = fog ? fog.near : 0;
const fogFar = fog ? fog.far : 0;
if (fog) {
fog.near = 1e7;
fog.far = 2e7; // smoothstep → factor 0 at any real distance
}
renderer.setScissorTest(true);
renderer.setScissor(x, y, w, h);
renderer.setViewport(x, y, w, h);
@@ -4354,6 +4368,12 @@ function renderDroneView() {
renderer.setScissorTest(false);
renderer.setViewport(0, 0, cr.width, cr.height);
if (fog) {
fog.near = fogNear;
fog.far = fogFar;
}
drone.fogCleared = !!fog;
drone.view = { x, y, w, h };
}
@@ -7137,10 +7157,10 @@ function animate() {
renderer.render(scene, camera);
perfFrame.render = performance.now() - tRender;
// Drone command-panel feed: scissored second pass on alternate frames.
// Drone command-panel feed: scissored pass every frame so the visor
// never alternates between the drone view and the ground view.
if (drone.active && drone.mesh) {
drone.frame += 1;
if (drone.frame % 2 === 0) renderDroneView();
renderDroneView();
}
}
animate();
@@ -7626,6 +7646,26 @@ window.__city10 = {
return out;
},
playerCrossingAllowed,
/** Debug: the WebGL renderer (frame forensics for automated tests). */
get gl() {
return renderer;
},
/** Debug: live scene background/fog hex + camera pose. */
get sceneInfo() {
return {
bg: scene.background ? scene.background.getHexString() : null,
fog: scene.fog ? scene.fog.color.getHexString() : null,
fogNear: scene.fog ? scene.fog.near : null,
fogFar: scene.fog ? scene.fog.far : null,
cam: {
x: +camera.position.x.toFixed(2),
y: +camera.position.y.toFixed(2),
z: +camera.position.z.toFixed(2),
},
toneMapping: renderer.toneMapping,
exposure: renderer.toneMappingExposure,
};
},
/** Current bump-to-talk peer (null when the chat is closed). */
get talkPeer() {
return currentPeer;
@@ -7684,6 +7724,8 @@ window.__city10 = {
trafficLights: trafficSystem ? trafficSystem.heads.length / 2 : 0,
crosswalks: crosswalkCount,
sidewalkSegments: sidewalkSegmentCount,
hour: +(sim.hour ?? 0).toFixed(1),
night: +(sim.nightFactor ?? 0).toFixed(2),
beach: beachSystem ? beachSystem.counts() : null,
flights: airplaneSystem ? airplaneSystem.flights() : null,
port: portSystem ? portSystem.counts() : null,
@@ -7693,6 +7735,7 @@ window.__city10 = {
y: +drone.pos.y.toFixed(1),
z: +drone.pos.z.toFixed(1),
yaw: +drone.yaw.toFixed(2),
fogCleared: !!drone.fogCleared,
view: drone.view || { x: 0, y: 0, w: 0, h: 0 },
} : null,
robots: pedestrianSystem