fix(scene): keep traffic lamps out of the hero car's near field
This commit is contained in:
@@ -9,9 +9,36 @@ import type { RenderQualitySettings } from '@/scene/render-quality';
|
||||
* resting lets the faster lanes pull away. Instances wrap through a hidden band
|
||||
* behind the camera, so nothing pops in view.
|
||||
*/
|
||||
const NEAR_Z = 46;
|
||||
/**
|
||||
* The hero car renders at 2.78x scale: its body spans |x| < 2.6 and its nose
|
||||
* reaches z = -5.5. Traffic never enters that envelope or the near field
|
||||
* around it. Lamps fade out over TRAFFIC_FADE_LENGTH before TRAFFIC_NEAR_Z and
|
||||
* wrap inside the hidden band, so no light ever slides past the car (round 5:
|
||||
* bodiless lamp pairs passing the flank read as the car's own taillights
|
||||
* coming loose).
|
||||
*/
|
||||
export const HERO_CAR_HALF_WIDTH = 2.6;
|
||||
export const TRAFFIC_NEAR_Z = -24;
|
||||
export const TRAFFIC_FADE_LENGTH = 22;
|
||||
const FAR_Z = -660;
|
||||
const SPAN = NEAR_Z - FAR_Z;
|
||||
const SPAN = TRAFFIC_NEAR_Z - FAR_Z;
|
||||
/** Lane centres. Fast lanes on the left, slower trucks on the right. */
|
||||
export const TRAFFIC_LANES = [-5.7, -5.7, -4.3, 5.4, 5.7] as const;
|
||||
export const TRAFFIC_LAMP_HALF_SPACING = { fast: 0.9, slow: 1.0 } as const;
|
||||
|
||||
/** Visibility of a lamp, trail or pool at world z (1 = fully lit). */
|
||||
export function trafficNearFade(z: number): number {
|
||||
const t = Math.min(1, Math.max(0, (TRAFFIC_NEAR_Z - z) / TRAFFIC_FADE_LENGTH));
|
||||
return t * t * (3 - 2 * t);
|
||||
}
|
||||
|
||||
/** Keeps a vehicle inside [FAR_Z, TRAFFIC_NEAR_Z], wrapping through the
|
||||
* hidden band at either end. */
|
||||
export function wrapTrafficZ(z: number): number {
|
||||
if (z > TRAFFIC_NEAR_Z) return z - SPAN * Math.ceil((z - TRAFFIC_NEAR_Z) / SPAN);
|
||||
if (z < FAR_Z) return z + SPAN * Math.ceil((FAR_Z - z) / SPAN);
|
||||
return z;
|
||||
}
|
||||
|
||||
interface Vehicle {
|
||||
x: number;
|
||||
@@ -29,7 +56,7 @@ export function createTraffic(
|
||||
const group = new THREE.Group();
|
||||
group.name = 'expressway-traffic';
|
||||
const count = quality.postEffects ? (quality.postScale >= 1 ? 14 : 10) : 7;
|
||||
const lanes = [-5.1, -5.1, -2.6, 4.9, 5.2];
|
||||
const lanes = TRAFFIC_LANES;
|
||||
const vehicles: Vehicle[] = [];
|
||||
let seed = 7;
|
||||
const random = () => {
|
||||
@@ -42,9 +69,9 @@ export function createTraffic(
|
||||
const fast = lane < 0;
|
||||
vehicles.push({
|
||||
x: lane + (random() - 0.5) * 0.5,
|
||||
z: FAR_Z + random() * (SPAN - 120) + 20,
|
||||
z: FAR_Z + 20 + random() * (SPAN - 40),
|
||||
speed: fast ? 26 + random() * 22 : 9 + random() * 6,
|
||||
width: fast ? 0.78 + random() * 0.12 : 1.0,
|
||||
width: fast ? TRAFFIC_LAMP_HALF_SPACING.fast : TRAFFIC_LAMP_HALF_SPACING.slow,
|
||||
height: fast ? 0.95 : 1.55 + random() * 0.4,
|
||||
tint: random(),
|
||||
});
|
||||
@@ -56,10 +83,14 @@ export function createTraffic(
|
||||
varying vec2 vUv;
|
||||
varying float vDepth;
|
||||
varying float vLevel;
|
||||
varying float vNear;
|
||||
void main() {
|
||||
vUv = uv;
|
||||
vLevel = aLevel;
|
||||
vec4 view = modelViewMatrix * instanceMatrix * vec4(position, 1.0);
|
||||
vec4 world = modelMatrix * instanceMatrix * vec4(position, 1.0);
|
||||
// Matches trafficNearFade(): dark before the hero car's near field.
|
||||
vNear = smoothstep(0.0, 1.0, clamp((${TRAFFIC_NEAR_Z.toFixed(1)} - world.z) / ${TRAFFIC_FADE_LENGTH.toFixed(1)}, 0.0, 1.0));
|
||||
vec4 view = viewMatrix * world;
|
||||
vDepth = -view.z;
|
||||
gl_Position = projectionMatrix * view;
|
||||
}`;
|
||||
@@ -67,9 +98,10 @@ export function createTraffic(
|
||||
varying vec2 vUv;
|
||||
varying float vDepth;
|
||||
varying float vLevel;
|
||||
varying float vNear;
|
||||
void main() {
|
||||
float far = 1.0 - smoothstep(420.0, 660.0, vDepth);
|
||||
float near = smoothstep(1.5, 7.0, vDepth);
|
||||
float near = vNear;
|
||||
${shape}
|
||||
vec3 color = mix(vec3(1.0, 0.03, 0.06), vec3(1.0, 0.22, 0.05), vLevel);
|
||||
gl_FragColor = vec4(color * glow * far * near, 1.0);
|
||||
@@ -135,9 +167,7 @@ export function createTraffic(
|
||||
for (let i = 0; i < count; i++) {
|
||||
const v = vehicles[i]!;
|
||||
const relative = travelSpeed - v.speed; // > 0: we are overtaking
|
||||
v.z += relative * dt;
|
||||
if (v.z > NEAR_Z) v.z -= SPAN;
|
||||
else if (v.z < FAR_Z) v.z += SPAN;
|
||||
v.z = wrapTrafficZ(v.z + relative * dt);
|
||||
const trailLength = THREE.MathUtils.clamp(Math.abs(relative) * 0.16, 0.3, 14);
|
||||
// Trails stream away from the direction of relative travel.
|
||||
const trailDirection = relative >= 0 ? -1 : 1;
|
||||
|
||||
Reference in New Issue
Block a user