feat: sculpt portfolio into an immersive midnight expressway

Author physical road furniture, layered terrain and atmosphere, projected illuminated route signs, and detailed Blender coachwork. Refine camera mass, wheel contact, chapter composition, and compact reading space while preserving approved content and audio contracts.

Include isolated Blender source, before/after and hardware Chrome motion evidence, preservation hashes, validated preview checks, and documented inherited unit failures.
This commit is contained in:
2026-09-05 05:10:17 +03:00
parent c9ac36e249
commit b188cac7ed
74 changed files with 3168 additions and 1223 deletions

View File

@@ -33,10 +33,15 @@ export function createSun(resources: SceneResourceBag): SunWorld {
uTime: { value: 0 },
uDriveEnergy: { value: 0 },
};
const geometry = resources.own('geometry', new THREE.CircleGeometry(112, 128));
const material = resources.own('material', new THREE.ShaderMaterial({
uniforms,
vertexShader: `
const geometry = resources.own(
'geometry',
new THREE.CircleGeometry(126, 160),
);
const material = resources.own(
'material',
new THREE.ShaderMaterial({
uniforms,
vertexShader: `
varying vec2 vSunUv;
void main() {
@@ -44,7 +49,7 @@ export function createSun(resources: SceneResourceBag): SunWorld {
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
fragmentShader: `
uniform vec3 uColor;
uniform vec3 uHazeColor;
uniform float uScanBands;
@@ -55,97 +60,102 @@ export function createSun(resources: SceneResourceBag): SunWorld {
varying vec2 vSunUv;
void main() {
vec2 centered = vSunUv - 0.5;
float radius = length(centered);
float softEdge = 1.0 - smoothstep(0.465, 0.5, radius);
float limb = smoothstep(0.31, 0.485, radius);
float broadField = 0.5 + 0.5 * sin(
vSunUv.x * 9.0
+ sin(vSunUv.y * 11.0 + uTime * 0.028) * 1.35
);
float fineField = 0.5 + 0.5 * sin(
vSunUv.x * 23.0 - vSunUv.y * 16.0 + sin(uTime * 0.021) * 0.7
);
float internalField = broadField * 0.7 + fineField * 0.3;
float verticalHeat = mix(0.68, 1.3, smoothstep(0.06, 0.92, vSunUv.y));
float lowerDisc = 1.0 - smoothstep(0.28, 0.67, vSunUv.y);
float scanPhase = fract(vSunUv.y * uScanBands + sin(uTime * 0.08) * 0.008);
float scanBand = smoothstep(0.09, 0.18, scanPhase)
* (1.0 - smoothstep(0.72, 0.82, scanPhase));
float bandCut = mix(1.0, 0.06 + scanBand * 0.94, lowerDisc);
float horizonFade = smoothstep(0.015, 0.13, vSunUv.y);
float radialCore = 1.0 - smoothstep(0.02, 0.48, radius);
float luminance = verticalHeat
* (1.08 + uWarmth * 0.16 + uRouteProgress * 0.045 + uDriveEnergy * 0.045);
vec3 warmCore = mix(uColor, vec3(1.0, 0.56, 0.32), smoothstep(0.25, 0.92, vSunUv.y) * 0.84);
vec3 core = mix(uColor * 0.82, warmCore * 0.83, smoothstep(0.05, 0.86, vSunUv.y));
core = mix(core, core * mix(0.82, 1.13, internalField), 0.54);
core = mix(core, uHazeColor * 0.72, limb * 0.22 + lowerDisc * 0.055);
gl_FragColor = vec4(core * luminance, softEdge * bandCut * horizonFade);
vec2 solar = vSunUv;
float low = 1.0 - solar.y;
solar.x += sin(solar.y * 132.0 + uTime * 0.42) * 0.0015 * low * low;
float radius = length(solar - 0.5);
float softEdge = 1.0 - smoothstep(0.496, 0.5, radius);
float stripes = (0.65 - solar.y) * 17.0;
float phase = fract(stripes);
float gap = mix(0.12, 0.61, clamp(stripes / 11.0, 0.0, 1.0));
float separated = smoothstep(gap, gap + 0.024, phase);
float bandMask = solar.y > 0.65 ? 1.0 : separated;
float lip = exp(-abs(phase - gap - 0.025) * 90.0) * step(solar.y, 0.65);
float convection = sin(solar.x * 19.0 + sin(solar.y * 23.0 + uTime * 0.055))
* sin(solar.y * 31.0 - uTime * 0.032);
vec3 vermilion = vec3(0.91, 0.045, 0.075);
vec3 coral = mix(uColor, vec3(1.0, 0.19, 0.10), 0.6);
vec3 crown = vec3(1.0, 0.54, 0.25);
vec3 core = mix(vermilion, coral, smoothstep(0.0, 0.58, solar.y));
core = mix(core, crown, smoothstep(0.47, 1.0, solar.y));
float filament = pow(abs(sin(solar.x * 39.0 + solar.y * 25.0 + convection * 2.4)), 16.0);
core *= 0.87 + convection * 0.055 + filament * 0.07;
float limb = pow(smoothstep(0.38, 0.498, radius), 4.0);
core += vec3(1.0, 0.24, 0.095) * (limb * 0.22 + lip * 0.20);
float extinction = smoothstep(0.025, 0.19, solar.y);
if(softEdge * bandMask * extinction < 0.02) discard;
gl_FragColor = vec4(core * (1.0 + uWarmth * 0.10), softEdge * bandMask * extinction);
#include <tonemapping_fragment>
#include <colorspace_fragment>
}
`,
transparent: true,
depthWrite: false,
depthTest: true,
blending: THREE.AdditiveBlending,
toneMapped: true,
}));
transparent: true,
depthWrite: true,
depthTest: true,
blending: THREE.NormalBlending,
toneMapped: true,
}),
);
const disc = new THREE.Mesh(geometry, material);
disc.name = 'horizon-sun';
disc.position.set(0, 60, SUN_DISTANCE_Z);
disc.position.set(0, 79, SUN_DISTANCE_Z);
group.add(disc);
const limbMaterial = resources.own('material', new THREE.ShaderMaterial({
uniforms: {
uColor: { value: sunColor.clone() },
uEnergy: { value: 0 },
},
vertexShader: `
const limbMaterial = resources.own(
'material',
new THREE.ShaderMaterial({
uniforms: {
uColor: { value: sunColor.clone() },
uEnergy: { value: 0 },
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
fragmentShader: `
uniform vec3 uColor;
uniform float uEnergy;
varying vec2 vUv;
void main() {
float radius = distance(vUv, vec2(0.5));
float ring = smoothstep(0.405, 0.475, radius)
* (1.0 - smoothstep(0.475, 0.515, radius));
float ring = smoothstep(0.477, 0.488, radius)
* (1.0 - smoothstep(0.491, 0.499, radius));
gl_FragColor = vec4(uColor * (0.72 + uEnergy * 0.07), ring * (0.22 + uEnergy * 0.025));
#include <tonemapping_fragment>
#include <colorspace_fragment>
}
`,
transparent: true,
depthWrite: false,
depthTest: true,
blending: THREE.AdditiveBlending,
toneMapped: true,
}));
transparent: true,
depthWrite: false,
depthTest: true,
blending: THREE.AdditiveBlending,
toneMapped: true,
}),
);
const limb = new THREE.Mesh(geometry, limbMaterial);
limb.name = 'sun-limb-glow';
limb.position.copy(disc.position);
limb.position.z -= 0.3;
limb.scale.setScalar(1.075);
limb.scale.setScalar(1.034);
group.add(limb);
const haloTexture = resources.own('texture', createHaloTexture());
const haloMaterial = resources.own('material', new THREE.SpriteMaterial({
map: haloTexture,
color: sunColor.clone(),
transparent: true,
opacity: 0.13,
depthWrite: false,
depthTest: true,
blending: THREE.AdditiveBlending,
toneMapped: true,
}));
const haloMaterial = resources.own(
'material',
new THREE.SpriteMaterial({
map: haloTexture,
color: sunColor.clone(),
transparent: true,
opacity: 0.13,
depthWrite: false,
depthTest: true,
blending: THREE.AdditiveBlending,
toneMapped: true,
}),
);
const halo = new THREE.Sprite(haloMaterial);
halo.name = 'horizon-halo';
halo.position.copy(disc.position);
@@ -153,16 +163,19 @@ export function createSun(resources: SceneResourceBag): SunWorld {
halo.scale.set(330, 232, 1);
group.add(halo);
const bleedMaterial = resources.own('material', new THREE.SpriteMaterial({
map: haloTexture,
color: sunColor.clone(),
transparent: true,
opacity: 0.075,
depthWrite: false,
depthTest: true,
blending: THREE.AdditiveBlending,
toneMapped: true,
}));
const bleedMaterial = resources.own(
'material',
new THREE.SpriteMaterial({
map: haloTexture,
color: sunColor.clone(),
transparent: true,
opacity: 0.075,
depthWrite: false,
depthTest: true,
blending: THREE.AdditiveBlending,
toneMapped: true,
}),
);
const bleed = new THREE.Sprite(bleedMaterial);
bleed.name = 'horizon-light-bleed';
bleed.position.set(0, 7, SUN_DISTANCE_Z - 2.2);
@@ -181,13 +194,15 @@ export function createSun(resources: SceneResourceBag): SunWorld {
limbMaterial.uniforms.uColor.value.copy(palette.sun);
uniforms.uWarmth.value = clamp01(palette.warmth);
uniforms.uRouteProgress.value = progress;
uniforms.uTime.value = Number.isFinite(elapsedSeconds) ? elapsedSeconds : 0;
uniforms.uTime.value = Number.isFinite(elapsedSeconds)
? elapsedSeconds
: 0;
uniforms.uDriveEnergy.value = energy;
limbMaterial.uniforms.uEnergy.value = energy;
haloMaterial.opacity = 0.105 + palette.warmth * 0.05 + energy * 0.015;
haloMaterial.opacity = 0.075 + palette.warmth * 0.03 + energy * 0.015;
bleedMaterial.opacity = 0.055 + palette.warmth * 0.03 + energy * 0.01;
halo.scale.set(480 + energy * 18, 360 + energy * 10, 1);
disc.position.y = 60 + palette.warmth * 14;
disc.position.y = 79 + palette.warmth * 14;
limb.position.y = disc.position.y;
halo.position.y = disc.position.y;
bleed.position.y = 7 + palette.warmth * 3;