feat: layer the nightdrive sky and atmosphere

This commit is contained in:
2026-09-02 14:52:03 +03:00
parent 3c932cb840
commit 5c52de9134
15 changed files with 801 additions and 168 deletions

View File

@@ -0,0 +1,92 @@
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 type { SceneResourceBag } from '@/scene/resources';
export interface PostProcessor {
resize(width: number, height: number, pixelRatio: number): void;
render(scene: unknown, camera: unknown): void;
dispose(): void;
}
interface ComposerLike {
addPass(pass: unknown): void;
setPixelRatio(pixelRatio: number): void;
setSize(width: number, height: number): void;
render(): void;
dispose(): void;
}
interface RenderPassLike {
scene: unknown;
camera: unknown;
dispose(): void;
}
interface BloomPassLike {
dispose(): void;
}
export interface PostProcessingFactories {
createComposer(renderer: unknown): ComposerLike;
createRenderPass(scene: unknown, camera: unknown): RenderPassLike;
createBloomPass(): BloomPassLike;
}
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,
),
};
export function createPostProcessor(
resources: SceneResourceBag,
renderer: unknown,
factories: PostProcessingFactories = productionFactories,
): PostProcessor {
const composer = factories.createComposer(renderer);
const renderPass = factories.createRenderPass(new THREE.Scene(), new THREE.Camera());
const bloomPass = factories.createBloomPass();
composer.addPass(renderPass);
composer.addPass(bloomPass);
let disposed = false;
const processor: PostProcessor = {
resize(width, height, pixelRatio) {
if (disposed) return;
composer.setPixelRatio(clamp(pixelRatio, 0.5, 2));
composer.setSize(
Math.max(1, Math.floor(finiteOr(width, 1))),
Math.max(1, Math.floor(finiteOr(height, 1))),
);
},
render(scene, camera) {
if (disposed) return;
renderPass.scene = scene;
renderPass.camera = camera;
composer.render();
},
dispose() {
if (disposed) return;
disposed = true;
renderPass.dispose();
bloomPass.dispose();
composer.dispose();
},
};
return resources.own('pass', processor);
}
function finiteOr(value: number, fallback: number): number {
return Number.isFinite(value) ? value : fallback;
}
function clamp(value: number, minimum: number, maximum: number): number {
return THREE.MathUtils.clamp(finiteOr(value, minimum), minimum, maximum);
}