mapspinner 0.1.31 → 0.1.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/planet.html +8 -10
- package/src/gl-render.js +13 -4
- package/src/shaders/terrain.glsl +55 -1
package/package.json
CHANGED
package/planet.html
CHANGED
|
@@ -477,9 +477,9 @@ function frameLoop(){
|
|
|
477
477
|
// take the existing CPU-mirror fallback. Below 20km (where collision is load-bearing) keep GPU-exact.
|
|
478
478
|
const _altRoughKm = _camLenMv - RADIUS;
|
|
479
479
|
const _gpuGM = (_altRoughKm < 20.0 && _orchMv && _orchMv.render && _orchMv.render.sampleGroundM) ? _orchMv.render.sampleGroundM(_camDirMv) : null;
|
|
480
|
-
const _surfM = (_gpuGM != null && isFinite(_gpuGM)) ?
|
|
481
|
-
:
|
|
482
|
-
const altSurfElev = Math.
|
|
480
|
+
const _surfM = (_gpuGM != null && isFinite(_gpuGM)) ? _gpuGM
|
|
481
|
+
: jsBroadShape(cam.pos[0],cam.pos[1],cam.pos[2]);
|
|
482
|
+
const altSurfElev = Math.min(_surfM, 9500.0) / WEBGL2_TERRAIN_R_M;
|
|
483
483
|
const altKmAbove = Math.max(1e-3, (Math.hypot(...cam.pos) - RADIUS*(1.0 + altSurfElev)));
|
|
484
484
|
// CAMERA MOVE-STEP. NO ELEVATION SLOWDOWN (user 2026-06-05: 'get rid of the camera slowdown code').
|
|
485
485
|
// The step scales with altitude-above-surface ONLY so the scene still moves at orbit (a constant step
|
|
@@ -522,14 +522,12 @@ function frameLoop(){
|
|
|
522
522
|
// a height above an inflated reference -> the eye was really ground+margin+2m, not 2m.) The mirror
|
|
523
523
|
// fallback keeps a small +3m only because the CPU jsBroadShape can under-predict vs the GPU.
|
|
524
524
|
if (gpuM != null && isFinite(gpuM)) {
|
|
525
|
-
renderedM =
|
|
525
|
+
renderedM = gpuM; // GPU-exact rendered height (may be negative = seabed)
|
|
526
526
|
} else {
|
|
527
|
-
renderedM =
|
|
527
|
+
renderedM = jsBroadShape(cam.pos[0], cam.pos[1], cam.pos[2]) + 3.0; // mirror fallback (CPU under-predicts)
|
|
528
528
|
}
|
|
529
|
-
// cap
|
|
530
|
-
|
|
531
|
-
// BELOW visible 11-15km peaks -> tunnel through the rendered geometry (collision-elev-margin, wb4syopmo).
|
|
532
|
-
const surfElev = Math.max(0.0, Math.min(renderedM, 15000.0)) / WEBGL2_TERRAIN_R_M; // fraction of R (match CMAX)
|
|
529
|
+
// cap to composeHeight's CMAX=15000; allow negative for seabed
|
|
530
|
+
const surfElev = Math.min(renderedM, 15000.0) / WEBGL2_TERRAIN_R_M;
|
|
533
531
|
const eyeHeight = 2.0 / WEBGL2_M_PER_UNIT; // EXACTLY 2.0 m above ground (km-units = 2m / m-per-unit)
|
|
534
532
|
const minR = RADIUS * (1.0 + surfElev) + eyeHeight;
|
|
535
533
|
if (r < minR) { const s = minR/r; cam.pos[0]*=s; cam.pos[1]*=s; cam.pos[2]*=s; }
|
|
@@ -537,7 +535,7 @@ function frameLoop(){
|
|
|
537
535
|
if (r > maxR) { const s = maxR/r; cam.pos[0]*=s; cam.pos[1]*=s; cam.pos[2]*=s; }
|
|
538
536
|
|
|
539
537
|
// height of the eye above the local ground, in metres (for the HUD readout).
|
|
540
|
-
const heightAboveGroundM = (Math.hypot(...cam.pos) - RADIUS*(1.0 +
|
|
538
|
+
const heightAboveGroundM = (Math.hypot(...cam.pos) - RADIUS*(1.0 + surfElev)) * 1000;
|
|
541
539
|
window.__altM = heightAboveGroundM;
|
|
542
540
|
|
|
543
541
|
// The camera (autopilot + free-fly + clamp) has now been fully updated this frame,
|
package/src/gl-render.js
CHANGED
|
@@ -799,7 +799,7 @@ export async function initMapspinnerRender(gl, opts = {}) {
|
|
|
799
799
|
gl.uniform1f(U('uTexTileM'), _g('texTile', 2400.0)); // metres per repeat (user: 24m read as noise/rock -- 100x bigger)
|
|
800
800
|
gl.uniform1f(U('uTexNrmK'), _g('texNrmK', 1.5)); // 0.8 -> 3.0 (user 2026-06-11 'displacement normals must be THE texture normals'), 3.0->1.5 (2026-06-13 'texture displacement far too obvious vs elevation')
|
|
801
801
|
gl.uniform1f(U('uTexMix'), _g('texMix', 0.85)); // splat blend amount (0 = off)
|
|
802
|
-
gl.uniform1f(U('uTexWarp'), _g('texWarp', 0.
|
|
802
|
+
gl.uniform1f(U('uTexWarp'), _g('texWarp', 0.23)); // anti-repetition warp amplitude (-30% from 0.325, grass warp too intense)
|
|
803
803
|
gl.uniform1f(U('uTexPhoto'), _g('texPhoto', 0.0)); // raw photo-color fraction (0 = patch matches the macro shade exactly)
|
|
804
804
|
gl.uniform1f(U('uTexPhotoNear'), _g('texPhotoNear', 0.45)); // near-field material identity (photo hue at macro luminance; user 2026-06-12 'must be either grass or sand')
|
|
805
805
|
gl.uniform4f(U('uSurfMeanL'), _surfMeanL[0], _surfMeanL[1], _surfMeanL[2], _surfMeanL[3]); // per-layer mean linear luminance (shade-match divisor)
|
|
@@ -870,6 +870,10 @@ export async function initMapspinnerRender(gl, opts = {}) {
|
|
|
870
870
|
gl.enableVertexAttribArray(1); gl.vertexAttribPointer(1, 4, gl.FLOAT, false, STRIDE, 0); gl.vertexAttribDivisor(1, 1); // iOffset
|
|
871
871
|
gl.enableVertexAttribArray(2); gl.vertexAttribPointer(2, 1, gl.FLOAT, false, STRIDE, 4 * 4); gl.vertexAttribDivisor(2, 1); // iFace
|
|
872
872
|
gl.uniform1f(U('uIsWater'), 0.0);
|
|
873
|
+
// UNDERWATER DETECTION: camera below sea level enables underwater shading + water surface
|
|
874
|
+
// rendering from below. Set before the terrain draw so the FS can apply underwater fog.
|
|
875
|
+
const _uw = camDist < R - 2.0;
|
|
876
|
+
gl.uniform1f(U('uUnderwater'), _uw ? 1.0 : 0.0);
|
|
873
877
|
gl.drawElementsInstanced(gl.TRIANGLES, indices.length, gl.UNSIGNED_INT, 0, n);
|
|
874
878
|
// SEPARATE WATER SURFACE (user 2026-06-11): second instanced draw with uIsWater=1 -- the VS
|
|
875
879
|
// pins the mesh to sea level, the FS shades animated water and alpha-blends it over the
|
|
@@ -901,9 +905,14 @@ export async function initMapspinnerRender(gl, opts = {}) {
|
|
|
901
905
|
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(wl), gl.DYNAMIC_DRAW);
|
|
902
906
|
gl.enableVertexAttribArray(1); gl.vertexAttribPointer(1, 4, gl.FLOAT, false, STRIDE, 0); gl.vertexAttribDivisor(1, 1);
|
|
903
907
|
gl.enableVertexAttribArray(2); gl.vertexAttribPointer(2, 1, gl.FLOAT, false, STRIDE, 4 * 4); gl.vertexAttribDivisor(2, 1);
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
908
|
+
if (_uw) {
|
|
909
|
+
gl.disable(gl.BLEND);
|
|
910
|
+
gl.depthMask(true);
|
|
911
|
+
} else {
|
|
912
|
+
gl.enable(gl.BLEND);
|
|
913
|
+
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
914
|
+
gl.depthMask(false);
|
|
915
|
+
}
|
|
907
916
|
gl.uniform1f(U('uIsWater'), 1.0);
|
|
908
917
|
gl.drawElementsInstanced(gl.TRIANGLES, indices.length, gl.UNSIGNED_INT, 0, wn);
|
|
909
918
|
gl.uniform1f(U('uIsWater'), 0.0);
|
package/src/shaders/terrain.glsl
CHANGED
|
@@ -206,6 +206,7 @@ uniform float uVertexAO; // per-vertex shading/AO strength lev
|
|
|
206
206
|
// blended over the seabed). One program + a uniform flag = no second cold compile, no duplicated
|
|
207
207
|
// per-frame uniform churn, and the branch is uniform-coherent (free on the GPU).
|
|
208
208
|
uniform float uIsWater; // 0 = terrain pass, 1 = water-surface pass
|
|
209
|
+
uniform float uUnderwater; // 0 = camera above water, 1 = camera below sea level
|
|
209
210
|
uniform float uBeachTopM; // beach ceiling (m): below this, grass/snow yield to sand (window.__beachTop; 30 default)
|
|
210
211
|
uniform float uHiFreqCut; // hi-freq elevation-noise attenuation, applied to ALL hi-freq sources:
|
|
211
212
|
// broadShapeM/MD fine octaves (o>=6) + vtxDisplace micro-relief
|
|
@@ -1452,6 +1453,34 @@ void main() {
|
|
|
1452
1453
|
// none of the terrain material/atmosphere work below runs for water fragments.
|
|
1453
1454
|
if (uIsWater > 0.5) {
|
|
1454
1455
|
if (vH > 1.0) discard; // surface under land: depth test culls it anyway; discard kills shoreline shimmer
|
|
1456
|
+
// UNDERWATER WATER SURFACE (camera below sea level): render the surface from below as
|
|
1457
|
+
// opaque deep blue with Gerstner wave animation. No sky reflection, no Fresnel (TIR from
|
|
1458
|
+
// below = mostly opaque deep water), no Beer-Lambert (there is no seabed above the camera).
|
|
1459
|
+
if (uUnderwater > 0.5) {
|
|
1460
|
+
highp vec3 wOriginW = floor(camWorld / 1024.0) * 1024.0;
|
|
1461
|
+
highp vec2 wpW = vec2(dot(vWorld - wOriginW, ux), dot(vWorld - wOriginW, uy));
|
|
1462
|
+
vec2 slopeW = oceanWaveSlope(wpW, oceanTime);
|
|
1463
|
+
highp float wDistW = length(camWorld - vWorld);
|
|
1464
|
+
slopeW *= clamp(1.0 - wDistW / 4000.0, 0.0, 1.0);
|
|
1465
|
+
vec3 wn = normalize(uz - ux * slopeW.x - uy * slopeW.y);
|
|
1466
|
+
vec3 viewW = normalize(camWorld - vWorld);
|
|
1467
|
+
float ndl = max(dot(wn, sunDir), 0.0);
|
|
1468
|
+
// Sunlight attenuated through the water column; deep blue ambient
|
|
1469
|
+
float depthAtten = exp(-max(0.0, terrainR - length(camWorld)) * 0.0005);
|
|
1470
|
+
vec3 sunUnder = vec3(1.0, 0.6, 0.3) * depthAtten * ndl;
|
|
1471
|
+
vec3 deepBlue = vec3(0.005, 0.06, 0.18);
|
|
1472
|
+
vec3 waveBright = vec3(0.0, 0.02, 0.06) * length(slopeW);
|
|
1473
|
+
vec3 wcol = deepBlue + sunUnder * 0.4 + waveBright;
|
|
1474
|
+
float macroMuW = dot(uz, sunDir);
|
|
1475
|
+
float dayShadeW = mix(uNightFloor, 1.0, smoothstep(-uTermWidth, uTermWidth, macroMuW));
|
|
1476
|
+
vec3 cW = wcol * dayShadeW * uExposure;
|
|
1477
|
+
vec3 mappedW = clamp((cW * (2.51 * cW + 0.03)) / (cW * (2.43 * cW + 0.59) + 0.14), 0.0, 1.0);
|
|
1478
|
+
float lumW = dot(mappedW, vec3(0.2126, 0.7152, 0.0722));
|
|
1479
|
+
mappedW = mix(vec3(lumW), mappedW, uLookSat);
|
|
1480
|
+
mappedW = clamp((mappedW - 0.5) * uLookContrast + 0.5, 0.0, 1.0);
|
|
1481
|
+
fragColor = vec4(pow(mappedW, vec3(1.0 / 2.2)), 1.0);
|
|
1482
|
+
return;
|
|
1483
|
+
}
|
|
1455
1484
|
highp float depthM = max(-vH, 0.0);
|
|
1456
1485
|
highp vec3 wOriginW = floor(camWorld / 1024.0) * 1024.0; // snapped anchor (fp32 wave-phase fix, same as the old branch)
|
|
1457
1486
|
highp vec2 wpW = vec2(dot(vWorld - wOriginW, ux), dot(vWorld - wOriginW, uy));
|
|
@@ -1840,7 +1869,18 @@ void main() {
|
|
|
1840
1869
|
// blended over this seabed. Sea ice still keys the seabed albedo near the poles.
|
|
1841
1870
|
|
|
1842
1871
|
vec3 skyIrr;
|
|
1843
|
-
vec3 sunIrr
|
|
1872
|
+
vec3 sunIrr;
|
|
1873
|
+
if (uUnderwater > 0.5) {
|
|
1874
|
+
// Underwater terrain (seabed): sun attenuated through water column, blue ambient, no
|
|
1875
|
+
// atmospheric Rayleigh/Mie scattering. Sun colour shifts toward green-blue with depth.
|
|
1876
|
+
float depth = max(0.0, terrainR - length(camWorld));
|
|
1877
|
+
float atten = exp(-depth * 0.0004);
|
|
1878
|
+
float ndl = max(dot(nAtm, sunDir), 0.0);
|
|
1879
|
+
sunIrr = vec3(1.0, 0.65, 0.35) * atten * ndl * 0.7;
|
|
1880
|
+
skyIrr = vec3(0.02, 0.07, 0.18);
|
|
1881
|
+
} else {
|
|
1882
|
+
sunIrr = atm_sunSkyIrradiance(pAtm, nAtm, sunDir, skyIrr);
|
|
1883
|
+
}
|
|
1844
1884
|
// The Rayleigh sky irradiance is strongly blue; at full strength it tints the lit
|
|
1845
1885
|
// CONTINENTS blue-grey from orbit (witnessed: lit mean [83,95,112] vs warm albedo
|
|
1846
1886
|
// [90,83,74] at 2000km -- the blue is the sky-irradiance term, NOT aerial inscatter).
|
|
@@ -1987,6 +2027,20 @@ void main() {
|
|
|
1987
2027
|
color = mix(lit, hazed, apGate * uHazeMul); // uHazeMul lever (2026-06-10 'pale hazy': full-strength haze milked the midground)
|
|
1988
2028
|
}
|
|
1989
2029
|
}
|
|
2030
|
+
// UNDERWATER FOG (camera below sea level): replace the air-based Aerial Perspective with
|
|
2031
|
+
// absorption/scattering through water. Fog colour deepens with camera depth so the seabed
|
|
2032
|
+
// fades to blue-green with distance, red absorbed first. Applied only to terrain (uIsWater<0.5).
|
|
2033
|
+
if (uUnderwater > 0.5 && uIsWater < 0.5) {
|
|
2034
|
+
highp vec3 camA = atmPos(camWorld, terrainR);
|
|
2035
|
+
highp vec3 segKm = pAtm - camA;
|
|
2036
|
+
highp float dKm = length(segKm);
|
|
2037
|
+
float depth = max(0.0, terrainR - length(camWorld));
|
|
2038
|
+
// Water absorption coefficients (per km): red attenuates fastest, blue slowest.
|
|
2039
|
+
vec3 absorb = vec3(4.0, 0.8, 0.3) * (1.0 + depth * 0.0003);
|
|
2040
|
+
vec3 uwTrans = exp(-absorb * dKm);
|
|
2041
|
+
vec3 uwFog = vec3(0.002, 0.06, 0.16) + vec3(0.0, 0.02, 0.04) * depth / 1000.0;
|
|
2042
|
+
color = mix(color * uwTrans + uwFog * (1.0 - uwTrans), uwFog, smoothstep(50.0, 500.0, dKm * 1000.0));
|
|
2043
|
+
}
|
|
1990
2044
|
// RIVERS post-lighting (witnessed browser-2115/2118: the river-blue in ALBEDO is multiplied
|
|
1991
2045
|
// by the warm sun irradiance (sunIrr.b is low) so land rivers lose their blue in the lit
|
|
1992
2046
|
// result -- 15k blue albedo px -> 0 lit px, even with the sun HIGH (sunDotUp 0.85). Ocean
|