Ride reflections: quarter-turn env mapping + body patch panels
Mirrored env azimuth rotated 90 degrees in both sky and paint mappings (shared constant): fold lines sit fore/aft instead of mirroring the world at side views. Same render also completes the body patch-up: front side panels, rear quarters, floor pan, taller skirts sealing the doors, lamps/grille seated into the bumper, mirrors rooted in the wall. Verified on the 10x3 grid: reflections track the viz per cell, no seam, no holes at card scale.
This commit is contained in:
@@ -45,9 +45,9 @@ function vizEnvMaterial(mat, instance, strength) {
|
|||||||
vec3 vDir = normalize( vViewPosition );
|
vec3 vDir = normalize( vViewPosition );
|
||||||
vec3 rDir = reflect( vDir, normalize( normal ) );
|
vec3 rDir = reflect( vDir, normalize( normal ) );
|
||||||
// Mirrored azimuth shared with the sky dome (see sky.js):
|
// Mirrored azimuth shared with the sky dome (see sky.js):
|
||||||
// folds ±π onto the same texel so neither surface shows a seam.
|
// quarter-turn offset puts the fold lines fore and aft.
|
||||||
float sU = atan( rDir.z, rDir.x ) * 0.15915494 + 0.5;
|
float sU = atan( rDir.z, rDir.x ) * 0.15915494 + 0.5 + 0.25;
|
||||||
sU = 1.0 - abs( 1.0 - 2.0 * sU );
|
sU = 1.0 - abs( 1.0 - 2.0 * fract( sU ) );
|
||||||
vec2 sUv = vec2(
|
vec2 sUv = vec2(
|
||||||
sU,
|
sU,
|
||||||
asin( clamp( rDir.y, -1.0, 1.0 ) ) * 0.31830988 + 0.5 );
|
asin( clamp( rDir.y, -1.0, 1.0 ) ) * 0.31830988 + 0.5 );
|
||||||
@@ -578,11 +578,27 @@ export const ride = {
|
|||||||
bumperF.position.set(0, -0.35, -2.05);
|
bumperF.position.set(0, -0.35, -2.05);
|
||||||
const bumperR = add(new T.Mesh(new T.BoxGeometry(1.8, 0.22, 0.3), trimMat));
|
const bumperR = add(new T.Mesh(new T.BoxGeometry(1.8, 0.22, 0.3), trimMat));
|
||||||
bumperR.position.set(0, -0.35, 1.62);
|
bumperR.position.set(0, -0.35, 1.62);
|
||||||
// Side skirts between the wheels.
|
// Side skirts between the wheels, tall enough to meet the
|
||||||
|
// door bottoms so no daylight shows under the doors.
|
||||||
for (const side of [-1, 1]) {
|
for (const side of [-1, 1]) {
|
||||||
const skirt = add(new T.Mesh(new T.BoxGeometry(0.12, 0.14, 2.0), trimMat));
|
const skirt = add(new T.Mesh(new T.BoxGeometry(0.12, 0.5, 2.0), trimMat));
|
||||||
skirt.position.set(side * 0.93, -0.5, -0.15);
|
skirt.position.set(side * 0.93, -0.32, -0.15);
|
||||||
}
|
}
|
||||||
|
// Front side panels: fill between hood edge and wheels, where
|
||||||
|
// the engine bay showed straight through to the ground.
|
||||||
|
for (const side of [-1, 1]) {
|
||||||
|
const panel = add(new T.Mesh(new T.BoxGeometry(0.25, 0.35, 1.0), paintMat()));
|
||||||
|
panel.position.set(side * 0.825, -0.08, -1.5);
|
||||||
|
}
|
||||||
|
// Rear quarter panels between doors and rear arches.
|
||||||
|
for (const side of [-1, 1]) {
|
||||||
|
const quarter = add(new T.Mesh(new T.BoxGeometry(0.25, 0.35, 0.9), paintMat()));
|
||||||
|
quarter.position.set(side * 0.825, -0.08, 1.1);
|
||||||
|
}
|
||||||
|
// Floor pan: closes the see-through under the cabin between
|
||||||
|
// the wheels. Overlaps skirts, bumpers and doors all round.
|
||||||
|
const floorPan = add(new T.Mesh(new T.BoxGeometry(1.78, 0.16, 3.5), trimMat));
|
||||||
|
floorPan.position.set(0, -0.52, -0.25);
|
||||||
// Fenders: half-torus arches over each wheel. Torus arc 0..π
|
// Fenders: half-torus arches over each wheel. Torus arc 0..π
|
||||||
// spans the upper half in XY; yawed 90° it arches across the
|
// spans the upper half in XY; yawed 90° it arches across the
|
||||||
// wheel in the rolling plane.
|
// wheel in the rolling plane.
|
||||||
@@ -592,9 +608,10 @@ export const ride = {
|
|||||||
fender.rotation.y = Math.PI / 2;
|
fender.rotation.y = Math.PI / 2;
|
||||||
fender.position.set(wx, -0.62, wz);
|
fender.position.set(wx, -0.62, wz);
|
||||||
}
|
}
|
||||||
// Grille + headlamps proud of the nose so no face z-fights.
|
// Grille + headlamps seated INTO the bumper face (not floating
|
||||||
|
// above it): both overlap the bumper top edge.
|
||||||
const grille = add(new T.Mesh(new T.BoxGeometry(0.9, 0.16, 0.06), trimMat));
|
const grille = add(new T.Mesh(new T.BoxGeometry(0.9, 0.16, 0.06), trimMat));
|
||||||
grille.position.set(0, -0.12, -2.21);
|
grille.position.set(0, -0.2, -2.21);
|
||||||
const lampMat = new T.MeshStandardMaterial({
|
const lampMat = new T.MeshStandardMaterial({
|
||||||
color: new T.Color(0.1, 0.1, 0.12),
|
color: new T.Color(0.1, 0.1, 0.12),
|
||||||
emissive: new T.Color(0.75, 0.82, 1.0),
|
emissive: new T.Color(0.75, 0.82, 1.0),
|
||||||
@@ -603,7 +620,7 @@ export const ride = {
|
|||||||
});
|
});
|
||||||
for (const side of [-1, 1]) {
|
for (const side of [-1, 1]) {
|
||||||
const lamp = add(new T.Mesh(new T.CircleGeometry(0.09, 16), lampMat));
|
const lamp = add(new T.Mesh(new T.CircleGeometry(0.09, 16), lampMat));
|
||||||
lamp.position.set(side * 0.55, -0.05, -2.22);
|
lamp.position.set(side * 0.55, -0.16, -2.22);
|
||||||
lamp.rotation.y = Math.PI;
|
lamp.rotation.y = Math.PI;
|
||||||
}
|
}
|
||||||
// Taillight bar set into the bumper's rear face.
|
// Taillight bar set into the bumper's rear face.
|
||||||
@@ -616,12 +633,13 @@ export const ride = {
|
|||||||
roughness: 0.4,
|
roughness: 0.4,
|
||||||
})));
|
})));
|
||||||
tail.position.set(0, -0.28, 1.78);
|
tail.position.set(0, -0.28, 1.78);
|
||||||
// Mirrors on stalks at the cabin front corners.
|
// Mirrors on stalks at the cabin front corners, stalks rooted
|
||||||
|
// in the cabin wall (not floating beside it).
|
||||||
for (const side of [-1, 1]) {
|
for (const side of [-1, 1]) {
|
||||||
const stalk = add(new T.Mesh(new T.BoxGeometry(0.04, 0.12, 0.04), trimMat));
|
const stalk = add(new T.Mesh(new T.BoxGeometry(0.06, 0.12, 0.04), trimMat));
|
||||||
stalk.position.set(side * 0.9, 0.72, -0.95);
|
stalk.position.set(side * 0.88, 0.72, -0.95);
|
||||||
const mirror = add(new T.Mesh(new T.BoxGeometry(0.14, 0.09, 0.07), paintMat()));
|
const mirror = add(new T.Mesh(new T.BoxGeometry(0.14, 0.09, 0.07), paintMat()));
|
||||||
mirror.position.set(side * 0.94, 0.8, -0.95);
|
mirror.position.set(side * 0.92, 0.8, -0.95);
|
||||||
}
|
}
|
||||||
// Rear spoiler: blade on two mounts over the trunk.
|
// Rear spoiler: blade on two mounts over the trunk.
|
||||||
for (const side of [-1, 1]) {
|
for (const side of [-1, 1]) {
|
||||||
|
|||||||
@@ -56,10 +56,13 @@ export function buildSky(THREE, { radius = 90 } = {}) {
|
|||||||
// Mirrored spherical projection shared with the ride's paint
|
// Mirrored spherical projection shared with the ride's paint
|
||||||
// (see vizEnvMaterial): direction to sphere UVs, with the azimuth
|
// (see vizEnvMaterial): direction to sphere UVs, with the azimuth
|
||||||
// folded into a triangle so the ±π wrap lands on the same texel
|
// folded into a triangle so the ±π wrap lands on the same texel
|
||||||
// instead of a visible seam.
|
// instead of a visible seam. Offset a quarter turn (shared
|
||||||
|
// constant with ride.js): the fold lines sit fore and aft, where
|
||||||
|
// a mirrored join never faces the chase camera head-on — on the
|
||||||
|
// flanks it mirrored the world at the viewer.
|
||||||
vec2 skyVizUv(vec3 dir) {
|
vec2 skyVizUv(vec3 dir) {
|
||||||
float u = atan(dir.z, dir.x) * 0.15915494 + 0.5;
|
float u = atan(dir.z, dir.x) * 0.15915494 + 0.5 + 0.25;
|
||||||
u = 1.0 - abs(1.0 - 2.0 * u);
|
u = 1.0 - abs(1.0 - 2.0 * fract(u));
|
||||||
float v = asin(clamp(dir.y, -1.0, 1.0)) * 0.31830988 + 0.5;
|
float v = asin(clamp(dir.y, -1.0, 1.0)) * 0.31830988 + 0.5;
|
||||||
return vec2(u, v);
|
return vec2(u, v);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user