Ride sky plays the viz so the paint mirrors something true

Shared sky dome gains an optional viz overlay (off by default; chamber/stage untouched): the running video mixed over the gradient, masked to the upper sky so the horizon stays fog-matched. Same mirrored-sphere projection as the bodywork reflection, so paint and sky agree. Ride binds its vizTarget and holds 0.55.
This commit is contained in:
Dejvino
2026-09-21 19:03:54 +02:00
parent ac6654b0dd
commit 3a3022327c
2 changed files with 41 additions and 1 deletions
+15 -1
View File
@@ -44,8 +44,12 @@ function vizEnvMaterial(mat, instance, strength) {
{
vec3 vDir = normalize( vViewPosition );
vec3 rDir = reflect( vDir, normalize( normal ) );
// Mirrored azimuth shared with the sky dome (see sky.js):
// folds ±π onto the same texel so neither surface shows a seam.
float sU = atan( rDir.z, rDir.x ) * 0.15915494 + 0.5;
sU = 1.0 - abs( 1.0 - 2.0 * sU );
vec2 sUv = vec2(
atan( rDir.z, rDir.x ) * 0.15915494 + 0.5,
sU,
asin( clamp( rDir.y, -1.0, 1.0 ) ) * 0.31830988 + 0.5 );
totalEmissiveRadiance += texture2D( u_vizEnv, sUv ).rgb * u_vizEnvStrength;
}`
@@ -107,6 +111,16 @@ export const ride = {
m.userData.vizEnv.value = texture;
}
}
// The sky wears the same video, so the paint mirrors something true.
// Same mirrored-sphere projection as the bodywork (see sky.js).
if (instance.skyMat) {
if (instance.skyMat.uniforms.u_viz.value !== texture) {
instance.skyMat.uniforms.u_viz.value = texture;
}
if (instance.skyMat.uniforms.u_vizAmount.value <= 0) {
instance.skyMat.uniforms.u_vizAmount.value = 0.55;
}
}
},
// Radio-glow wash for the viz composite — arcs with venueState.light.
+26
View File
@@ -31,6 +31,11 @@ export function buildSky(THREE, { radius = 90 } = {}) {
u_bot: { value: new T.Color(0.02, 0.02, 0.03) },
u_bands: { value: 0.0 },
u_seed: { value: 0.0 },
// The running video, composited elsewhere, overlaid on the dome
// so mirrored bodywork has something true to mirror. Off (0)
// unless a venue binds a texture — chamber and stage never do.
u_viz: { value: null },
u_vizAmount: { value: 0.0 },
},
vertexShader: `
varying vec3 vPos;
@@ -45,7 +50,19 @@ export function buildSky(THREE, { radius = 90 } = {}) {
uniform vec3 u_bot;
uniform float u_bands;
uniform float u_seed;
uniform sampler2D u_viz;
uniform float u_vizAmount;
varying vec3 vPos;
// Mirrored spherical projection shared with the ride's paint
// (see vizEnvMaterial): direction to sphere UVs, with the azimuth
// folded into a triangle so the ±π wrap lands on the same texel
// instead of a visible seam.
vec2 skyVizUv(vec3 dir) {
float u = atan(dir.z, dir.x) * 0.15915494 + 0.5;
u = 1.0 - abs(1.0 - 2.0 * u);
float v = asin(clamp(dir.y, -1.0, 1.0)) * 0.31830988 + 0.5;
return vec2(u, v);
}
void main() {
float h = normalize(vPos).y; // -1 .. 1
vec3 col = h > 0.0
@@ -58,6 +75,15 @@ export function buildSky(THREE, { radius = 90 } = {}) {
b = smoothstep(0.75, 0.95, b) * u_bands;
col += u_top * b * 0.6;
}
// The viz as the sky itself, mixed over the gradient amount.
// Masked to the upper sky: the horizon band stays gradient so
// the ground keeps melting into it (fog-matched), while the
// zenith plays the video.
if (u_vizAmount > 0.001) {
vec3 viz = texture2D(u_viz, skyVizUv(normalize(vPos))).rgb;
float m = smoothstep(0.02, 0.5, h) * clamp(u_vizAmount, 0.0, 1.0);
col = mix(col, viz, clamp(m, 0.0, 1.0));
}
gl_FragColor = vec4(col, 1.0);
}
`,