[gemini] Add procedural cloud layer below floating island (#242) #314
123
app.js
123
app.js
@@ -350,6 +350,126 @@ const perlin = createPerlinNoise();
|
||||
scene.add(islandGroup);
|
||||
})();
|
||||
|
||||
// === PROCEDURAL CLOUD LAYER ===
|
||||
// A volumetric cloud layer below the island, using a custom shader with Perlin noise.
|
||||
|
||||
const CLOUD_LAYER_Y = -6.0;
|
||||
const CLOUD_DIMENSIONS = 120;
|
||||
const CLOUD_THICKNESS = 15;
|
||||
const CLOUD_OPACITY = 0.6;
|
||||
|
||||
const cloudGeometry = new THREE.BoxGeometry(CLOUD_DIMENSIONS, CLOUD_THICKNESS, CLOUD_DIMENSIONS, 8, 4, 8);
|
||||
|
||||
const CloudShader = {
|
||||
uniforms: {
|
||||
'uTime': { value: 0.0 },
|
||||
'uCloudColor': { value: new THREE.Color(0x88bbff) },
|
||||
'uNoiseScale': { value: new THREE.Vector3(0.015, 0.015, 0.015) },
|
||||
'uDensity': { value: 0.8 },
|
||||
},
|
||||
vertexShader: `
|
||||
varying vec3 vWorldPosition;
|
||||
void main() {
|
||||
vWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
||||
}
|
||||
`,
|
||||
fragmentShader: `
|
||||
uniform float uTime;
|
||||
uniform vec3 uCloudColor;
|
||||
uniform vec3 uNoiseScale;
|
||||
uniform float uDensity;
|
||||
varying vec3 vWorldPosition;
|
||||
|
||||
// Perlin noise function (simplified, 3D)
|
||||
// Source: https://thebookofshaders.com/11/
|
||||
vec3 mod289(vec3 x) { return x - floor(x / 289.0) * 289.0; }
|
||||
vec4 mod289(vec4 x) { return x - floor(x / 289.0) * 289.0; }
|
||||
vec4 permute(vec4 x) { return mod289(((x*34.0)+1.0)*x); }
|
||||
vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; }
|
||||
float snoise(vec3 v) {
|
||||
const vec2 C = vec2(1.0/6.0, 1.0/3.0);
|
||||
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
|
||||
vec3 i = floor(v + dot(v, C.yyy));
|
||||
vec3 x0 = v - i + dot(i, C.xxx);
|
||||
vec3 g = step(x0.yzx, x0.xyz);
|
||||
vec3 l = 1.0 - g;
|
||||
vec3 i1 = min(g.xyz, l.zxy);
|
||||
vec3 i2 = max(g.xyz, l.zxy);
|
||||
vec3 x1 = x0 - i1 + C.xxx;
|
||||
vec3 x2 = x0 - i2 + C.yyy;
|
||||
vec3 x3 = x0 - D.yyy;
|
||||
i = mod289(i);
|
||||
vec4 p = permute( permute( permute(
|
||||
i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
|
||||
+ i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
|
||||
+ i.x + vec4(0.0, i1.x, i2.x, 1.0 ));
|
||||
float n_ = 0.142857142857;
|
||||
vec3 ns = n_ * D.wyz - D.xzx;
|
||||
vec4 j = p - 49.0 * floor(p * ns.z * ns.z);
|
||||
vec4 x_ = floor(j * ns.z);
|
||||
vec4 y_ = floor(j - 7.0 * x_ );
|
||||
vec4 h = 1.0 - abs(x_ - ns.x) - abs(y_ - ns.y);
|
||||
vec4 b0 = vec4(x_.xy, y_.xy);
|
||||
vec4 b1 = vec4(x_.zw, y_.zw);
|
||||
vec4 s0 = floor(b0)*2.0 + 1.0;
|
||||
vec4 s1 = floor(b1)*2.0 + 1.0;
|
||||
vec4 sh = -step(h, vec4(0.0));
|
||||
vec4 a0 = b0 - floor(b0);
|
||||
vec4 a1 = b1 - floor(b1);
|
||||
vec3 p0 = vec3(a0.xy, h.x);
|
||||
vec3 p1 = vec3(a1.xy, h.y);
|
||||
vec3 p2 = vec3(a0.zw, h.z);
|
||||
vec3 p3 = vec3(a1.zw, h.w);
|
||||
vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2,p2), dot(p3,p3)));
|
||||
p0 *= norm.x;
|
||||
p1 *= norm.y;
|
||||
p2 *= norm.z;
|
||||
p3 *= norm.w;
|
||||
vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
|
||||
m = m * m;
|
||||
return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),
|
||||
dot(p2,x2), dot(p3,x3) ) );
|
||||
}
|
||||
|
||||
void main() {
|
||||
// Offset by time for animation
|
||||
vec3 noiseCoord = vWorldPosition * uNoiseScale + vec3(0.0, uTime * 0.005, uTime * 0.003);
|
||||
|
||||
// Combine multiple octaves of noise for a more detailed cloud
|
||||
float noiseVal = snoise(noiseCoord * 1.0) * 0.5;
|
||||
noiseVal += snoise(noiseCoord * 2.0) * 0.25;
|
||||
noiseVal += snoise(noiseCoord * 4.0) * 0.125;
|
||||
noiseVal = noiseVal / (1.0 + 0.5 + 0.25 + 0.125); // Normalize
|
||||
|
||||
// Remap noise to a more cloud-like density curve
|
||||
float density = smoothstep(0.3, 1.0, noiseVal * 0.5 + 0.5); // 0.3-1.0 to give more wispy edges
|
||||
density *= uDensity;
|
||||
|
||||
// Make clouds fade out towards the top and bottom of the layer
|
||||
float yPosNormalized = (vWorldPosition.y - (CLOUD_LAYER_Y - CLOUD_THICKNESS / 2.0)) / CLOUD_THICKNESS;
|
||||
float fadeFactor = 1.0 - smoothstep(0.0, 0.2, yPosNormalized) * smoothstep(1.0, 0.8, yPosNormalized);
|
||||
|
||||
gl_FragColor = vec4(uCloudColor, density * fadeFactor * CLOUD_OPACITY);
|
||||
if (gl_FragColor.a < 0.05) discard; // Don't render very transparent pixels
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
const cloudMaterial = new THREE.ShaderMaterial({
|
||||
uniforms: CloudShader.uniforms,
|
||||
vertexShader: CloudShader.vertexShader,
|
||||
fragmentShader: CloudShader.fragmentShader,
|
||||
transparent: true,
|
||||
depthWrite: false, // Important for proper blending of transparent objects
|
||||
blending: THREE.AdditiveBlending, // Optional: gives a more ethereal look
|
||||
side: THREE.DoubleSide,
|
||||
});
|
||||
|
||||
const clouds = new THREE.Mesh(cloudGeometry, cloudMaterial);
|
||||
clouds.position.y = CLOUD_LAYER_Y;
|
||||
scene.add(clouds);
|
||||
|
||||
// === COMMIT HEATMAP ===
|
||||
// Canvas-texture overlay on the floor. Each agent occupies a polar sector;
|
||||
// recent commits make that sector glow brighter. Activity decays over 24 h.
|
||||
@@ -880,6 +1000,9 @@ function animate() {
|
||||
// Heatmap floor: subtle breathing glow
|
||||
heatmapMat.opacity = 0.75 + Math.sin(elapsed * 0.6) * 0.2;
|
||||
|
||||
// Animate procedural clouds
|
||||
cloudMaterial.uniforms.uTime.value = elapsed;
|
||||
|
||||
if (photoMode) {
|
||||
orbitControls.update();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user