Upload index.html
Browse files- index.html +37 -20
index.html
CHANGED
|
@@ -119,9 +119,8 @@ function frac(v){ return v - Math.floor(v); }
|
|
| 119 |
// on is the ground it is drawn on to within about a millionth of a metre.
|
| 120 |
const F32 = v => Math.fround(v);
|
| 121 |
function lhash(x, z){ return (ihash((Math.imul(x, 3419) + Math.imul(z, 2147483647)) >>> 0) & 1023) / 1023; }
|
| 122 |
-
function lnoise(
|
| 123 |
-
const
|
| 124 |
-
const ix = Math.floor(px), iz = Math.floor(pz), fx = px - ix, fz = pz - iz;
|
| 125 |
const sx = fx*fx*(3 - 2*fx), sz = fz*fz*(3 - 2*fz);
|
| 126 |
const a = lhash(ix, iz), b = lhash(ix+1, iz), c = lhash(ix, iz+1), d = lhash(ix+1, iz+1);
|
| 127 |
const u = a + (b-a)*sx, v = c + (d-c)*sx;
|
|
@@ -143,10 +142,12 @@ function islandAt(cx, cz){
|
|
| 143 |
else if(low >= 96) return null; // most of the world is open sea
|
| 144 |
const s = ihash(h) & 0xFFFFFF;
|
| 145 |
const rb = s & 63, pk = (s >> 6) & 15, sh = (s >> 10) & 15, rg = (s >> 14) & 15;
|
| 146 |
-
const r = type === 1 ? 50 + rb*0.31 : type === 2 ? 9 + rb*0.14 : 14 + rb*0.25;
|
| 147 |
return {
|
| 148 |
-
x
|
| 149 |
-
|
|
|
|
|
|
|
| 150 |
r, code: pk + sh*16 + rg*256 + type*4096
|
| 151 |
};
|
| 152 |
}
|
|
@@ -158,7 +159,7 @@ function decode(code){
|
|
| 158 |
// straight fall over the last fraction of it, which is the beach the boat can actually sit on.
|
| 159 |
if(type === 1){ peak = 2.0 + pk*0.35; ramp = 0.10 + sh*0.04; }
|
| 160 |
else if(type === 2){ peak = 14.0 + pk*1.6; shape = 0.04 + sh*0.012; }
|
| 161 |
-
return { peak, shape, rough: rg/15, type, ramp };
|
| 162 |
}
|
| 163 |
|
| 164 |
function landH(x, z){
|
|
@@ -172,11 +173,23 @@ function landH(x, z){
|
|
| 172 |
for(let j=-span;j<=span;j++) for(let i=-span;i<=span;i++){
|
| 173 |
const isl = islandAt(cx+i, cz+j);
|
| 174 |
if(!isl) continue;
|
| 175 |
-
const d = Math.hypot(x - isl.x, z - isl.z);
|
| 176 |
-
if(d > isl.r) continue;
|
| 177 |
const p = decode(isl.code);
|
| 178 |
-
|
| 179 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 180 |
const terr = (p.ramp > 0 ? Math.min(1, k/p.ramp) : Math.pow(k, p.shape)) * nz;
|
| 181 |
h = Math.max(h, p.peak*terr - LAND.shelf); // the shelf is the shallow ring around an island
|
| 182 |
}
|
|
@@ -218,15 +231,14 @@ float fbm(vec2 p, int oct){
|
|
| 218 |
`;
|
| 219 |
|
| 220 |
const ISLANDS_GLSL = `
|
| 221 |
-
uniform vec4 uIsl[
|
| 222 |
uniform int uCount;
|
| 223 |
uint ihash(uint n){
|
| 224 |
n ^= n >> 15; n *= 0x2c1b3c9du; n ^= n >> 12; n *= 0x297a2d39u; n ^= n >> 15;
|
| 225 |
return n;
|
| 226 |
}
|
| 227 |
float lhash(int x, int y){ return float(ihash(uint(x)*3419u + uint(y)*2147483647u) & 1023u) / 1023.0; }
|
| 228 |
-
float lnoise(vec2 p){
|
| 229 |
-
p = p * 0.08; // same lattice scale as the JS copy
|
| 230 |
ivec2 i = ivec2(floor(p));
|
| 231 |
vec2 f = p - vec2(float(i.x), float(i.y));
|
| 232 |
f = f*f*(3.0-2.0*f);
|
|
@@ -237,18 +249,23 @@ float landH(vec2 p){
|
|
| 237 |
float h = -6.0; // no land in range: open water, deep enough that depth reads as deep
|
| 238 |
for(int i=0;i<uCount;i++){
|
| 239 |
vec4 isl = uIsl[i];
|
| 240 |
-
|
| 241 |
-
|
| 242 |
float code = isl.w;
|
| 243 |
float pk = mod(code, 16.0), sh = floor(mod(code/16.0, 16.0)), rg = floor(mod(code/256.0, 16.0));
|
| 244 |
float type = floor(code/4096.0);
|
| 245 |
-
float
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 246 |
if(type == 1.0){ peak = 2.0 + pk*0.35; }
|
| 247 |
else if(type == 2.0){ peak = 14.0 + pk*1.6; shape = 0.04 + sh*0.012; }
|
| 248 |
float rough = rg/15.0;
|
| 249 |
-
float k = 1.0 - d/
|
| 250 |
-
float nz = 0.55 + 0.45*rough*lnoise(
|
| 251 |
-
float terr = (
|
| 252 |
h = max(h, peak*terr - 1.2);
|
| 253 |
}
|
| 254 |
return h;
|
|
|
|
| 119 |
// on is the ground it is drawn on to within about a millionth of a metre.
|
| 120 |
const F32 = v => Math.fround(v);
|
| 121 |
function lhash(x, z){ return (ihash((Math.imul(x, 3419) + Math.imul(z, 2147483647)) >>> 0) & 1023) / 1023; }
|
| 122 |
+
function lnoise(px, pz){ // takes lattice coordinates, so the caller sets the scale
|
| 123 |
+
const ix = Math.floor(px), iz = Math.floor(pz), fx = px-ix, fz = pz-iz;
|
|
|
|
| 124 |
const sx = fx*fx*(3 - 2*fx), sz = fz*fz*(3 - 2*fz);
|
| 125 |
const a = lhash(ix, iz), b = lhash(ix+1, iz), c = lhash(ix, iz+1), d = lhash(ix+1, iz+1);
|
| 126 |
const u = a + (b-a)*sx, v = c + (d-c)*sx;
|
|
|
|
| 142 |
else if(low >= 96) return null; // most of the world is open sea
|
| 143 |
const s = ihash(h) & 0xFFFFFF;
|
| 144 |
const rb = s & 63, pk = (s >> 6) & 15, sh = (s >> 10) & 15, rg = (s >> 14) & 15;
|
| 145 |
+
const r = F32(type === 1 ? 50 + rb*0.31 : type === 2 ? 9 + rb*0.14 : 14 + rb*0.25);
|
| 146 |
return {
|
| 147 |
+
// x, z and r are stored as float32 values because that is what the shader receives. Otherwise
|
| 148 |
+
// the two sides can land on different lattice cells and disagree about the island's shape.
|
| 149 |
+
x: F32((cx + 0.5 + ((h >>> 8 & 255) - 127.5)/256) * ICELL),
|
| 150 |
+
z: F32((cz + 0.5 + ((h >>> 16 & 255) - 127.5)/256) * ICELL),
|
| 151 |
r, code: pk + sh*16 + rg*256 + type*4096
|
| 152 |
};
|
| 153 |
}
|
|
|
|
| 159 |
// straight fall over the last fraction of it, which is the beach the boat can actually sit on.
|
| 160 |
if(type === 1){ peak = 2.0 + pk*0.35; ramp = 0.10 + sh*0.04; }
|
| 161 |
else if(type === 2){ peak = 14.0 + pk*1.6; shape = 0.04 + sh*0.012; }
|
| 162 |
+
return { peak, shape, rough: F32(rg/15), type, ramp, pk, sh, rg };
|
| 163 |
}
|
| 164 |
|
| 165 |
function landH(x, z){
|
|
|
|
| 173 |
for(let j=-span;j<=span;j++) for(let i=-span;i<=span;i++){
|
| 174 |
const isl = islandAt(cx+i, cz+j);
|
| 175 |
if(!isl) continue;
|
|
|
|
|
|
|
| 176 |
const p = decode(isl.code);
|
| 177 |
+
// Nothing here is a circle. Two noise lattices are sized to the island itself: one gives the
|
| 178 |
+
// outline its lobes, the other the surface its hollows and peaks. Both are sampled relative to
|
| 179 |
+
// the island centre so the lattice coordinates stay small, every step is rounded to float32,
|
| 180 |
+
// and the offsets are small integers - otherwise the two sides pick different lattice cells and
|
| 181 |
+
// disagree about the shape, which is what would put the boat on a different height than the
|
| 182 |
+
// ground it is being drawn on.
|
| 183 |
+
const gx = F32(x), gz = F32(z);
|
| 184 |
+
const f1 = F32(F32(2 + Math.floor(p.rg/4)) / isl.r);
|
| 185 |
+
const f2 = F32(F32(6 + p.rg) / isl.r);
|
| 186 |
+
const dx = F32(gx - isl.x), dz = F32(gz - isl.z);
|
| 187 |
+
const d = F32(Math.sqrt(F32(F32(dx)*F32(dx)) + F32(F32(dz)*F32(dz)))); // what length() does in the shader
|
| 188 |
+
const wob = lnoise(F32(F32(dx*f1) + F32(p.pk)), F32(F32(dz*f1) + F32(p.sh)));
|
| 189 |
+
const rr = F32(isl.r * F32(F32(0.55) + F32(F32(0.45)*F32(wob))));
|
| 190 |
+
if(d > rr) continue;
|
| 191 |
+
const k = F32(F32(1.0) - F32(F32(d)/F32(rr)));
|
| 192 |
+
const nz = F32(F32(0.55) + F32(F32(0.45)*F32(p.rough)*lnoise(F32(F32(dx*f2) + F32((p.pk+p.sh+1)%16)), F32(F32(dz*f2) + F32((p.pk+p.sh+7)%16)))));
|
| 193 |
const terr = (p.ramp > 0 ? Math.min(1, k/p.ramp) : Math.pow(k, p.shape)) * nz;
|
| 194 |
h = Math.max(h, p.peak*terr - LAND.shelf); // the shelf is the shallow ring around an island
|
| 195 |
}
|
|
|
|
| 231 |
`;
|
| 232 |
|
| 233 |
const ISLANDS_GLSL = `
|
| 234 |
+
uniform vec4 uIsl[48]; // must match MAX_ISLANDS, otherwise islands past 25 are silently dropped
|
| 235 |
uniform int uCount;
|
| 236 |
uint ihash(uint n){
|
| 237 |
n ^= n >> 15; n *= 0x2c1b3c9du; n ^= n >> 12; n *= 0x297a2d39u; n ^= n >> 15;
|
| 238 |
return n;
|
| 239 |
}
|
| 240 |
float lhash(int x, int y){ return float(ihash(uint(x)*3419u + uint(y)*2147483647u) & 1023u) / 1023.0; }
|
| 241 |
+
float lnoise(vec2 p){ // takes lattice coordinates, so the caller sets the scale
|
|
|
|
| 242 |
ivec2 i = ivec2(floor(p));
|
| 243 |
vec2 f = p - vec2(float(i.x), float(i.y));
|
| 244 |
f = f*f*(3.0-2.0*f);
|
|
|
|
| 249 |
float h = -6.0; // no land in range: open water, deep enough that depth reads as deep
|
| 250 |
for(int i=0;i<uCount;i++){
|
| 251 |
vec4 isl = uIsl[i];
|
| 252 |
+
vec2 r = p - isl.xy; // island-relative, exactly like the JS copy
|
| 253 |
+
float d = length(r);
|
| 254 |
float code = isl.w;
|
| 255 |
float pk = mod(code, 16.0), sh = floor(mod(code/16.0, 16.0)), rg = floor(mod(code/256.0, 16.0));
|
| 256 |
float type = floor(code/4096.0);
|
| 257 |
+
float f1 = (2.0 + floor(rg/4.0)) / isl.z;
|
| 258 |
+
float f2 = (6.0 + rg) / isl.z;
|
| 259 |
+
float wob = lnoise(r*f1 + vec2(pk, sh));
|
| 260 |
+
float rr = isl.z * (0.55 + 0.45*wob);
|
| 261 |
+
if(d > rr) continue;
|
| 262 |
+
float peak = 1.2 + pk*0.22, shape = 1.2 + sh*0.03, ramp = 0.0;
|
| 263 |
if(type == 1.0){ peak = 2.0 + pk*0.35; }
|
| 264 |
else if(type == 2.0){ peak = 14.0 + pk*1.6; shape = 0.04 + sh*0.012; }
|
| 265 |
float rough = rg/15.0;
|
| 266 |
+
float k = 1.0 - d/rr;
|
| 267 |
+
float nz = 0.55 + 0.45*rough*lnoise(r*f2 + vec2(mod(pk+sh+1.0, 16.0), mod(pk+sh+7.0, 16.0)));
|
| 268 |
+
float terr = (ramp > 0.0) ? min(1.0, k/ramp) * nz : pow(k, shape) * nz;
|
| 269 |
h = max(h, peak*terr - 1.2);
|
| 270 |
}
|
| 271 |
return h;
|