Prompt
Create a SDF representation of a bird with animated flapping wings and basic coloring and shading, use a raymarcher to render with three.js
A system prompt was added to support web rendering
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Bird SDF Raymarching</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script> <style>body { margin: 0; }</style> </head> <body> <script> const scene = new THREE.Scene(); const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1); const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); const geometry = new THREE.PlaneGeometry(2, 2); const material = new THREE.ShaderMaterial({ uniforms: { time: { value: 0.0 }, resolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) } }, vertexShader: ` varying vec2 vUv; void main() { vUv = uv; gl_Position = vec4(position, 1.0); } `, fragmentShader: ` precision highp float; uniform float time; uniform vec2 resolution; varying vec2 vUv; float sdSphere(vec3 p, float r) { return length(p) - r; } float sdBox(vec3 p, vec3 b) { vec3 q = abs(p) - b; return length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0); } float sdCapsule(vec3 p, vec3 a, vec3 b, float r) { vec3 pa = p - a; vec3 ba = b - a; float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); return length(pa - ba * h) - r; } vec2 map(vec3 p) { float angle = 0.7 * sin(time * 5.0); // Body vec3 body_a = vec3(0.0, 0.0, -0.5); vec3 body_b = vec3(0.0, 0.0, 0.5); float body_r = 0.25; float d_body = sdCapsule(p, body_a, body_b, body_r); // Head float d_head = sdSphere(p - vec3(0.0, 0.0, 0.7), 0.2); // Right wing vec3 sh_r = vec3(0.25, 0.0, 0.0); vec3 p_r = p - sh_r; float ca = cos(angle); float sa = sin(angle); p_r = vec3(p_r.x * ca - p_r.y * sa, p_r.x * sa + p_r.y * ca, p_r.z); float d_wr = sdBox(p_r - vec3(0.4, 0.0, 0.0), vec3(0.4, 0.04, 0.15)); // Left wing vec3 sh_l = vec3(-0.25, 0.0, 0.0); vec3 p_l = p - sh_l; ca = cos(-angle); sa = sin(-angle); p_l = vec3(p_l.x * ca - p_l.y * sa, p_l.x * sa + p_l.y * ca, p_l.z); float d_wl = sdBox(p_l - vec3(-0.4, 0.0, 0.0), vec3(0.4, 0.04, 0.15)); // Min distance and ID float mind = d_body; float id = 1.0; if (d_head < mind) { mind = d_head; id = 2.0; } if (d_wr < mind) { mind = d_wr; id = 3.0; } if (d_wl < mind) { mind = d_wl; id = 3.0; } return vec2(mind, id); } vec3 calcNormal(vec3 p) { vec2 e = vec2(0.001, 0.0); return normalize(vec3( map(p + e.xyy).x - map(p - e.xyy).x, map(p + e.yxy).x - map(p - e.yxy).x, map(p + e.yyx).x - map(p - e.yyx).x )); } float raymarch(vec3 ro, vec3 rd, inout float id) { float t = 0.0; id = 0.0; for (int i = 0; i < 128; i++) { vec3 pos = ro + t * rd; vec2 res = map(pos); if (res.x < 0.001) { id = res.y; return t; } t += res.x; if (t > 20.0) break; } return -1.0; } void main() { vec2 uv = vUv * 2.0 - 1.0; uv.x *= resolution.x / resolution.y; vec3 ro = vec3(0.0, 0.5, -3.0); vec3 rd = normalize(vec3(uv, 1.5)); float objID = 0.0; float t = raymarch(ro, rd, objID); vec3 col; if (t < 0.0) { col = vec3(0.5, 0.7, 1.0); // sky } else { vec3 pos = ro + t * rd; vec3 nor = calcNormal(pos); vec3 lig = normalize(vec3(0.5, 1.0, -0.5)); float dif = max(dot(nor, lig), 0.0); if (objID == 1.0) col = vec3(0.8, 0.8, 0.8); // body else if (objID == 2.0) col = vec3(1.0, 0.9, 0.5); // head else if (objID == 3.0) col = vec3(0.3, 0.3, 0.8); // wings col *= 0.5 + 0.5 * dif; // shading } gl_FragColor = vec4(col, 1.0); } ` }); const quad = new THREE.Mesh(geometry, material); scene.add(quad); let clock = new THREE.Clock(); function animate() { requestAnimationFrame(animate); material.uniforms.time.value = clock.getElapsedTime(); renderer.render(scene, camera); } animate(); </script> </body> </html> ```
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>SDF Bird with Flapping Wings</title> <style> body { margin: 0; padding: 0; overflow: hidden; background: linear-gradient(to bottom, #87CEEB, #E0F6FF); } </style> </head> <body> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <script> // Scene setup const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // Vertex shader const vertexShader = ` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `; // Fragment shader with SDF raymarcher const fragmentShader = ` uniform float time; uniform vec2 resolution; varying vec2 vUv; const int MAX_STEPS = 100; const float MAX_DIST = 100.0; const float SURF_DIST = 0.01; // SDF operations float smin(float a, float b, float k) { float h = clamp(0.5 + 0.5 * (b - a) / k, 0.0, 1.0); return mix(b, a, h) - k * h * (1.0 - h); } float sdSphere(vec3 p, float r) { return length(p) - r; } float sdEllipsoid(vec3 p, vec3 r) { float k0 = length(p / r); float k1 = length(p / (r * r)); return k0 * (k0 - 1.0) / k1; } float sdCapsule(vec3 p, vec3 a, vec3 b, float r) { vec3 pa = p - a, ba = b - a; float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); return length(pa - ba * h) - r; } // Rotation matrices mat3 rotateX(float theta) { float c = cos(theta); float s = sin(theta); return mat3(1, 0, 0, 0, c, -s, 0, s, c); } mat3 rotateY(float theta) { float c = cos(theta); float s = sin(theta); return mat3(c, 0, s, 0, 1, 0, -s, 0, c); } mat3 rotateZ(float theta) { float c = cos(theta); float s = sin(theta); return mat3(c, -s, 0, s, c, 0, 0, 0, 1); } // Bird SDF float sdBird(vec3 p) { // Body (ellipsoid) vec3 bodyPos = p; float body = sdEllipsoid(bodyPos, vec3(0.4, 0.3, 0.8)); // Head (sphere) vec3 headPos = p - vec3(0.0, 0.1, 0.6); float head = sdSphere(headPos, 0.25); // Beak (elongated sphere) vec3 beakPos = p - vec3(0.0, 0.05, 0.8); float beak = sdEllipsoid(beakPos, vec3(0.05, 0.05, 0.15)); // Wing animation float wingFlap = sin(time * 8.0) * 0.6; // Left wing vec3 leftWingPos = p - vec3(-0.35, 0.0, 0.2); leftWingPos = rotateZ(wingFlap) * leftWingPos; float leftWing = sdEllipsoid(leftWingPos, vec3(0.8, 0.05, 0.3)); // Right wing vec3 rightWingPos = p - vec3(0.35, 0.0, 0.2); rightWingPos = rotateZ(-wingFlap) * rightWingPos; float rightWing = sdEllipsoid(rightWingPos, vec3(0.8, 0.05, 0.3)); // Tail vec3 tailPos = p - vec3(0.0, 0.0, -0.6); float tail = sdEllipsoid(tailPos, vec3(0.3, 0.05, 0.4)); // Combine all parts float bird = smin(body, head, 0.1); bird = smin(bird, beak, 0.05); bird = smin(bird, leftWing, 0.1); bird = smin(bird, rightWing, 0.1); bird = smin(bird, tail, 0.1); return bird; } float getDist(vec3 p) { // Animated bird position vec3 birdPos = p - vec3(sin(time * 0.5) * 2.0, sin(time * 1.2) * 0.5, 0.0); birdPos = rotateY(time * 0.3) * birdPos; return sdBird(birdPos); } vec3 getNormal(vec3 p) { float d = getDist(p); vec2 e = vec2(0.01, 0.0); vec3 n = d - vec3( getDist(p - e.xyy), getDist(p - e.yxy), getDist(p - e.yyx) ); return normalize(n); } float rayMarch(vec3 ro, vec3 rd) { float dO = 0.0; for(int i = 0; i < MAX_STEPS; i++) { vec3 p = ro + rd * dO; float dS = getDist(p); dO += dS; if(dO > MAX_DIST || dS < SURF_DIST) break; } return dO; } vec3 getColor(vec3 p, vec3 normal) { // Base bird colors vec3 bodyColor = vec3(0.8, 0.6, 0.2); // Orange-brown body vec3 wingColor = vec3(0.4, 0.3, 0.1); // Darker wings vec3 beakColor = vec3(0.9, 0.7, 0.2); // Yellow beak // Determine which part we're coloring based on position vec3 color = bodyColor; // Wing coloring (approximate) if(abs(p.x) > 0.3 && p.z > -0.2 && p.z < 0.5) { color = wingColor; } // Beak coloring if(p.z > 0.7) { color = beakColor; } return color; } void main() { vec2 uv = (vUv - 0.5) * 2.0; uv.x *= resolution.x / resolution.y; vec3 col = vec3(0.7, 0.9, 1.0); // Sky color vec3 ro = vec3(0.0, 0.0, 3.0); // Ray origin (camera) vec3 rd = normalize(vec3(uv, -1.0)); // Ray direction float d = rayMarch(ro, rd); if(d < MAX_DIST) { vec3 p = ro + rd * d; vec3 n = getNormal(p); // Get base color vec3 baseColor = getColor(p, n); // Lighting vec3 lightPos = vec3(2.0, 4.0, 6.0); vec3 lightDir = normalize(lightPos - p); // Diffuse lighting float diffuse = max(dot(n, lightDir), 0.0); // Ambient lighting float ambient = 0.3; // Specular lighting vec3 viewDir = normalize(ro - p); vec3 reflectDir = reflect(-lightDir, n); float specular = pow(max(dot(viewDir, reflectDir), 0.0), 32.0) * 0.5; // Combine lighting col = baseColor * (ambient + diffuse * 0.8) + vec3(specular); // Add some atmospheric perspective float fog = 1.0 - d / MAX_DIST; col = mix(vec3(0.7, 0.9, 1.0), col, fog); } gl_FragColor = vec4(col, 1.0); } `; // Create material const material = new THREE.ShaderMaterial({ uniforms: { time: { value: 0 }, resolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) } }, vertexShader: vertexShader, fragmentShader: fragmentShader }); // Create plane geometry to render the shader const geometry = new THREE.PlaneGeometry(2, 2); const mesh = new THREE.Mesh(geometry, material); scene.add(mesh); camera.position.z = 1; // Animation loop function animate() { requestAnimationFrame(animate); material.uniforms.time.value += 0.016; // ~60fps renderer.render(scene, camera); } // Handle window resize window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); material.uniforms.resolution.value.set(window.innerWidth, window.innerHeight); }); animate(); </script> </body> </html> ``` This creates a 3D bird using Signed Distance Functions (SDFs) and raymarching with the following features: 1. **Bird Structure**: Composed of ellipsoids and spheres for body, head, wings, beak, and tail 2. **Animated Wings**: Wings flap using sinusoidal motion with proper rotation 3. **Flight Animation**: The entire bird moves in a gentle flying pattern 4. **Coloring**: Different colors for body (orange-brown), wings (darker brown), and beak (yellow) 5. **Lighting**: Diffuse, ambient, and specular lighting for realistic shading 6. **Atmosphere**: Fog effect for depth and sky background The raymarcher efficiently renders the complex 3D bird shape in real-time, with smooth blending between body parts using the `smin` function for organic-looking transitions.
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>3D Bird with SDF and Raymarching</title> <style> body { margin: 0; overflow: hidden; background: linear-gradient(to bottom, #87CEEB 0%, #ffffff 100%); } canvas { display: block; } </style> </head> <body> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <script> // Scene setup const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // Raymarching shader for the bird const birdFragmentShader = ` uniform float time; uniform vec2 resolution; // Rotate function mat2 rotate(float angle) { return mat2(cos(angle), -sin(angle), sin(angle), cos(angle)); } // Bird SDF float birdSDF(vec3 p) { // Body vec3 bodyPos = p; bodyPos.z *= 0.7; float body = sdEllipsoid(bodyPos, vec3(0.3, 0.2, 0.6)); // Head vec3 headPos = p - vec3(0.0, 0.05, 0.4); headPos.xy *= rotate(0.3); float head = sdSphere(headPos, 0.15); // Combine body and head float bodyHead = smin(body, head, 0.1); // Wing animation float wingAngle = sin(time * 5.0) * 0.5; // Left wing vec3 leftWingPos = p - vec3(-0.2, 0.0, 0.0); leftWingPos.xy *= rotate(wingAngle); leftWingPos.xz *= rotate(0.5); float leftWing = sdBox(leftWingPos, vec3(0.05, 0.3, 0.4)); // Right wing vec3 rightWingPos = p - vec3(0.2, 0.0, 0.0); rightWingPos.xy *= rotate(-wingAngle); rightWingPos.xz *= rotate(-0.5); float rightWing = sdBox(rightWingPos, vec3(0.05, 0.3, 0.4)); // Combine all parts float result = smin(bodyHead, leftWing, 0.1); result = smin(result, rightWing, 0.1); // Beak vec3 beakPos = p - vec3(0.0, 0.02, 0.55); beakPos.xy *= rotate(0.2); float beak = sdCone(beakPos, 0.1, 0.05, 0.15); result = smin(result, beak, 0.05); return result; } // Signed distance functions float sdSphere(vec3 p, float r) { return length(p) - r; } float sdBox(vec3 p, vec3 b) { vec3 d = abs(p) - b; return length(max(d, 0.0)) + min(max(d.x, max(d.y, d.z)), 0.0); } float sdEllipsoid(vec3 p, vec3 r) { return (length(p / r) - 1.0) * min(min(r.x, r.y), r.z); } float sdCone(vec3 p, float radius1, float radius2, float height) { vec2 q = vec2(length(p.xz), p.y); float k = radius2 / height; vec2 ca = vec2(height, radius2 - radius1); vec2 cb = q - vec2(0.0, radius1); float s = max(k * cb.x, cb.y); float t = max(dot(cb, ca), 0.0) / dot(ca, ca); vec2 c = vec2(t, t * k); vec2 h = cb - c; float d1 = length(h) * sign(h.y); float d2 = length(cb - vec2(0.0, height)) * sign(height - cb.y); return min(d1, d2); } // Smooth minimum float smin(float a, float b, float k) { float h = clamp(0.5 + 0.5 * (b - a) / k, 0.0, 1.0); return mix(b, a, h) - k * h * (1.0 - h); } // Calculate normal vec3 calcNormal(vec3 p) { const float h = 0.001; const vec2 k = vec2(1, -1) * h; return normalize(k.xyy * birdSDF(p + k.xyy) + k.yyx * birdSDF(p + k.yyx) + k.yxy * birdSDF(p + k.yxy) + k.xxx * birdSDF(p + k.xxx)); } // Raymarching function float raymarch(vec3 ro, vec3 rd) { float t = 0.0; for (int i = 0; i < 100; i++) { vec3 p = ro + rd * t; float d = birdSDF(p); if (d < 0.001 || t > 20.0) break; t += d * 0.8; } return t; } // Phong shading vec3 phong(vec3 pos, vec3 nor, vec3 lightPos) { // Material properties vec3 kd = vec3(0.2, 0.4, 0.8); // Diffuse color (blue) vec3 ks = vec3(0.8, 0.8, 0.8); // Specular color float shininess = 50.0; // Lighting vec3 lightDir = normalize(lightPos - pos); vec3 viewDir = normalize(-pos); vec3 reflectDir = reflect(-lightDir, nor); // Diffuse component float diff = max(dot(nor, lightDir), 0.0); vec3 diffuse = diff * kd; // Specular component float spec = pow(max(dot(viewDir, reflectDir), 0.0), shininess); vec3 specular = spec * ks; return diffuse + specular; } void main() { // Normalized coordinates vec2 uv = (gl_FragCoord.xy - 0.5 * resolution.xy) / resolution.y; // Camera setup vec3 ro = vec3(0.0, 0.0, -3.0); // Ray origin (camera position) vec3 rd = normalize(vec3(uv, 1.0)); // Ray direction // Raymarching float t = raymarch(ro, rd); // Shading vec3 color = vec3(0.0); if (t < 20.0) { vec3 pos = ro + rd * t; vec3 nor = calcNormal(pos); vec3 lightPos = vec3(2.0, 2.0, 3.0); color = phong(pos, nor, lightPos); // Add some ambient light color += 0.2; // Add wing color variation float wingMask = smoothstep(0.1, 0.2, abs(pos.x) - 0.15); color = mix(color, vec3(0.9, 0.9, 1.0), wingMask * 0.3); } else { // Sky gradient background color = vec3(0.4, 0.7, 1.0) - uv.y * 0.5; } // Output to screen gl_FragColor = vec4(color, 1.0); } `; // Create a plane to render the shader on const geometry = new THREE.PlaneGeometry(2, 2); const material = new THREE.ShaderMaterial({ uniforms: { time: { value: 0 }, resolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) } }, fragmentShader: birdFragmentShader, side: THREE.DoubleSide }); const mesh = new THREE.Mesh(geometry, material); scene.add(mesh); camera.position.z = 1; // Handle window resize window.addEventListener('resize', () => { renderer.setSize(window.innerWidth, window.innerHeight); material.uniforms.resolution.value.set(window.innerWidth, window.innerHeight); }); // Animation loop function animate() { requestAnimationFrame(animate); // Update time uniform for animation material.uniforms.time.value = performance.now() / 1000; renderer.render(scene, camera); } animate(); </script> </body> </html> ```
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Ray-marched SDF Bird – Three.js</title> <style> html,body{margin:0;height:100%;overflow:hidden;background:#000;} canvas{display:block;} </style> </head> <body> <script src="https://cdn.jsdelivr.net/npm/three@0.152.2/build/three.min.js"></script> <script> /* ---------- Three.js setup ---------- */ const renderer = new THREE.WebGLRenderer({antialias:true}); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth,window.innerHeight); document.body.appendChild(renderer.domElement); const scene = new THREE.Scene(); const camera = new THREE.Camera(); // Full-screen quad -> no perspective camera needed camera.position.z = 1; const uniforms = { iTime : {value:0}, iResolution : {value:new THREE.Vector3()} }; /* ---------- Full-screen quad ---------- */ const geometry = new THREE.PlaneGeometry(2,2); const material = new THREE.ShaderMaterial({ uniforms:uniforms, vertexShader:` varying vec2 vUv; void main(){ vUv = uv; gl_Position = vec4(position,1.); } `, fragmentShader:` /* ========================================================== Signed-Distance-Function BIRD – Ray marcher ========================================================== */ precision highp float; uniform float iTime; uniform vec3 iResolution; varying vec2 vUv; /* ---- Useful helpers ---- */ float sdSphere( vec3 p, float r ){ return length(p)-r; } float sdEllipsoid( vec3 p, vec3 r ){ vec3 q = p / r; return length(q) - 1.0; } float sdBox( in vec3 p, in vec3 b ){ vec3 q = abs(p) - b; return length(max(q,0.0)) + min(max(q.x,max(q.y,q.z)),0.0); } float sdCone( vec3 p, vec2 c ){ // c = (tan(angle), height) float q = length(p.xy); return max(dot(vec2(c.x,-c.y), vec2(q,p.z)), p.z); } /* rotate around Z and Y axes */ mat3 rotZ(float a){ float s = sin(a), c = cos(a); return mat3( c,-s,0., s, c,0., 0.,0.,1.); } mat3 rotY(float a){ float s = sin(a), c = cos(a); return mat3( c,0., s, 0.,1.,0., -s,0., c); } /* Material IDs */ const int MAT_BODY = 1; const int MAT_WING = 2; const int MAT_HEAD = 3; const int MAT_BEAK = 4; const int MAT_TAIL = 5; struct SDFRes{ float d; int mat; }; /* ---- Bird model ---- */ SDFRes opUnion(SDFRes a, SDFRes b){ return (a.d < b.d) ? a : b; } SDFRes map( vec3 p ){ float t = iTime; /* 1. Body: stretched sphere (ellipsoid) */ vec3 bodyScale = vec3(0.25,0.18,0.18); float body = sdEllipsoid(p, bodyScale); SDFRes res; res.d = body; res.mat = MAT_BODY; /* 2. Head */ SDFRes head; vec3 hp = p - vec3(0.32,0.0,0.0); head.d = sdSphere(hp,0.11); head.mat = MAT_HEAD; res = opUnion(res, head); /* 3. Beak (cone) */ SDFRes beak; vec3 bp = hp - vec3(0.11,0.0,0.0); // start from head centre bp = rotY(-0.15)*bp; beak.d = sdCone(bp, vec2(0.12,0.18)); beak.mat = MAT_BEAK; res = opUnion(res, beak); /* 4. Wings */ float flap = sin(t*4.0)*0.6 + 0.3; // wing rotation angle for(int side=-1; side<=1; side+=2){ vec3 wp = p; wp.x -= 0.05; wp.y -= 0.0; wp.z += 0.0; // mirror for left/right if(side==-1) wp.y = -wp.y; // translate to wing root wp.y -= 0.18; // rotate to flap wp = rotZ(float(side)*flap) * wp; // undo mirror if(side==-1) wp.y = -wp.y; SDFRes wing; wing.d = sdBox(wp, vec3(0.30,0.04,0.10)); wing.mat = MAT_WING; res = opUnion(res, wing); } /* 5. Tail (simple box) */ SDFRes tail; vec3 tp = p - vec3(-0.23,0.0,0.0); tp = rotZ(0.3)*tp; tail.d = sdBox(tp, vec3(0.12,0.03,0.08)); tail.mat = MAT_TAIL; res = opUnion(res, tail); return res; } /* ---- Ray marching ---- */ vec3 calcNormal(vec3 p){ const float e = 0.0005; vec2 h = vec2(e,0); return normalize(vec3( map(p+h.xyy).d - map(p-h.xyy).d, map(p+h.yxy).d - map(p-h.yxy).d, map(p+h.yyx).d - map(p-h.yyx).d )); } vec3 getColor(int mat){ if(mat==MAT_BODY) return vec3(0.55,0.37,0.20); if(mat==MAT_HEAD) return vec3(0.75,0.55,0.32); if(mat==MAT_WING) return vec3(0.40,0.28,0.15); if(mat==MAT_BEAK) return vec3(1.00,0.80,0.10); if(mat==MAT_TAIL) return vec3(0.45,0.30,0.16); return vec3(1.0,0.0,1.0); } vec3 shade(vec3 p, vec3 rd, vec3 normal, int mat){ vec3 lightDir = normalize(vec3(-0.6,0.8,0.7)); vec3 col = getColor(mat); float diff = clamp(dot(normal, lightDir), 0.0, 1.0); vec3 viewDir = -rd; vec3 halfDir = normalize(lightDir + viewDir); float spec = pow(max(dot(normal, halfDir), 0.0), 32.0); vec3 ambient = 0.1*col; vec3 diffuse = diff*col; vec3 specular = spec*vec3(1.0); return ambient + diffuse + specular; } void main(){ vec2 uv = vUv*2.0 - 1.0; uv.x *= iResolution.x / iResolution.y; vec3 ro = vec3(-1.0, 0.0, 0.0); // camera position vec3 rd = normalize(vec3(1.5, uv)); // camera direction (simple FOV) float totalDist = 0.0; const float MAX_DIST = 5.0; const int MAX_STEPS = 128; const float SURF_EPS = 0.0004; int hitMat = -1; vec3 hitPos; for(int i=0;i<MAX_STEPS;i++){ vec3 pos = ro + rd*totalDist; SDFRes h = map(pos); if(h.d<SURF_EPS){ hitMat = h.mat; hitPos = pos; break; } totalDist += h.d; if(totalDist>MAX_DIST) break; } vec3 color; if(hitMat!=-1){ vec3 n = calcNormal(hitPos); color = shade(hitPos, rd, n, hitMat); }else{ // background gradient sky float t = 0.5*(rd.y+1.0); color = mix(vec3(0.6,0.8,1.0), vec3(0.2,0.4,0.8), t); } /* Gamma correction */ color = pow(color, vec3(1.0/2.2)); gl_FragColor = vec4(color,1.0); } `, }); const mesh = new THREE.Mesh(geometry,material); scene.add(mesh); /* ---------- Resize handling ---------- */ function onResize(){ renderer.setSize(window.innerWidth,window.innerHeight); uniforms.iResolution.value.set(window.innerWidth,window.innerHeight,1); } window.addEventListener('resize',onResize); onResize(); /* ---------- Animation loop ---------- */ const clock = new THREE.Clock(); function animate(){ requestAnimationFrame(animate); uniforms.iTime.value = clock.getElapsedTime(); renderer.render(scene,camera); } animate(); </script> </body> </html> ```
Here's a complete HTML file that creates an animated bird using Signed Distance Fields (SDF) with a raymarching renderer in Three.js. The bird will have flapping wings with basic coloring and shading. ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>SDF Bird with Raymarching</title> <style> body { margin: 0; overflow: hidden; } canvas { display: block; } </style> </head> <body> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script> <script> // Scene setup const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // Position camera camera.position.set(0, 5, 10); camera.lookAt(0, 0, 0); // Add orbit controls (optional for interaction) // Note: OrbitControls is not included in the core Three.js library, so for simplicity, it's omitted // If you want controls, you can add the OrbitControls.js script from Three.js examples // Shader Material for Raymarching const raymarchMaterial = new THREE.ShaderMaterial({ uniforms: { time: { value: 0.0 }, resolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) } }, vertexShader: ` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `, fragmentShader: ` uniform float time; uniform vec2 resolution; varying vec2 vUv; // SDF functions float sdSphere(vec3 p, float r) { return length(p) - r; } float sdEllipsoid(vec3 p, vec3 r) { float k0 = length(p / r); float k1 = length(p / (r * r)); return k0 * (k0 - 1.0) / k1; } float sdCapsule(vec3 p, vec3 a, vec3 b, float r) { vec3 pa = p - a, ba = b - a; float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); return length(pa - ba * h) - r; } float smoothMin(float a, float b, float k) { float h = clamp(0.5 + 0.5 * (b - a) / k, 0.0, 1.0); return mix(b, a, h) - k * h * (1.0 - h); } // Bird SDF float birdSDF(vec3 p) { // Body float body = sdEllipsoid(p - vec3(0.0, 0.0, 0.0), vec3(1.5, 1.0, 0.8)); // Head float head = sdSphere(p - vec3(1.2, 0.3, 0.0), 0.6); // Beak float beak = sdCapsule(p, vec3(1.5, 0.3, 0.0), vec3(2.0, 0.2, 0.0), 0.1); // Combine body, head, beak float bird = smoothMin(body, head, 0.3); bird = smoothMin(bird, beak, 0.1); // Wings (animated) float wingAngle = 0.5 * sin(time * 2.0); // Flapping animation vec3 wing1Pos = p - vec3(0.0, 0.0, 1.0); wing1Pos = mat3( cos(wingAngle), 0.0, sin(wingAngle), 0.0, 1.0, 0.0, -sin(wingAngle), 0.0, cos(wingAngle) ) * wing1Pos; float wing1 = sdEllipsoid(wing1Pos, vec3(2.0, 0.2, 1.0)); vec3 wing2Pos = p - vec3(0.0, 0.0, -1.0); wing2Pos = mat3( cos(wingAngle), 0.0, sin(wingAngle), 0.0, 1.0, 0.0, -sin(wingAngle), 0.0, cos(wingAngle) ) * wing2Pos; float wing2 = sdEllipsoid(wing2Pos, vec3(2.0, 0.2, 1.0)); bird = smoothMin(bird, wing1, 0.2); bird = smoothMin(bird, wing2, 0.2); return bird; } // Normal calculation vec3 calcNormal(vec3 p) { const float eps = 0.001; const vec2 h = vec2(eps, 0.0); return normalize(vec3( birdSDF(p + h.xyy) - birdSDF(p - h.xyy), birdSDF(p + h.yxy) - birdSDF(p - h.yxy), birdSDF(p + h.yyx) - birdSDF(p - h.yyx) )); } // Raymarching float raymarch(vec3 ro, vec3 rd) { float t = 0.0; for (int i = 0; i < 100; i++) { vec3 p = ro + t * rd; float d = birdSDF(p); if (d < 0.001) return t; t += d; if (t > 20.0) break; } return -1.0; } void main() { vec2 uv = (vUv - 0.5) * resolution / resolution.y; vec3 ro = vec3(0.0, 5.0, 10.0); // Camera position vec3 rd = normalize(vec3(uv, -1.0)); // Ray direction // Rotate camera to look at origin (approximate) vec3 lookAt = normalize(vec3(0.0, 0.0, 0.0) - ro); vec3 right = normalize(cross(lookAt, vec3(0.0, 1.0, 0.0))); vec3 up = cross(right, lookAt); rd = mat3(right, up, lookAt) * rd; float t = raymarch(ro, rd); vec3 col = vec3(0.2, 0.5, 0.8); // Background color (sky) if (t > 0.0) { vec3 pos = ro + t * rd; vec3 normal = calcNormal(pos); vec3 lightDir = normalize(vec3(0.5, 1.0, 0.5)); float diff = max(dot(normal, lightDir), 0.0); // Basic coloring vec3 baseColor = vec3(0.5, 0.3, 0.2); // Brownish bird color col = baseColor * (0.2 + 0.8 * diff); // Ambient + diffuse lighting } gl_FragColor = vec4(col, 1.0); } ` }); // Create a full-screen quad to render the shader const geometry = new THREE.PlaneGeometry(2, 2); const quad = new THREE.Mesh(geometry, raymarchMaterial); scene.add(quad); // Handle window resize window.addEventListener('resize', () => { renderer.setSize(window.innerWidth, window.innerHeight); camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); raymarchMaterial.uniforms.resolution.value.set(window.innerWidth, window.innerHeight); }); // Animation loop let time = 0; function animate() { requestAnimationFrame(animate); time += 0.016; // Approximate delta time raymarchMaterial.uniforms.time.value = time; renderer.render(scene, camera); } animate(); </script> </body> </html> ``` This code creates a 3D bird model using SDFs with raymarching in a Three.js shader. The bird features: - An ellipsoidal body and spherical head with a beak. - Animated flapping wings using a sinusoidal function to rotate them. - Basic coloring (brownish bird) and simple diffuse lighting with a directional light source. - A sky-blue background for contrast. You can open this HTML file in a web browser to see the rendered animation. The bird's wings will flap continuously, and the raymarching technique ensures a smooth, volumetric rendering of the SDF-defined geometry. If you wish to interact with the camera, you can include Three.js's OrbitControls by adding the appropriate script, though it's omitted here for brevity.
Of course! Here is a complete, self-contained HTML file that uses Three.js and a raymarching shader to render an animated bird. The bird is defined mathematically using Signed Distance Functions (SDFs), and its wings flap continuously. You can save this code as an `.html` file and open it in a web browser. ```html <!DOCTYPE html> <html lang="en"> <head> <title>SDF Raymarched Bird</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> <style> body { margin: 0; padding: 0; overflow: hidden; background-color: #000; color: #fff; } canvas { display: block; } </style> </head> <body> <!-- Vertex Shader: Passes geometry and UVs through --> <script id="vertexShader" type="x-shader/x-vertex"> varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } </script> <!-- Fragment Shader: The core of the raymarching renderer --> <script id="fragmentShader" type="x-shader/x-fragment"> varying vec2 vUv; uniform vec2 u_resolution; uniform float u_time; uniform vec3 u_cameraPos; uniform vec3 u_cameraLookAt; // --- Raymarching Constants --- const int MAX_STEPS = 100; const float MAX_DIST = 100.0; const float HIT_PRECISION = 0.001; // --- Material IDs --- const float MAT_BODY = 1.0; const float MAT_BEAK = 2.0; const float MAT_EYE = 3.0; const float MAT_WING = 4.0; // --- SDF Primitives (the building blocks of our model) --- float sdSphere(vec3 p, float s) { return length(p) - s; } float sdBox(vec3 p, vec3 b) { vec3 q = abs(p) - b; return length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0); } float sdEllipsoid(vec3 p, vec3 r) { float k0 = length(p / r); float k1 = length(p / (r * r)); return k0 * (k0 - 1.0) / k1; } // --- SDF Operations (how we combine and modify shapes) --- vec2 opUnion(vec2 d1, vec2 d2) { return (d1.x < d2.x) ? d1 : d2; } vec2 opSmoothUnion(vec2 d1, vec2 d2, float k) { float h = clamp(0.5 + 0.5 * (d2.x - d1.x) / k, 0.0, 1.0); float d = mix(d2.x, d1.x, h) - k * h * (1.0 - h); // Mix materials based on proximity float m = mix(d2.y, d1.y, h); return vec2(d, m); } vec2 opSubtraction(vec2 d1, vec2 d2) { float d = max(-d1.x, d2.x); // Keep material of the second object if it's the one being shown float m = (d > d2.x) ? d1.y : d2.y; return vec2(d, d1.y); } mat2 rot(float a) { float s = sin(a); float c = cos(a); return mat2(c, -s, s, c); } // --- Bird Scene Definition --- // This is the core function defining the bird's geometry. // It returns a vec2: x = distance to surface, y = material ID. vec2 map(vec3 p) { // Main Body (ellipsoid) vec3 bodyPos = p - vec3(0.0, 0.15, 0.0); float bodySDF = sdEllipsoid(bodyPos, vec3(0.25, 0.3, 0.2)); vec2 res = vec2(bodySDF, MAT_BODY); // Head (sphere) vec3 headPos = p - vec3(0.2, 0.4, 0.0); float headSDF = sdSphere(headPos, 0.2); res = opSmoothUnion(res, vec2(headSDF, MAT_BODY), 0.15); // Beak (box, transformed) vec3 beakPos = p - vec3(0.4, 0.38, 0.0); beakPos.xy = rot(-0.2) * beakPos.xy; // Rotate down float beakSDF = sdBox(beakPos, vec3(0.1, 0.02, 0.05)); res = opSmoothUnion(res, vec2(beakSDF, MAT_BEAK), 0.05); // Eye (sphere, subtracted) vec3 eyePos = p - vec3(0.3, 0.45, -0.08); // Right eye float eyeSDF = sdSphere(eyePos, 0.03); eyePos.z *= -1.0; // Left eye eyeSDF = min(eyeSDF, sdSphere(eyePos, 0.03)); res = opSubtraction(res, vec2(eyeSDF, MAT_EYE)); // Wings (animated boxes) float flapAngle = 0.9 * sin(u_time * 8.0); vec3 wingPivot = vec3(-0.05, 0.25, 0.0); vec3 wingP = p - wingPivot; // Symmetrize for two wings wingP.z = abs(wingP.z) - 0.18; // Apply flapping rotation wingP.xy = rot(flapAngle) * wingP.xy; float wingSDF = sdBox(wingP, vec3(0.02, 0.25, 0.3)); res = opSmoothUnion(res, vec2(wingSDF, MAT_WING), 0.1); return res; } // --- Utility Functions for Rendering --- // Calculate the normal vector at a surface point by sampling the SDF vec3 calcNormal(vec3 p) { vec2 e = vec2(0.001, 0.0); return normalize(vec3( map(p + e.xyy).x - map(p - e.xyy).x, map(p + e.yxy).x - map(p - e.yxy).x, map(p + e.yyx).x - map(p - e.yyx).x )); } // The raymarching algorithm vec2 raymarch(vec3 ro, vec3 rd) { float t = 0.0; vec2 res = vec2(-1.0, -1.0); for (int i = 0; i < MAX_STEPS; i++) { vec3 p = ro + rd * t; res = map(p); if (res.x < HIT_PRECISION || t > MAX_DIST) break; t += res.x; } if (t > MAX_DIST) t = -1.0; return vec2(t, res.y); } // Simple soft shadow calculation float calcSoftShadow(vec3 ro, vec3 rd, float k) { float res = 1.0; float t = 0.01; // Start slightly away from the surface for (int i = 0; i < 32; i++) { float h = map(ro + rd * t).x; if(h < HIT_PRECISION) return 0.0; res = min(res, k * h / t); t += h; if(t > MAX_DIST) break; } return clamp(res, 0.0, 1.0); } // --- Main Rendering Logic --- void main() { // Setup screen coordinates vec2 p = (2.0 * vUv - 1.0) * vec2(u_resolution.x / u_resolution.y, 1.0); // Setup camera vec3 ro = u_cameraPos; vec3 target = u_cameraLookAt; vec3 fwd = normalize(target - ro); vec3 right = normalize(cross(fwd, vec3(0.0, 1.0, 0.0))); vec3 up = normalize(cross(right, fwd)); // Calculate ray direction float fov = 2.0; vec3 rd = normalize(p.x * right + p.y * up + fov * fwd); // Raymarch the scene vec2 hit = raymarch(ro, rd); // Default background color vec3 col = vec3(0.5, 0.7, 0.9) - 0.4 * rd.y; // Sky gradient if (hit.x > 0.0) { // We hit something vec3 pos = ro + rd * hit.x; vec3 normal = calcNormal(pos); // --- Lighting --- vec3 lightPos = vec3(2.0, 4.0, 3.0); vec3 lightDir = normalize(lightPos - pos); vec3 viewDir = normalize(ro - pos); // Color based on material ID vec3 baseColor; if (hit.y == MAT_BODY) { baseColor = vec3(0.9, 0.7, 0.2); // Yellow-Brown } else if (hit.y == MAT_BEAK) { baseColor = vec3(1.0, 0.5, 0.1); // Orange } else if (hit.y == MAT_WING) { baseColor = vec3(0.8, 0.6, 0.15); // Darker Yellow } else { baseColor = vec3(0.1); // Fallback (e.g. eye) } // Ambient light float ambient = 0.3; // Diffuse light (Lambertian) float diffuse = max(dot(normal, lightDir), 0.0); // Specular light (Blinn-Phong) vec3 halfwayDir = normalize(lightDir + viewDir); float spec = pow(max(dot(normal, halfwayDir), 0.0), 32.0); vec3 specular = vec3(0.8) * spec; // Shadows float shadow = calcSoftShadow(pos, lightDir, 16.0); // Rim light for silhouette float rim = pow(1.0 - max(dot(normal, viewDir), 0.0), 3.0); vec3 rimColor = vec3(1.0, 0.8, 0.5) * rim; // Combine lighting components col = baseColor * (ambient + diffuse * shadow) + specular * shadow + rimColor; } gl_FragColor = vec4(col, 1.0); } </script> <!-- Use unpkg CDN for Three.js --> <script src="https://unpkg.com/three@0.142.0/build/three.min.js"></script> <script type="module"> let scene, camera, renderer, material, clock; function init() { // Basic Three.js setup scene = new THREE.Scene(); camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1); renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); clock = new THREE.Clock(); // Setup a plane that fills the screen const geometry = new THREE.PlaneGeometry(2, 2); // Define uniforms for the shader const uniforms = { u_time: { value: 0.0 }, u_resolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) }, u_cameraPos: { value: new THREE.Vector3() }, u_cameraLookAt: { value: new THREE.Vector3(0, 0.2, 0) } }; // Create the shader material material = new THREE.ShaderMaterial({ uniforms: uniforms, vertexShader: document.getElementById('vertexShader').textContent, fragmentShader: document.getElementById('fragmentShader').textContent }); const plane = new THREE.Mesh(geometry, material); scene.add(plane); // Handle window resizing window.addEventListener('resize', onWindowResize, false); } function onWindowResize() { renderer.setSize(window.innerWidth, window.innerHeight); material.uniforms.u_resolution.value.x = window.innerWidth; material.uniforms.u_resolution.value.y = window.innerHeight; } function animate() { requestAnimationFrame(animate); const elapsedTime = clock.getElapsedTime(); // Update time uniform material.uniforms.u_time.value = elapsedTime; // Animate the camera to orbit around the bird const radius = 3.0; const camX = Math.sin(elapsedTime * 0.3) * radius; const camZ = Math.cos(elapsedTime * 0.3) * radius; material.uniforms.u_cameraPos.value.set(camX, 1.5, camZ); renderer.render(scene, camera); } init(); animate(); </script> </body> </html> ```
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>3D Bird SDF with Raymarching</title> <style> body { margin: 0; overflow: hidden; } canvas { display: block; } </style> </head> <body> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/EffectComposer.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/RenderPass.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/ShaderPass.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/shaders/CopyShader.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/shaders/EffectComposer.js"></script> <script id="vertexShader" type="x-shader/x-vertex"> varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } </script> <script id="fragmentShader" type="x-shader/x-fragment"> uniform vec2 iResolution; uniform float iTime; varying vec2 vUv; // Signed distance functions float sdSphere(vec3 p, float r) { return length(p) - r; } float sdBox(vec3 p, vec3 b) { vec3 q = abs(p) - b; return length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0); } vec3 rotateX(vec3 p, float angle) { float c = cos(angle); float s = sin(angle); return vec3(p.x, p.y*c - p.z*s, p.y*s + p.z*c); } vec3 rotateY(vec3 p, float angle) { float c = cos(angle); float s = sin(angle); return vec3(p.x*c + p.z*s, p.y, -p.x*s + p.z*c); } float sdBirdBody(vec3 p) { // Bird body - stretched sphere vec3 bodyP = p; bodyP.x *= 0.7; bodyP.y *= 1.2; bodyP.z *= 0.8; return sdSphere(bodyP, 0.8) * 0.9; } float sdBirdHead(vec3 p) { vec3 headPos = vec3(0.0, 0.8, 1.3); vec3 localP = p - headPos; localP = rotateY(localP, -0.3); return sdSphere(localP, 0.5); } float sdBirdBeak(vec3 p) { vec3 beakPos = vec3(0.0, 0.7, 1.9); vec3 localP = p - beakPos; localP = rotateY(localP, -0.3); localP = rotateX(localP, 0.2); vec3 beakBox = vec3(0.2, 0.2, 0.6); return sdBox(localP, beakBox); } float wingDeformation(float time) { // Animation for flapping wings return 0.8 * sin(time * 5.0) + 1.2; } float sdBirdWing(vec3 p, bool leftWing) { vec3 wingPos = vec3(leftWing ? -0.9 : 0.9, 0.2, 0.2); vec3 localP = p - wingPos; // Wing flapping animation float flap = wingDeformation(iTime); float wingRotationX = sin(iTime * 3.0) * 0.4 * flap; float wingRotationZ = sin(iTime * 3.0 + 3.14159) * 0.2; localP = rotateX(localP, wingRotationX); localP = rotateZ(localP, wingRotationZ); // Wing is a flattened box vec3 wingBox = vec3(0.3, 0.8, 0.15); return sdBox(localP, wingBox); } float sdBirdTail(vec3 p) { vec3 tailPos = vec3(0.0, -0.2, -1.0); vec3 localP = p - tailPos; localP = rotateX(localP, -0.3); vec3 tailBox = vec3(0.2, 0.3, 0.8); return sdBox(localP, tailBox); } float map(vec3 p) { float body = sdBirdBody(p); float head = sdBirdHead(p); float beak = sdBirdBeak(p); float leftWing = sdBirdWing(p, true); float rightWing = sdBirdWing(p, false); float tail = sdBirdTail(p); // Union operations (minimum) float bird = min(body, head); bird = min(bird, beak); bird = min(bird, leftWing); bird = min(bird, rightWing); bird = min(bird, tail); return bird; } vec3 calculateNormal(vec3 p) { const float eps = 0.001; const vec2 h = vec2(eps, 0.0); return normalize(vec3( map(p + h.xyy) - map(p - h.xyy), map(p + h.yxy) - map(p - h.yxy), map(p + h.yyx) - map(p - h.yyx) )); } float calculateAO(vec3 p, vec3 n) { float r = 0.0; float w = 1.0; for (float i = 1.0; i <= 5.0; i++) { float d = i * 0.1; r += w * (d - map(p + n * d)); w *= 0.7; } return 1.0 - clamp(r, 0.0, 1.0); } vec3 calculateColor(vec3 p, vec3 normal) { // Basic coloring based on position and normal vec3 bodyColor = vec3(0.8, 0.8, 0.2); // Yellowish body vec3 headColor = vec3(0.4, 0.4, 0.4); // Gray head vec3 beakColor = vec3(1.0, 0.5, 0.0); // Orange beak vec3 wingColor = vec3(0.2, 0.2, 0.8); // Blue wings vec3 tailColor = vec3(0.2, 0.8, 0.2); // Green tail // Determine which part we're on based on position float bodyDist = sdBirdBody(p); float headDist = sdBirdHead(p); float beakDist = sdBirdBeak(p); float leftWingDist = sdBirdWing(p, true); float rightWingDist = sdBirdWing(p, false); float tailDist = sdBirdTail(p); float minDist = min(min(min(bodyDist, headDist), min(beakDist, min(leftWingDist, rightWingDist))), tailDist); vec3 color = bodyColor; if (minDist == headDist) { color = headColor; } else if (minDist == beakDist) { color = beakColor; } else if (minDist == leftWingDist || minDist == rightWingDist) { color = wingColor; } else if (minDist == tailDist) { color = tailColor; } // Add some shading vec3 lightDir = normalize(vec3(1.0, 1.0, -1.0)); float diff = max(dot(normal, lightDir), 0.0); float ao = calculateAO(p, normal); float rim = pow(1.0 - max(dot(normal, normalize(p)), 0.0), 3.0); return color * (diff * 0.7 + 0.3) * ao + rim * 0.3; } vec4 raymarch(vec3 ro, vec3 rd) { float dist = 0.0; float totalDist = 0.0; for (int i = 0; i < 100; i++) { vec3 p = ro + rd * totalDist; dist = map(p); totalDist += dist; if (dist < 0.001) { vec3 normal = calculateNormal(p); vec3 color = calculateColor(p, normal); return vec4(color, 1.0); } if (totalDist > 20.0) { break; } } // Sky background float t = 0.5 * (rd.y + 1.0); return mix(vec4(0.8, 0.9, 1.0, 1.0), vec4(0.4, 0.6, 1.0, 1.0), t); } void main() { vec2 uv = vUv * 2.0 - 1.0; uv.x *= iResolution.x / iResolution.y; // Camera setup vec3 ro = vec3(3.0 * cos(iTime * 0.2), 1.0, 3.0 * sin(iTime * 0.2)); vec3 target = vec3(0.0, 0.3, 0.0); vec3 lookDir = normalize(target - ro); vec3 rightDir = normalize(cross(lookDir, vec3(0.0, 1.0, 0.0))); vec3 upDir = normalize(cross(rightDir, lookDir)); vec3 rd = normalize(lookDir + 0.8 * uv.x * rightDir + 0.8 * uv.y * upDir); vec4 color = raymarch(ro, rd); gl_FragColor = color; } </script> <script> // Set up the scene const scene = new THREE.Scene(); const camera = new THREE.Camera(); camera.position.z = 1; const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // Create a plane to render the shader on const plane = new THREE.Mesh( new THREE.PlaneGeometry(2, 2, 0), new THREE.ShaderMaterial({ uniforms: { iResolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) }, iTime: { value: 0.0 } }, vertexShader: document.getElementById('vertexShader').textContent, fragmentShader: document.getElementById('fragmentShader').textContent }) ); scene.add(plane); // Animation loop function animate() { requestAnimationFrame(animate); plane.material.uniforms.iTime.value = performance.now() / 1000; renderer.render(scene, camera); } // Handle window resize window.addEventListener('resize', () => { renderer.setSize(window.innerWidth, window.innerHeight); plane.material.uniforms.iResolution.value.set(window.innerWidth, window.innerHeight); }); animate(); </script> </body> </html> ```
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>SDF Animated Bird with Raymarching (Three.js)</title> <style> body { margin: 0; overflow: hidden; background: #181b20;} canvas { display: block; } </style> <script src="https://cdn.jsdelivr.net/npm/three@0.150.1/build/three.min.js"></script> </head> <body> <script id="fragShader" type="x-shader/x-fragment"> uniform float iTime; uniform vec2 iResolution; #define MAX_STEPS 90 #define MAX_DIST 30.0 #define SURF_DIST 0.001 // Utility float smin(float a, float b, float k) { float h = clamp(0.5 + 0.5*(b-a)/k, 0.0, 1.0); return mix(b, a, h) - k*h*(1.0-h); } float smax(float a, float b, float k) { return -smin(-a, -b, k); } float smoothUnion(float d1, float d2, float k) { float h = clamp(0.5+0.5*(d2-d1)/k, 0.0, 1.0); return mix(d2, d1, h) - k*h*(1.0-h); } float sdSphere(vec3 p, float r) { return length(p) - r; } float sdEllipsoid(vec3 p, vec3 r) { return (length(p/r) - 1.0)*min(min(r.x, r.y), r.z); } float sdCapsule(vec3 p, vec3 a, vec3 b, float r) { vec3 pa = p-a, ba = b-a; float h = clamp(dot(pa,ba)/dot(ba,ba),0.0,1.0); return length(pa - ba*h) - r; } float sdBox(vec3 p, vec3 b) { vec3 d = abs(p) - b; return length(max(d,0.0)) + min(max(d.x,max(d.y,d.z)),0.0); } mat2 rot(float a) { float c = cos(a), s = sin(a); return mat2(c, -s, s, c); } float sdBird(vec3 p, out int mId) { mId = 0; // Body float body = sdEllipsoid(p, vec3(0.22,0.30,0.22)); mId = 1; // Head vec3 ph = p - vec3(0.0,0.33,0.04); float head = sdEllipsoid(ph, vec3(0.15,0.14,0.13)); body = smin(body, head, 0.1); // Beak vec3 pb = p - vec3(0.0,0.35,0.22); pb.xz *= rot(-0.07); float beak = sdBox(pb - vec3(0.0,0.0,0.09), vec3(0.027,0.027,0.09)); beak = smin(beak, sdEllipsoid(pb - vec3(0,0,0.18), vec3(0.02,0.02,0.10)), 0.03); body = smin(body, beak, 0.04); if(beak < min(head,body)) mId = 2; // Eyes vec3 eye = p - vec3(0.07,0.38,0.11); float lEye = sdSphere(eye, 0.017); eye.x = -eye.x; float rEye = sdSphere(eye, 0.017); float eyes = min(lEye, rEye); body = smin(body, eyes, 0.003); if(eyes < min(beak,head,body)) mId = 3; // Animated wings float flap = sin(iTime*2.0)*0.40 + 0.7; // [-0.6,1.0] float wingZ = 0.18; float wingWidth = 0.11 + abs(sin(iTime*2.0))*0.12; vec3 pwL = p - vec3(-0.23,0.19,wingZ); pwL.xy *= rot(-flap*1.1); float wingL = sdEllipsoid(pwL, vec3(wingWidth, 0.03, 0.16)); vec3 pwR = p - vec3(0.23,0.19,wingZ); pwR.xy *= rot(flap*1.1); float wingR = sdEllipsoid(pwR, vec3(wingWidth, 0.03, 0.16)); float wings = min(wingL, wingR); body = smin(body, wings, 0.07); if(wings < min(eyes,beak,head,body)) mId = 4; // Tail (fan of ellipsoids) float tail = 100.; for(int i = -1; i <= 1; ++i){ float ang = radians(16.0)*float(i); vec3 t = p - vec3(0.0,-0.22,0.0); t.xy *= rot(ang); float f = sdEllipsoid(t, vec3(0.04,0.11,0.10)); tail = min(tail, f); } body = smin(body, tail, 0.02); // Legs (two capsules and claws) float leg1 = sdCapsule(p, vec3(-0.07,-.345,0.02), vec3(-0.09,-0.23,0.03), 0.019); float claw1 = sdCapsule(p, vec3(-0.09,-0.23,0.03), vec3(-0.12,-0.22,0.04), 0.011); leg1 = min(leg1, claw1); float leg2 = sdCapsule(p, vec3(0.07,-.345,0.01), vec3(0.09,-0.23,0.03), 0.019); float claw2 = sdCapsule(p, vec3(0.09,-0.23,0.03), vec3(0.12,-0.22,0.03), 0.011); leg2 = min(leg2, claw2); float legs = min(leg1, leg2); body = smin(body, legs, 0.025); return body; } // Normal from SDF vec3 getNormal(vec3 p) { float d = 0.0; int m; vec2 e = vec2(1e-4, 0.0); d = sdBird(p, m); return normalize(vec3( sdBird(p+e.xyy, m)-d, sdBird(p+e.yxy, m)-d, sdBird(p+e.yyx, m)-d )); } // Coloring function vec3 getMaterial(vec3 p, int m) { // 0: default, 1: bodyHead, 2: beak, 3: eyes, 4: wings if(m == 2) return vec3(1.0, 0.75, 0.21); // Beak: yellow else if(m == 3) return vec3(0.05, 0.07, 0.07); // Eyes: black else if(m == 4) return vec3(0.22,0.27,0.42); // Wings: blue else if(m == 1) return vec3(0.87,0.74,0.40); // Head: gold // Legs & tail: brownish else return vec3(0.78,0.55,0.29); } void main() { vec2 uv = (gl_FragCoord.xy / iResolution.xy) * 2. - 1.; uv.x *= iResolution.x / iResolution.y; // Camera vec3 ro = vec3(0.0,0.14,1.5); float t = iTime*0.33; ro.xz *= rot(0.11 * cos(iTime*0.18)); ro.yz *= rot(-0.32*sinf(iTime*0.14)); vec3 lookAt = vec3(0.,0.09,0.); vec3 f = normalize(lookAt - ro); vec3 r = normalize(cross(f, vec3(0.0,1.0,0.0))); vec3 u = cross(r, f); vec3 rd = normalize(uv.x*r + uv.y*u + 2.0*f); // Raymarching float dO = 0.0; int mId = 0; float dS; vec3 p; int hitId = -1; for(int i=0; i < MAX_STEPS; i++) { p = ro + rd*dO; dS = sdBird(p, mId); if(dS<SURF_DIST) {hitId = mId; break;} dO += dS; if(dO>MAX_DIST) break; } vec3 bg = mix(vec3(0.42,0.61,0.93), vec3(0.07,0.09,0.18), clamp(uv.y+0.47, 0.0, 1.0)); vec3 col = bg; if(dO<MAX_DIST){ vec3 n = getNormal(p); // Simple shadow float shadow = 1.0; vec3 lp = vec3(2.0, 2.4, 2.0); vec3 l = normalize(lp-p); float dO2 = 0.01; int dummy; for(int k=0;k<24;k++){ float dS2 = sdBird(p + l*dO2, dummy); if(dS2 < 0.003) { shadow = 0.0; break; } dO2 += dS2; if(dO2 > 2.0) break; } // Lighting float diff = clamp(dot(n, l), 0.0, 1.0); float ao = exp(-0.22*dO); // Fake AO float fres = pow(1.3-max(dot(n,-rd),0.0), 2.1); float spec = pow(clamp(dot(reflect(-l, n), -rd),0.0,1.0), 18.0); col = getMaterial(p, hitId); col *= (0.36 + 0.77*diff)*shadow + fres*vec3(1.7,1.6,1.5) + vec3(0.14,0.19,0.32)*pow(max(-n.y,0.0),2.0); col = mix(col, bg, pow(clamp(dO*0.03, 0.0, 1.0),2.0)); col += 0.23*spec*shadow + 0.07*ao; } col = pow(col, vec3(0.4545)); // Gamma correction gl_FragColor = vec4(col,1.0); } </script> <script id="vertShader" type="x-shader/x-vertex"> varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); } </script> <script> const renderer = new THREE.WebGLRenderer({antialias:true}); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); const scene = new THREE.Scene(); const camera = new THREE.Camera(); const uniforms = { iTime: { value: 0 }, iResolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) } }; const material = new THREE.ShaderMaterial({ uniforms, vertexShader: document.getElementById("vertShader").textContent, fragmentShader: document.getElementById("fragShader").textContent }); const quad = new THREE.Mesh(new THREE.PlaneGeometry(2,2), material); scene.add(quad); function animate(t){ uniforms.iTime.value = t * 0.001; uniforms.iResolution.value.set(window.innerWidth, window.innerHeight); renderer.setSize(window.innerWidth, window.innerHeight); renderer.render(scene, camera); requestAnimationFrame(animate); } animate(); window.addEventListener('resize', ()=>{ uniforms.iResolution.value.set(window.innerWidth, window.innerHeight); renderer.setSize(window.innerWidth, window.innerHeight); }); </script> </body> </html> ``` This code renders an animated SDF bird with flapping wings, colored beak/eyes/wings/body, and simple shading using a real-time raymarcher in Three.js.
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Bird SDF Animation</title> <style> body { margin: 0; overflow: hidden; } canvas { display: block; } </style> </head> <body> <script type="module"> import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.152.2/build/three.module.js'; import { OrbitControls } from 'https://cdn.jsdelivr.net/npm/three@0.152.2/examples/jsm/controls/OrbitControls.js'; // SHADER CODE const fragmentShader = ` #ifdef GL_ES precision highp float; #endif uniform float iTime; uniform vec2 iResolution; #define PI 3.14159265 #define MAX_STEPS 100 #define MAX_DIST 100.0 #define SURF_DIST 0.001 // Rotate vector around y-axis vec3 rotateY(vec3 p, float a) { float c = cos(a), s = sin(a); return mat3(c, 0, s, 0, 1, 0, -s, 0, c) * p; } // Signed distance to a box float sdBox(vec3 p, vec3 b) { vec3 d = abs(p) - b; return length(max(d, 0.0)) + min(max(d.x, max(d.y, d.z)), 0.0); } // Signed distance to an ellipsoid float sdEllipsoid(vec3 p, vec3 r) { return (length(p/r) - 1.0) * min(r.x, min(r.y, r.z)); } // Bird SDF float getDistance(vec3 p) { // Body float body = sdEllipsoid(p, vec3(0.8, 1.0, 0.4)); // Wings float wingLen = 2.0; float flap = sin(iTime * 5.0) * 0.5; vec3 wing1 = p - vec3(0.0, 0.0, 0.0); wing1 = rotateY(wing1, -flap - 1.3); float wingd1 = sdBox(wing1, vec3(0.2, wingLen, 0.05)); vec3 wing2 = p - vec3(0.0, 0.0, 0.0); wing2 = rotateY(wing2, flap + 1.3); float wingd2 = sdBox(wing2, vec3(0.2, wingLen, 0.05)); float wings = min(wingd1, wingd2); return min(body, wings); } // Compute normal vec3 getNormal(vec3 p) { float d = getDistance(p); vec2 e = vec2(0.001, 0.0); vec3 n = d - vec3( getDistance(p - e.xyy), getDistance(p - e.yxy), getDistance(p - e.yyx) ); return normalize(n); } // Raymarching float rayMarch(vec3 ro, vec3 rd) { float dO = 0.0; for(int i = 0; i < MAX_STEPS; i++) { vec3 p = ro + rd * dO; float dS = getDistance(p); dO += dS; if(dO > MAX_DIST || dS < SURF_DIST) break; } return dO; } // Lighting float getLight(vec3 p) { vec3 lightPos = vec3(2.0, 5.0, 2.0); vec3 l = normalize(lightPos - p); vec3 n = getNormal(p); float dif = dot(n, l); dif = clamp(dif, 0.0, 1.0); return dif; } void mainImage( out vec4 fragColor, in vec2 fragCoord ) { vec2 uv = (fragCoord-.5*iResolution.xy)/iResolution.y; vec3 ro = vec3(0.0, 0.0, -5.0); vec3 rd = normalize(vec3(uv, 1.0)); float d = rayMarch(ro, rd); vec3 p = ro + rd * d; float brightness = 0.0; if(d < MAX_DIST) { brightness = getLight(p); } vec3 col = vec3(0.2, 0.2, 0.2) * brightness; fragColor = vec4(col, 1.0); } void main() { mainImage(gl_FragColor, gl_FragCoord.xy); } `; // THREE.JS SETUP const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000); camera.position.z = 5; const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(window.devicePixelRatio); document.body.appendChild(renderer.domElement); // Screen quad const geometry = new THREE.PlaneGeometry(2, 2); const material = new THREE.ShaderMaterial({ fragmentShader: fragmentShader, uniforms: { iTime: { value: 0 }, iResolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) } } }); const mesh = new THREE.Mesh(geometry, material); scene.add(mesh); const controls = new OrbitControls(camera, renderer.domElement); // Animate let clock = new THREE.Clock(); function animate() { requestAnimationFrame(animate); material.uniforms.iTime.value = clock.getElapsedTime(); controls.update(); renderer.render(scene, camera); } animate(); // Handle resize window.addEventListener('resize', () => { material.uniforms.iResolution.value.set(window.innerWidth, window.innerHeight); renderer.setSize(window.innerWidth, window.innerHeight); camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); }); </script> </body> </html> ```
Here's a complete HTML implementation that creates a Signed Distance Function (SDF) representation of a bird with animated flapping wings using three.js and raymarching techniques: ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>SDF Bird with Flapping Wings</title> <style> body { margin: 0; overflow: hidden; } canvas { display: block; } </style> </head> <body> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script> <script> // Scene setup const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.set(0, 0, 5); const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(window.devicePixelRatio); document.body.appendChild(renderer.domElement); // Orbit controls const controls = new THREE.OrbitControls(camera, renderer.domElement); controls.enableDamping = true; controls.dampingFactor = 0.05; // Raymarching setup const raymarchShader = { uniforms: { time: { value: 0.0 }, resolution: { value: new THREE.Vector4() } }, vertexShader: ` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `, fragmentShader: ` uniform float time; uniform vec4 resolution; varying vec2 vUv; // SDF operations float sdSphere(vec3 p, float r) { return length(p) - r; } float sdEllipsoid(vec3 p, vec3 r) { return (length(p/r) - 1.0) * min(min(r.x, r.y), r.z); } float sdCapsule(vec3 p, vec3 a, vec3 b, float r) { vec3 pa = p - a, ba = b - a; float h = clamp(dot(pa, ba)/dot(ba, ba), 0.0, 1.0); return length(pa - ba*h) - r; } float opUnion(float d1, float d2) { return min(d1, d2); } float opSmoothUnion(float d1, float d2, float k) { float h = clamp(0.5 + 0.5*(d2-d1)/k, 0.0, 1.0); return mix(d2, d1, h) - k*h*(1.0-h); } // Bird SDF float birdSDF(vec3 p) { // Animated wing flapping float wingAngle = sin(time * 3.0) * 0.5; // Body float body = sdEllipsoid(p - vec3(0, 0, 0), vec3(0.5, 0.3, 0.4)); // Head float head = sdSphere(p - vec3(0.5, 0.15, 0), 0.2); // Left wing vec3 wingLeftPos = p - vec3(-0.2, 0, -0.1); mat3 wingLeftRot = mat3( cos(wingAngle), 0, sin(wingAngle), 0, 1, 0, -sin(wingAngle), 0, cos(wingAngle) ); wingLeftPos *= wingLeftRot; float wingLeft = sdEllipsoid(wingLeftPos, vec3(0.5, 0.1, 0.2)); // Right wing (mirror of left) vec3 wingRightPos = p - vec3(-0.2, 0, 0.1); mat3 wingRightRot = mat3( cos(-wingAngle), 0, sin(-wingAngle), 0, 1, 0, -sin(-wingAngle), 0, cos(-wingAngle) ); wingRightPos *= wingRightRot; float wingRight = sdEllipsoid(wingRightPos, vec3(0.5, 0.1, 0.2)); // Tail float tail = sdCapsule(p, vec3(-0.4, 0, 0), vec3(-0.8, 0, 0), 0.05); // Combine shapes with smooth blending float bird = opSmoothUnion(body, head, 0.1); bird = opSmoothUnion(bird, wingLeft, 0.1); bird = opSmoothUnion(bird, wingRight, 0.1); bird = opSmoothUnion(bird, tail, 0.05); return bird; } // Raymarcher vec3 calcNormal(vec3 p) { const float h = 0.0001; const vec2 k = vec2(1,-1); return normalize( k.xyy*birdSDF( p + k.xyy*h ) + k.yyx*birdSDF( p + k.yyx*h ) + k.yxy*birdSDF( p + k.yxy*h ) + k.xxx*birdSDF( p + k.xxx*h ) ); } vec3 render(vec3 ro, vec3 rd) { vec3 p = ro; float t = 0.0; float d; // Raymarching loop for (int i = 0; i < 64; i++) { d = birdSDF(p); if (d < 0.001) break; t += d; p = ro + rd * t; } // Shading if (d < 0.001) { vec3 n = calcNormal(p); vec3 lightPos = vec3(2.0, 2.0, 2.0); vec3 lightDir = normalize(lightPos - p); // Basic material coloring (blue bird with yellow beak) vec3 material = mix( vec3(0.2, 0.4, 1.0), // Blue body vec3(1.0, 0.8, 0.2), // Yellow beak smoothstep(0.55, 0.6, p.x) * (1.0 - smoothstep(0.4, 0.45, p.y)) ); // Diffuse lighting float diff = max(0.0, dot(n, lightDir)); vec3 col = material * diff; // Ambient light col += material * 0.2; // Rim lighting col += pow(1.0 - abs(dot(n, rd)), 4.0) * vec3(0.2, 0.3, 0.5); return col; } // Background color (sky gradient) return vec3(0.5 + rd.y * 0.5); } void main() { vec2 uv = vUv * 2.0 - 1.0; uv.x *= resolution.z; // Ray setup vec3 ro = vec3(0, 0, -2); vec3 rd = normalize(vec3(uv, 2.0)); // Render vec3 col = render(ro, rd); // Gamma correction col = pow(col, vec3(0.4545)); gl_FragColor = vec4(col, 1.0); } ` }; // Shader material const material = new THREE.ShaderMaterial({ uniforms: raymarchShader.uniforms, vertexShader: raymarchShader.vertexShader, fragmentShader: raymarchShader.fragmentShader }); // Fullscreen quad const quad = new THREE.Mesh( new THREE.PlaneGeometry(2, 2), material ); scene.add(quad); // Handle resize function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); material.uniforms.resolution.value.set( window.innerWidth, window.innerHeight, window.innerWidth / window.innerHeight, 1.0 ); } window.addEventListener('resize', onWindowResize); onWindowResize(); // Animation loop function animate() { requestAnimationFrame(animate); controls.update(); material.uniforms.time.value = performance.now() / 1000; renderer.render(scene, camera); } animate(); </script> </body> </html> ``` This implementation includes: 1. A bird SDF composed of: - An ellipsoid for the body - A sphere for the head - Two animated ellipsoids for wings (flapping up and down) - A capsule for the tail 2. Raymarching with: - 64 iterations for performance/quality balance - Normal calculation for proper lighting - Basic shading with diffuse lighting and ambient occlusion 3. Coloring: - Blue body color - Yellow beak - Smooth blending between parts 4. Animation: - Wings flap smoothly using sine wave - Orbit controls to view from any angle The bird will flap its wings continuously with smooth animation. You can rotate the view using mouse controls. The background shows a simple sky gradient.
To accomplish this task, we'll go through the steps of creating a Signed Distance Field (SDF) representation of a bird with animated flapping wings and rendering it using a raymarcher with Three.js. We'll also add basic coloring and shading. First, let's define the SDF for our bird. For simplicity, we will represent the bird using basic shapes: a sphere for the body and two boxes for the wings. Next, we'll set up a Three.js scene and implement a raymarcher to render our SDF. Here's a complete example: ```html <!DOCTYPE html> <html> <head> <title>SDF Bird with Three.js</title> <style> body { margin: 0; } canvas { display: block; } </style> </head> <body> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <script> // Define the SDF for the bird function sdfBird(p, time) { let body = sphere(p, new THREE.Vector3(0, 0, 0), 0.5); let wingAngle = Math.sin(time) * 0.5; let leftWing = box(rotateY(new THREE.Vector3(p.x + 0.4, p.y, p.z), wingAngle), new THREE.Vector3(0.3, 0.1, 0.1)); let rightWing = box(rotateY(new THREE.Vector3(p.x - 0.4, p.y, p.z), -wingAngle), new THREE.Vector3(0.3, 0.1, 0.1)); return union(union(body, leftWing), rightWing); } // SDF operations function sphere(p, center, radius) { return p.clone().sub(center).length() - radius; } function box(p, size) { let q = p.clone().abs().sub(size); return Math.max(q.x, Math.max(q.y, q.z)); } function union(d1, d2) { return Math.min(d1, d2); } function rotateY(p, angle) { let c = Math.cos(angle); let s = Math.sin(angle); let x = p.x * c - p.z * s; let z = p.x * s + p.z * c; return new THREE.Vector3(x, p.y, z); } // Raymarcher function raymarch(ro, rd, time) { let dO = 0.; for (let i = 0; i < 100; i++) { let p = ro.clone().add(rd.clone().multiplyScalar(dO)); let dS = sdfBird(p, time); dO += dS; if (dS < 0.001) { // Simple normal estimation let eps = 0.001; let normal = new THREE.Vector3( sdfBird(p.clone().add(new THREE.Vector3(eps, 0, 0)), time) - dS, sdfBird(p.clone().add(new THREE.Vector3(0, eps, 0)), time) - dS, sdfBird(p.clone().add(new THREE.Vector3(0, 0, eps)), time) - dS ).normalize(); // Basic shading let lightDir = new THREE.Vector3(1, 1, 1).normalize(); let diffuse = Math.max(0., normal.dot(lightDir)); return new THREE.Vector3(0.8, 0.5, 0.2).multiplyScalar(diffuse); // Brownish color } if (dO > 20.) { return new THREE.Vector3(0.5, 0.7, 1); // Sky blue } } return new THREE.Vector3(0, 0, 0); // Background } // Camera settings let camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.z = 3; // Renderer let renderer = new THREE.WebGLRenderer({ canvas: document.createElement('canvas'), antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); let clock = new THREE.Clock(); function animate() { requestAnimationFrame(animate); let time = clock.getElapsedTime(); let canvas = renderer.domElement; let ctx = canvas.getContext('2d'); // Not needed, using WebGLRenderer directly camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setClearColor(0xffffff, 1); // Clear with white // Render the scene using raymarching (custom shader or direct rendering) // For simplicity, we'll directly compute and render the image here // Normally, you'd pass this to a shader for GPU acceleration let image = new Uint8ClampedArray(window.innerWidth * window.innerHeight * 4); for (let y = 0; y < window.innerHeight; y++) { for (let x = 0; x < window.innerWidth; x++) { let uv = new THREE.Vector2(x / window.innerWidth, y / window.innerHeight).multiplyScalar(2).subScalar(1); uv.x *= window.innerWidth / window.innerHeight; // Aspect correction let ro = camera.position; let rd = new THREE.Vector3(uv.x, uv.y, -1).unproject(camera).sub(ro).normalize(); let color = raymarch(ro, rd, time); let index = (y * window.innerWidth + x) * 4; image[index + 0] = color.x * 255; image[index + 1] = color.y * 255; image[index + 2] = color.z * 255; image[index + 3] = 255; // Alpha } } // Directly put the pixel data into a canvas (not using Three.js rendering pipeline for the raymarcher output) let imgData = new ImageData(image, window.innerWidth, window.innerHeight); ctx.putImageData(imgData, 0, 0); } animate(); </script> </body> </html> ``` This code demonstrates a simple raymarched SDF representation of a bird with animated wings. The bird is shaded with a basic diffuse lighting model, and the wings flap as the animation progresses. Please note that the rendering is done directly on a 2D canvas context using JavaScript, which is not the most efficient method, especially for complex scenes or larger resolutions. For better performance, you should consider implementing the raymarcher in a WebGL shader. To optimize this example, you could port the raymarching logic to a GLSL shader and use Three.js's `ShaderMaterial` to render the SDF. This would allow the raymarching to be executed on the GPU, significantly improving performance. For brevity and simplicity, the example provided directly computes and renders the image using JavaScript, bypassing Three.js's rendering pipeline for the raymarched content.
Response not available
# Bird SDF Representation with Animated Flapping Wings In this example, we'll create a simple SDF (Signed Distance Field) representation of a bird with animated flapping wings using JavaScript and the Three.js library for rendering. ## SDF Representation We'll define the SDF for the bird's body and wings separately and then combine them. ```javascript // BirdSDF.js class BirdSDF { constructor() { this.wingAngle = 0; } updateWingAngle(angle) { this.wingAngle = angle; } // SDF for a sphere sphereSDF(pos, center, radius) { return length(pos - center) - radius; } // SDF for a box boxSDF(pos, center, size) { let q = abs(pos - center) - size / 2; return length(max(q, 0)) + min(max(q.x, max(q.y, q.z)), 0); } // SDF for the bird's body bodySDF(pos) { let center = new THREE.Vector3(0, 0, 0); let radius = 0.5; return this.sphereSDF(pos, center, radius); } // SDF for a wing wingSDF(pos, angle) { let center = new THREE.Vector3(1, 0, 0); let size = new THREE.Vector3(1, 0.1, 0.1); let rotationMatrix = new THREE.Matrix4(); rotationMatrix.makeRotationY(angle); let rotatedPos = new THREE.Vector3(); rotatedPos.copy(pos); rotatedPos.applyMatrix4(rotationMatrix); return this.boxSDF(rotatedPos, center, size); } // Combine SDFs for the bird's body and wings sdf(pos) { let bodySDF = this.bodySDF(pos); let wing1SDF = this.wingSDF(pos, this.wingAngle); let wing2SDF = this.wingSDF(pos, -this.wingAngle); return min(min(bodySDF, wing1SDF), wing2SDF); } } ``` ## Raymarcher Rendering with Three.js ```html <!-- index.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Bird SDF Raymarcher</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <style> body { margin: 0; background-color: #f0f0f0; overflow: hidden; } #canvas { width: 100%; height: 100vh; display: block; } </style> </head> <body> <canvas id="canvas"></canvas> <script src="BirdSDF.js"></script> <script> let canvas = document.getElementById('canvas'); let scene = new THREE.Scene(); let camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100); camera.position.z = 5; let renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); let birdSDF = new BirdSDF(); let time = 0; function animate() { requestAnimationFrame(animate); time += 0.01; birdSDF.updateWingAngle(Math.sin(time) * 0.5); let raymarchShader = ` uniform vec2 resolution; uniform float time; #include <common> float sphereSDF(vec3 pos, vec3 center, float radius) { return length(pos - center) - radius; } float boxSDF(vec3 pos, vec3 center, vec3 size) { vec3 q = abs(pos - center) - size / 2.0; return length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0); } float wingSDF(vec3 pos, float angle) { vec3 center = vec3(1.0, 0.0, 0.0); vec3 size = vec3(1.0, 0.1, 0.1); vec3 rotatedPos = pos; rotatedPos.x = pos.x * cos(angle) - pos.z * sin(angle); rotatedPos.z = pos.x * sin(angle) + pos.z * cos(angle); return boxSDF(rotatedPos, center, size); } float bodySDF(vec3 pos) { vec3 center = vec3(0.0, 0.0, 0.0); float radius = 0.5; return sphereSDF(pos, center, radius); } float sdf(vec3 pos, float wingAngle) { float body = bodySDF(pos); float wing1 = wingSDF(pos, wingAngle); float wing2 = wingSDF(pos, -wingAngle); return min(min(body, wing1), wing2); } vec3 normal(vec3 p, float wingAngle) { vec2 e = vec2(0.001, 0); return normalize(vec3( sdf(p + e.xyy, wingAngle) - sdf(p - e.xyy, wingAngle), sdf(p + e.yxy, wingAngle) - sdf(p - e.yxy, wingAngle), sdf(p + e.yyx, wingAngle) - sdf(p - e.yyx, wingAngle) )); } float raymarch(vec3 ro, vec3 rd, float wingAngle) { float t = 0.0; for (int i = 0; i < 64; i++) { vec3 pos = ro + rd * t; float d = sdf(pos, wingAngle); if (d < 0.001) { return t; } t += d; if (t > 10.0) { break; } } return -1.0; } void main() { vec2 uv = (gl_FragCoord.xy - 0.5 * resolution.xy) / resolution.y; vec3 ro = vec3(0.0, 0.0, 5.0); vec3 rd = normalize(vec3(uv, -1.0)); float wingAngle = sin(time) * 0.5; float t = raymarch(ro, rd, wingAngle); if (t > 0.0) { vec3 pos = ro + rd * t; vec3 n = normal(pos, wingAngle); float diffuse = max(0.0, dot(n, normalize(vec3(-1.0, 1.0, 1.0)))); gl_FragColor = vec4(vec3(diffuse), 1.0); } else { gl_FragColor = vec4(vec3(0.0), 1.0); } } `; let uniforms = { resolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) }, time: { value: time } }; let material = new THREE.ShaderMaterial({ vertexShader: ` void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `, fragmentShader: raymarchShader, uniforms: uniforms }); let plane = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), material); scene.clear(); scene.add(plane); renderer.render(scene, camera); } animate(); </script> </body> </html> ``` Note that this is a basic example, and there are many ways to improve and customize it. The raymarcher can be optimized, and the SDF can be made more complex and detailed. Additionally, you can add more features like lighting, textures, and animations.
Here is a complete HTML page that uses three‑js and a fragment‑shader ray‑marcher to render a simple bird made from signed‑distance functions. The wings flap over time, and basic coloring / shading is applied. ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>SDF Bird with Flapping Wings (Three.js Raymarcher)</title> <style> body { margin:0; overflow:hidden; background:#111; } canvas { display:block; width:100vw; height:100vh; } </style> </head> <body> <script src="https://cdn.jsdelivr.net/npm/three@0.166.1/build/three.min.js"></script> <script id="fragmentShader" type="x-shader/x-fragment"> uniform float uTime; uniform vec2 uResolution; // ---------- Helper Functions ---------- // Smooth minimum float smin( float a, float b, float k ) { float h = clamp( 0.5 + 0.5*(b-a)/k, 0.0, 1.0 ); return mix( b, a, h ) - k*h*(1.0-h); } // Rotation around Y axis mat3 rotY( float a ){ float c = cos(a), s = sin(a); return mat3( c, 0.0, -s, 0.0,1.0, 0.0, s, 0.0, c ); } // ---------- Signed Distance Functions ---------- // Ellipsoid (scaled sphere) float sdEllipsoid( vec3 p, vec3 r ){ // scale space vec3 q = p / r; return length(q) - 1.0; } // Bird body (elongated ellipsoid) float bodySDF( vec3 p ){ return sdEllipsoid( p, vec3(0.2,0.12,0.5) ); } // Wing (thin ellipsoid) with flapping animation float wingSDF( vec3 p, float side, float flap ){ // side = +1 for right wing, -1 for left wing // flap controls the opening angle (radians) // Position wing at the side of the body p = p - vec3( side*0.25, 0.0, 0.0 ); // Rotate wing around its root axis to flap float ang = side * flap; mat3 R = rotY( ang ); p = R * p; // Wing shape return sdEllipsoid( p, vec3(0.05,0.02,0.3) ); } // Combine parts into a bird float birdSDF( vec3 p ){ // Flap angle oscillates with time float flap = sin(uTime*4.0) * 0.5; // +/-0.5 rad (~30°) float dBody = bodySDF(p); float dWingR = wingSDF(p, +1.0, flap); float dWingL = wingSDF(p, -1.0, flap); float d = smin(dBody, dWingR, 0.08); d = smin(d, dWingL, 0.08); return d; } // ---------- Raymarcher ---------- const int MAX_STEPS = 128; const float MAX_DIST = 20.0; const float SURF_EPS = 0.001; float map( vec3 p ){ return birdSDF(p); } vec3 getNormal( vec3 p ){ const vec2 e = vec2(0.001,0); return normalize( vec3( map(p+e.xyy) - map(p-e.xyy), map(p+e.yxy) - map(p-e.yxy), map(p+e.yyx) - map(p-e.yyx) )); } // Simple diffuse + simple ambient vec3 shade( vec3 p, vec3 rd ){ vec3 n = getNormal(p); vec3 lightDir = normalize(vec3(0.8,0.5,0.6)); float diff = max(dot(n,lightDir),0.0); vec3 col = vec3(0.9,0.7,0.5); // base bird colour vec3 ambient = vec3(0.2); return col * (diff + ambient); } // Camera vec3 camPos = vec3(0.0,0.2,2.0); vec3 camTarget = vec3(0.0,0.0,0.0); float fov = 45.0; void main(){ // Normalised pixel coordinates ([-1,1] range) vec2 uv = (gl_FragCoord.xy*2.0 - uResolution)/uResolution.y; // Camera basis vec3 forward = normalize(camTarget - camPos); vec3 right = normalize(cross(forward, vec3(0.0,1.0,0.0))); vec3 up = cross(right, forward); // Ray direction float focalLen = 1.0 / tan(radians(fov)*0.5); vec3 rd = normalize( uv.x*right + uv.y*up + focalLen*forward ); // Raymarch float t = 0.0; float d; for(int i=0;i<MAX_STEPS;i++){ vec3 p = camPos + rd*t; d = map(p); if(d < SURF_EPS){ // hit vec3 col = shade(p, rd); gl_FragColor = vec4(col,1.0); return; } if(t>MAX_DIST) break; t += d; } // background gl_FragColor = vec4(0.1,0.12,0.15,1.0); } </script> <script> // ----------- Three.js Boilerplate ----------- const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); const scene = new THREE.Scene(); const camera = new THREE.Camera(); // dummy, we compute rays in shader // Full‑screen quad const geometry = new THREE.PlaneGeometry(2, 2); const material = new THREE.ShaderMaterial({ fragmentShader: document.getElementById('fragmentShader').textContent, uniforms: { uTime: { value: 0 }, uResolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) } } }); const mesh = new THREE.Mesh(geometry, material); scene.add(mesh); // Resize handling window.addEventListener('resize', () => { renderer.setSize(window.innerWidth, window.innerHeight); material.uniforms.uResolution.value.set(window.innerWidth, window.innerHeight); }); // Animation loop function animate(time) { material.uniforms.uTime.value = time * 0.001; // seconds renderer.render(scene, camera); requestAnimationFrame(animate); } requestAnimationFrame(animate); </script> </body> </html> ```
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>SDF Bird Raymarcher</title> <style> body { margin: 0; overflow: hidden; } canvas { display: block; } </style> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> </head> <body> <script> // Set up Three.js scene const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.z = 5; const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // Vertex shader for full-screen quad const vertexShader = ` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `; // Fragment shader with SDF raymarcher const fragmentShader = ` uniform float iTime; // Uniform for animation time varying vec2 vUv; // SDF primitives float sdSphere(vec3 p, float r) { return length(p) - r; } float sdBox(vec3 p, vec3 b) { vec3 q = abs(p) - b; return length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0); } // Union operation float opUnion(float d1, float d2) { return min(d1, d2); } // SDF for the bird float sdBird(vec3 p) { float time = iTime; // Use time for animation // Body: Elongated sphere vec3 bodyPos = p - vec3(0.0, 0.0, 0.0); float body = sdSphere(bodyPos, 0.8); // Head: Smaller sphere vec3 headPos = p - vec3(1.2, 0.0, 0.0); float head = sdSphere(headPos, 0.4); // Wings: Boxes with flapping animation float flapAngle = sin(time * 2.0) * 0.5; // Simple sine wave for flapping // Wing 1: Rotate around Y-axis vec3 wing1Base = p - vec3(0.0, 0.0, 0.5); mat2 rotMat = mat2(cos(flapAngle), -sin(flapAngle), sin(flapAngle), cos(flapAngle)); vec3 wing1Local = vec3(wing1Base.x, wing1Base.y * rotMat[0][0] + wing1Base.z * rotMat[1][0], wing1Base.y * rotMat[0][1] + wing1Base.z * rotMat[1][1]); // Simplified 2D rotation in YZ plane float wing1 = sdBox(wing1Local - vec3(0.0, 0.0, 0.5), vec3(0.1, 0.05, 0.6)); // Wing 2: Mirrored vec3 wing2Base = p - vec3(0.0, 0.0, -0.5); vec3 wing2Local = vec3(wing2Base.x, wing2Base.y * rotMat[0][0] + wing2Base.z * rotMat[1][0], wing2Base.y * rotMat[0][1] + wing2Base.z * rotMat[1][1]); float wing2 = sdBox(wing2Local - vec3(0.0, 0.0, -0.5), vec3(0.1, 0.05, 0.6)); // Combine shapes float sceneDist = opUnion(body, head); sceneDist = opUnion(sceneDist, wing1); sceneDist = opUnion(sceneDist, wing2); return sceneDist; } // Raymarching function vec3 rayDirection(vec2 uv, vec3 cameraPos, vec3 lookAt) { vec3 forward = normalize(lookAt - cameraPos); vec3 right = normalize(cross(vec3(0.0, 1.0, 0.0), forward)); vec3 up = cross(forward, right); return normalize(forward + uv.x * right + uv.y * up); } vec3 calcNormal(vec3 p) { float eps = 0.001; return normalize(vec3( sdBird(p + vec3(eps, 0.0, 0.0)) - sdBird(p + vec3(-eps, 0.0, 0.0)), sdBird(p + vec3(0.0, eps, 0.0)) - sdBird(p + vec3(0.0, -eps, 0.0)), sdBird(p + vec3(0.0, 0.0, eps)) - sdBird(p + vec3(0.0, 0.0, -eps)) )); } void main() { vec2 uv = (vUv - 0.5) * 2.0; // Center UV uv.x *= (window.innerWidth / window.innerHeight); // Aspect ratio vec3 cameraPos = vec3(0.0, 0.0, -3.0); // Camera position vec3 lookAt = vec3(0.0, 0.0, 0.0); // Look at origin vec3 rd = rayDirection(uv, cameraPos, lookAt); float t = 0.0; // Ray march distance float maxDist = 100.0; float step = 0.01; // Small step for accuracy vec3 color = vec3(0.1, 0.1, 0.1); // Background color for(int i = 0; i < 100; i++) { // March up to 100 steps vec3 p = cameraPos + rd * t; float d = sdBird(p); if(d < 0.01) { // Hit threshold vec3 normal = calcNormal(p); vec3 lightDir = normalize(vec3(1.0, 1.0, 1.0)); // Simple light direction float diffuse = max(dot(normal, lightDir), 0.0); // Diffuse shading // Basic coloring: Body brown, wings gray float bodyDist = sdSphere(p - vec3(0.0, 0.0, 0.0), 0.8); if(bodyDist < 0.01) color = vec3(0.5, 0.25, 0.0) * (diffuse + 0.2); // Brown with shading else color = vec3(0.5, 0.5, 0.5) * (diffuse + 0.2); // Gray for wings break; } t += d; // Step by distance if(t > maxDist) break; } gl_FragColor = vec4(color, 1.0); } `; // Create the shader material const material = new THREE.ShaderMaterial({ uniforms: { iTime: { value: 0.0 } }, vertexShader: vertexShader, fragmentShader: fragmentShader }); const geometry = new THREE.PlaneGeometry(2, 2); // Full-screen quad const mesh = new THREE.Mesh(geometry, material); scene.add(mesh); // Animation loop function animate() { requestAnimationFrame(animate); material.uniforms.iTime.value = performance.now() / 1000; // Update time uniform renderer.render(scene, camera); } animate(); // Handle window resize window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); </script> </body> </html> ```