Lane C S13 gate 2.2: the front wall grounds on the horizon, ends feathered
Two QA sightings from in-yard eye height, both geometry, both reproduced on dev_skyfx before touching anything. (1) The band stopped AT the camera's horizontal plane, but the ground's true horizon sits ~0.6 deg below it (the dip), so a bright sky sliver showed under the base — the floating slab. The band now overshoots to ~12 deg below the equator; the ground occludes the overshoot by depth, and it survives scale.y's 0.3 rise floor. (2) The arc-end fade (sin^0.75) left the outer tenth at ~0.36 alpha — the hard vertical seam. Now sin^1.6 with the arc widened 2.2->2.6 rad so the solid core keeps its span while the ends drop under 0.12. Verified by eye on dev_skyfx (southerly t=24, front 0.51): base meets the ground line, no sliver, no seam. Selftest 338/0/0 (+1 test pinning both in numbers). Mutation-checked: the old thetaLength and the old exponent each red. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
a861151a4a
commit
c2a19df7d5
@ -656,15 +656,25 @@ export function createSkyFx(o = {}) {
|
||||
// fixed post-change instant — def + seed in, same wall out, every run.
|
||||
const FRONT_LEAD = 12; // s before the change the wall starts rising
|
||||
const FRONT_FADE = 8; // s after the swing completes for it to clear
|
||||
const FRONT_ARC = 2.2; // radians of horizon the wall spans (~126°)
|
||||
const FRONT_ARC = 2.6; // radians of horizon the wall spans (~149°)
|
||||
const FRONT_MAX_OP = 0.85;
|
||||
const frontEvents = (def.events || [])
|
||||
.filter((e) => e.type === 'windchange' && Number.isFinite(e.t))
|
||||
.map((e) => ({ t0: e.t, over: e.over ?? 6, az: null }));
|
||||
// Band from ~34° above the horizon down to it, centred (in local space) on
|
||||
// azimuth π: SphereGeometry's (x,z) = (-cosφ, sinφ)·sinθ, so φ=0 sits at
|
||||
// atan2(z,x) = π. rotation.y = π − A then carries the centre to azimuth A.
|
||||
const frontGeo = new THREE.SphereGeometry(170, 24, 8, -FRONT_ARC / 2, FRONT_ARC, Math.PI * 0.30, Math.PI * 0.20);
|
||||
// Band from ~34° above the horizon down to ~12° BELOW it, centred (in local
|
||||
// space) on azimuth π: SphereGeometry's (x,z) = (-cosφ, sinφ)·sinθ, so φ=0
|
||||
// sits at atan2(z,x) = π. rotation.y = π − A then carries the centre to A.
|
||||
//
|
||||
// SPRINT13 gate 2.2 — why the band overshoots the equator: the old band
|
||||
// stopped AT the horizontal plane through the camera, but from in-yard eye
|
||||
// height the ground's true horizon sits ~0.6° BELOW that plane (the dip),
|
||||
// so a sliver of bright sky showed under the wall's base and the bank read
|
||||
// as a floating slab — the QA sighting, reproduced on dev_skyfx before this
|
||||
// fix. Overshooting to −12° buries the base behind the ground from any eye
|
||||
// height that matters (it survives scale.y's 0.3 floor: −37 m compressed to
|
||||
// −11 m is still −3.7° elevation, below the dip). The ground plane occludes
|
||||
// the overshoot by depth, so nothing is drawn twice.
|
||||
const frontGeo = new THREE.SphereGeometry(170, 24, 10, -FRONT_ARC / 2, FRONT_ARC, Math.PI * 0.30, Math.PI * 0.27);
|
||||
{
|
||||
// Soft edges via vertex alpha, or the band reads as a floating rectangle
|
||||
// (checked by eye on dev_skyfx.html — the hard phi/theta cut was exactly
|
||||
@ -672,11 +682,15 @@ export function createSkyFx(o = {}) {
|
||||
// the arc ends and across the top, so it sits ON the horizon like a bank
|
||||
// of weather instead of hanging in the sky like a screen.
|
||||
// Sphere uv: x runs 0..1 across the arc, y is 1 at the band top, 0 at the
|
||||
// horizon edge.
|
||||
// (now sub-horizon) bottom edge.
|
||||
// The 1.6 exponent is gate 2.2's other half: 0.75 left the outer tenth of
|
||||
// the arc at ~0.36 alpha, which from in-yard eye height is the "hard
|
||||
// vertical seam" the QA saw at the bank's ends. 1.6 holds the same solid
|
||||
// core (the arc is wider to compensate) but takes the ends to <0.12.
|
||||
const uv = frontGeo.attributes.uv;
|
||||
const rgba = new Float32Array(uv.count * 4);
|
||||
for (let i = 0; i < uv.count; i++) {
|
||||
const across = Math.pow(Math.sin(Math.PI * uv.getX(i)), 0.75);
|
||||
const across = Math.pow(Math.sin(Math.PI * uv.getX(i)), 1.6);
|
||||
const up = 1 - smoothstep(0.45, 1, uv.getY(i));
|
||||
rgba[i * 4] = 1; rgba[i * 4 + 1] = 1; rgba[i * 4 + 2] = 1;
|
||||
rgba[i * 4 + 3] = across * up;
|
||||
|
||||
@ -227,6 +227,31 @@ export default async function run(t) {
|
||||
'dispose did not hand the sun back exactly (intensity / colour / shadow radius)');
|
||||
});
|
||||
|
||||
// SPRINT13 gate 2.2 — the wall's edges, judged from in-yard eye height.
|
||||
// Two QA sightings, both geometry: a sliver of bright sky under the bank's
|
||||
// base (it stopped AT the camera's horizontal plane, above the ground's true
|
||||
// horizon), and a hard vertical seam at the arc ends (0.36 alpha at the
|
||||
// outer tenth). The band now overshoots the equator and the end fade is
|
||||
// steeper; these pins are what "grounded" and "feathered" mean in numbers.
|
||||
t.test('the change-front wall grounds below the horizon and feathers its ends', () => {
|
||||
const wind = createWind(storms.storm_03_southerly);
|
||||
const sky = createSkyFx({ wind });
|
||||
const geo = sky.front.geometry;
|
||||
geo.computeBoundingBox();
|
||||
assert(geo.boundingBox.min.y < -20,
|
||||
`front base at y=${geo.boundingBox.min.y.toFixed(1)} — a band stopping at eye level floats a sky sliver under the wall`);
|
||||
const uv = geo.attributes.uv, col = geo.attributes.color;
|
||||
assert(col && col.itemSize === 4, 'front lost its vertex alpha — the edges are hard cuts again');
|
||||
let edgeMax = 0;
|
||||
for (let i = 0; i < uv.count; i++) {
|
||||
const u = uv.getX(i);
|
||||
if (u <= 0.09 || u >= 0.91) edgeMax = Math.max(edgeMax, col.getW(i));
|
||||
}
|
||||
assert(edgeMax < 0.2,
|
||||
`arc-end alpha ${edgeMax.toFixed(2)} — the vertical seam QA saw at the bank's ends`);
|
||||
sky.dispose();
|
||||
});
|
||||
|
||||
// --- SPRINT2 §Lane C.3: rain has to stop at the cloth ---
|
||||
// Driven with a synthetic 4×4 m panel rather than a whole cloth sim: the thing
|
||||
// under test is the projection, and a flat panel makes the right answer
|
||||
|
||||
Loading…
Reference in New Issue
Block a user