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 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 );
|
||||
// quarter-turn offset puts the fold lines fore and aft.
|
||||
float sU = atan( rDir.z, rDir.x ) * 0.15915494 + 0.5 + 0.25;
|
||||
sU = 1.0 - abs( 1.0 - 2.0 * fract( sU ) );
|
||||
vec2 sUv = vec2(
|
||||
sU,
|
||||
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);
|
||||
const bumperR = add(new T.Mesh(new T.BoxGeometry(1.8, 0.22, 0.3), trimMat));
|
||||
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]) {
|
||||
const skirt = add(new T.Mesh(new T.BoxGeometry(0.12, 0.14, 2.0), trimMat));
|
||||
skirt.position.set(side * 0.93, -0.5, -0.15);
|
||||
const skirt = add(new T.Mesh(new T.BoxGeometry(0.12, 0.5, 2.0), trimMat));
|
||||
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..π
|
||||
// spans the upper half in XY; yawed 90° it arches across the
|
||||
// wheel in the rolling plane.
|
||||
@@ -592,9 +608,10 @@ export const ride = {
|
||||
fender.rotation.y = Math.PI / 2;
|
||||
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));
|
||||
grille.position.set(0, -0.12, -2.21);
|
||||
grille.position.set(0, -0.2, -2.21);
|
||||
const lampMat = new T.MeshStandardMaterial({
|
||||
color: new T.Color(0.1, 0.1, 0.12),
|
||||
emissive: new T.Color(0.75, 0.82, 1.0),
|
||||
@@ -603,7 +620,7 @@ export const ride = {
|
||||
});
|
||||
for (const side of [-1, 1]) {
|
||||
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;
|
||||
}
|
||||
// Taillight bar set into the bumper's rear face.
|
||||
@@ -616,12 +633,13 @@ export const ride = {
|
||||
roughness: 0.4,
|
||||
})));
|
||||
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]) {
|
||||
const stalk = add(new T.Mesh(new T.BoxGeometry(0.04, 0.12, 0.04), trimMat));
|
||||
stalk.position.set(side * 0.9, 0.72, -0.95);
|
||||
const stalk = add(new T.Mesh(new T.BoxGeometry(0.06, 0.12, 0.04), trimMat));
|
||||
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()));
|
||||
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.
|
||||
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
|
||||
// (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.
|
||||
// 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) {
|
||||
float u = atan(dir.z, dir.x) * 0.15915494 + 0.5;
|
||||
u = 1.0 - abs(1.0 - 2.0 * u);
|
||||
float u = atan(dir.z, dir.x) * 0.15915494 + 0.5 + 0.25;
|
||||
u = 1.0 - abs(1.0 - 2.0 * fract(u));
|
||||
float v = asin(clamp(dir.y, -1.0, 1.0)) * 0.31830988 + 0.5;
|
||||
return vec2(u, v);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user