summaryrefslogtreecommitdiff
path: root/serve/index.html
diff options
context:
space:
mode:
authorprospanclaudebot <[email protected]>2026-07-10 22:57:38 +0000
committerprospanclaudebot <[email protected]>2026-07-10 22:57:38 +0000
commita9b41742dabf427b3e8d5d4cba0803c9dbe77f20 (patch)
tree1db77b4f0c1059de33308c72f26bb25c6ba87b6a /serve/index.html
parent82be5d249c394460081370f1d033a93f32ced492 (diff)
downloadplaneta-zhopa-a9b41742dabf427b3e8d5d4cba0803c9dbe77f20.tar.gz
planeta-zhopa-a9b41742dabf427b3e8d5d4cba0803c9dbe77f20.zip
textures: гёрстнер-океан (вода) + fbm normal map (земля)
Diffstat (limited to 'serve/index.html')
-rw-r--r--serve/index.html26
1 files changed, 21 insertions, 5 deletions
diff --git a/serve/index.html b/serve/index.html
index f8ce0f2..bcc5f17 100644
--- a/serve/index.html
+++ b/serve/index.html
@@ -1243,8 +1243,22 @@ function roughTex(freq, rmin, rmax) {
}
g.putImageData(im, 0, 0); const t = new THREE.CanvasTexture(cv); t.encoding = THREE.LinearEncoding; t.needsUpdate = true; return t;
}
+function normalTex(freq, strength) { // нормал-карта из fbm-шума -> микрорельеф земли в затенении
+ const Sz = 512, cv = document.createElement('canvas'); cv.width = cv.height = Sz;
+ const g = cv.getContext('2d'), im = g.createImageData(Sz, Sz);
+ const H = (i, j) => { const wx = (i / Sz - 0.5) * TERR * 2, wz = (j / Sz - 0.5) * TERR * 2; return fbm(wx * freq + 11.1, wz * freq + 5.5) + 0.5 * fbm(wx * freq * 3.1 + 2.0, wz * freq * 3.1); };
+ for (let j = 0; j < Sz; j++) for (let i = 0; i < Sz; i++) {
+ const hL = H((i - 1 + Sz) % Sz, j), hR = H((i + 1) % Sz, j), hD = H(i, (j - 1 + Sz) % Sz), hU = H(i, (j + 1) % Sz);
+ let nx = (hL - hR) * strength, ny = (hD - hU) * strength, nz = 1.0;
+ const L = Math.hypot(nx, ny, nz) || 1; nx /= L; ny /= L; nz /= L;
+ const k = (j * Sz + i) * 4;
+ im.data[k] = Math.round((nx * 0.5 + 0.5) * 255); im.data[k + 1] = Math.round((ny * 0.5 + 0.5) * 255); im.data[k + 2] = Math.round((nz * 0.5 + 0.5) * 255); im.data[k + 3] = 255;
+ }
+ g.putImageData(im, 0, 0); const t = new THREE.CanvasTexture(cv); t.encoding = THREE.LinearEncoding; t.needsUpdate = true; return t;
+}
const terrain = new THREE.Mesh(tgeo, new THREE.MeshStandardMaterial({
- vertexColors: true, roughness: 1.0, metalness: 0.0, roughnessMap: roughTex(0.02, 0.42, 0.96) // land: rough, but varied
+ vertexColors: true, roughness: 1.0, metalness: 0.0, roughnessMap: roughTex(0.02, 0.42, 0.96), // land: rough, but varied
+ normalMap: normalTex(0.06, 2.4), normalScale: new THREE.Vector2(0.8, 0.8) // микрорельеф из fbm-шума -> земля не плоская
}));
terrain.receiveShadow = true;
scene.add(terrain);
@@ -1256,13 +1270,15 @@ const waterMat = new THREE.MeshStandardMaterial({ color: MAP.water, roughness: 1
waterMat.onBeforeCompile = (sh) => { // fractal-sine waves: displaces the surface + ripples the normal (so LTC/reflections shimmer)
sh.uniforms.uTime = waterTime;
const wfn = 'uniform float uTime;\n' +
- 'float wv(vec2 p){ float w=0.0,a=1.0,f=0.045; vec2 d=vec2(1.0,0.35);' +
- ' for(int i=0;i<4;i++){ w+=a*sin(dot(normalize(d),p)*f+uTime*(0.9+float(i)*0.35)); a*=0.5; f*=1.92; d=vec2(d.x*0.6-d.y*0.8,d.x*0.8+d.y*0.6); } return w; }\n';
+ 'float oceanH(vec2 p){ float h=0.0,A=1.25,L=95.0; vec2 d=vec2(1.0,0.28);' +
+ ' for(int i=0;i<6;i++){ vec2 dd=normalize(d); float k=6.2831853/L; float c=sqrt(9.8/k); h+=A*sin(dot(dd,p)*k+uTime*c*0.14); A*=0.62; L*=0.62; d=vec2(d.x*0.6-d.y*0.8,d.x*0.8+d.y*0.6); } return h; }\n' +
+ 'vec3 oceanG(vec2 p){ vec3 o=vec3(0.0); float A=1.7,L=95.0; vec2 d=vec2(1.0,0.28);' +
+ ' for(int i=0;i<6;i++){ vec2 dd=normalize(d); float k=6.2831853/L; float c=sqrt(9.8/k); float f=dot(dd,p)*k+uTime*c*0.14; float Q=0.6/(k*A*6.0); o.x+=Q*A*dd.x*cos(f); o.y+=Q*A*dd.y*cos(f); o.z+=A*sin(f); A*=0.62; L*=0.62; d=vec2(d.x*0.6-d.y*0.8,d.x*0.8+d.y*0.6); } return o; }\n';
sh.vertexShader = wfn + sh.vertexShader;
sh.vertexShader = sh.vertexShader.replace('#include <beginnormal_vertex>',
- '#include <beginnormal_vertex>\n float e=1.5; float wx=wv(position.xy+vec2(e,0.0))-wv(position.xy-vec2(e,0.0)); float wy=wv(position.xy+vec2(0.0,e))-wv(position.xy-vec2(0.0,e)); objectNormal=normalize(vec3(-wx,-wy,2.0*e));');
+ '#include <beginnormal_vertex>\n float e=1.6; float wx=oceanH(position.xy+vec2(e,0.0))-oceanH(position.xy-vec2(e,0.0)); float wy=oceanH(position.xy+vec2(0.0,e))-oceanH(position.xy-vec2(0.0,e)); objectNormal=normalize(vec3(-wx,-wy,2.0*e));');
sh.vertexShader = sh.vertexShader.replace('#include <begin_vertex>',
- '#include <begin_vertex>\n transformed.z += wv(position.xy)*0.8;');
+ '#include <begin_vertex>\n vec3 og=oceanG(position.xy); transformed.x+=og.x; transformed.y+=og.y; transformed.z+=og.z;');
};
const water = new THREE.Mesh(new THREE.PlaneGeometry(TERR * 2, TERR * 2, 110, 110), waterMat);
water.rotation.x = -Math.PI / 2; water.position.y = WATER_Y; scene.add(water);