Files
Portfolio/src/scene/world/horizon-air.ts

67 lines
2.4 KiB
TypeScript

import * as THREE from 'three';
import type { SceneResourceBag } from '@/scene/resources';
import type { MutableChapterPalette } from './palette';
/** Thin, spatially separated air masses. No screen-sized render targets. */
export function createHorizonAir(resources: SceneResourceBag) {
const group = new THREE.Group();
group.name = 'horizon-air-masses';
const uniforms = {
uTime: { value: 0 },
uWarmth: { value: 0 },
uDawn: { value: 0 },
uColor: { value: new THREE.Color(0xc5646b) },
};
const material = resources.own(
'material',
new THREE.ShaderMaterial({
uniforms,
vertexShader: `
varying vec2 vUv;
void main(){vUv=uv;gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0);}
`,
fragmentShader: `
uniform float uTime;
uniform float uWarmth;
uniform float uDawn;
uniform vec3 uColor;
varying vec2 vUv;
void main(){
float wave=sin(vUv.x*12.0+uTime*0.018)*0.055+sin(vUv.x*31.0-uTime*0.026)*0.018;
float ribbon=exp(-pow((vUv.y-0.45-wave)*5.6,2.0));
float thin=exp(-pow((vUv.y-0.51-wave)*37.0,2.0));
float horizontal=pow(max(0.0,sin(vUv.x*3.14159)),2.0);
float colorWave=0.5+0.5*sin(vUv.x*7.0+uTime*0.035);
vec3 color=mix(uColor,vec3(0.27,0.045,0.75),colorWave*0.55);
color=mix(color,vec3(0.05,0.52,0.75),smoothstep(0.54,0.82,vUv.y)*0.3);
gl_FragColor=vec4(color,(ribbon*0.16+thin*0.032)*horizontal*(0.7+uWarmth*0.3)*(1.0+uDawn));
#include <tonemapping_fragment>
#include <colorspace_fragment>
}
`,
transparent: true,
depthWrite: false,
depthTest: true,
blending: THREE.AdditiveBlending,
}),
);
const geometry = resources.own('geometry', new THREE.PlaneGeometry(1450, 95));
for (let i = 0; i < 3; i++) {
const mesh = new THREE.Mesh(geometry, material);
mesh.name = `stratified-air-${i + 1}`;
mesh.position.set((i - 1) * 85, 15 + i * 11, -500 - i * 70);
mesh.scale.set(1 - i * 0.12, 1 - i * 0.18, 1);
group.add(mesh);
}
return {
group,
/** dawn: END OF SHIFT, the air masses catch first light (gain up to 2x). */
update(palette: Readonly<MutableChapterPalette>, time: number, dawn = 0) {
uniforms.uTime.value = time;
uniforms.uDawn.value = Number.isFinite(dawn) ? Math.min(1, Math.max(0, dawn)) : 0;
uniforms.uWarmth.value = palette.warmth;
uniforms.uColor.value.copy(palette.sun);
},
};
}