feat: refine nightdrive world and authored car

Integrate the Blender grand tourer with a cohesive shader-driven road, terrain, atmosphere, lighting, and post-processing pass. Refine responsive drive dynamics and sign lifecycle behavior, and add durable visual, accessibility, and performance verification evidence.
This commit is contained in:
2026-09-02 23:16:25 +03:00
parent 7dae835ac5
commit bc18bc2043
73 changed files with 1555 additions and 293 deletions

View File

@@ -2,6 +2,7 @@ import * as THREE from "three";
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass.js";
import { UnrealBloomPass } from "three/examples/jsm/postprocessing/UnrealBloomPass.js";
import { OutputPass } from "three/examples/jsm/postprocessing/OutputPass.js";
import type {
GpuRenderTargetAllocation,
GpuRenderTargetReporter,
@@ -10,6 +11,7 @@ import type {
export interface PostProcessor extends GpuRenderTargetReporter {
resize(width: number, height: number, pixelRatio: number): void;
setDynamics(acceleration: number): void;
render(scene: unknown, camera: unknown): void;
dispose(): void;
}
@@ -38,23 +40,32 @@ interface RenderPassLike {
}
interface BloomPassLike {
strength: number;
radius: number;
threshold: number;
renderTargetBright?: RenderTargetLike;
renderTargetsHorizontal?: readonly RenderTargetLike[];
renderTargetsVertical?: readonly RenderTargetLike[];
dispose(): void;
}
interface OutputPassLike {
dispose(): void;
}
export interface PostProcessingFactories {
createComposer(renderer: unknown): ComposerLike;
createRenderPass(scene: unknown, camera: unknown): RenderPassLike;
createBloomPass(): BloomPassLike;
createOutputPass(): OutputPassLike;
}
const productionFactories: PostProcessingFactories = {
createComposer: (renderer) => new EffectComposer(renderer),
createRenderPass: (scene, camera) => new RenderPass(scene, camera),
createBloomPass: () =>
new UnrealBloomPass(new THREE.Vector2(1, 1), 0.46, 0.32, 0.84),
new UnrealBloomPass(new THREE.Vector2(1, 1), 0.54, 0.42, 0.88),
createOutputPass: () => new OutputPass(),
};
export function createPostProcessor(
@@ -68,8 +79,10 @@ export function createPostProcessor(
new THREE.Camera(),
);
const bloomPass = factories.createBloomPass();
const outputPass = factories.createOutputPass();
composer.addPass(renderPass);
composer.addPass(bloomPass);
composer.addPass(outputPass);
let disposed = false;
const processor: PostProcessor = {
@@ -84,6 +97,13 @@ export function createPostProcessor(
Math.max(1, Math.floor(finiteOr(height, 1))),
);
},
setDynamics(acceleration) {
if (disposed) return;
const energy = clamp(acceleration, 0, 1);
bloomPass.strength = 0.54 + energy * 0.22;
bloomPass.radius = 0.42 + energy * 0.08;
bloomPass.threshold = 0.88 - energy * 0.03;
},
render(scene, camera) {
if (disposed) return;
renderPass.scene = scene;
@@ -95,6 +115,7 @@ export function createPostProcessor(
disposed = true;
renderPass.dispose();
bloomPass.dispose();
outputPass.dispose();
composer.dispose();
},
};