토론

동화책 속 아이스크림 계곡을 배경으로 한 기발한 스톱모션 스타일 애니메이션

Wikiprompt, 무료 프롬프트 백과사전에서

Alex Prompter

2026년 7월 24일

동화책 속 아이스크림 계곡을 배경으로 한 기발한 스톱모션 스타일 애니메이션 Three.js에서 절차적으로 생성된 애니메이션 복셀 산악 계곡 세계를 렌더링하기 위한 상세한 단일 파일 프롬프트로, 배회하는 주민, 낮/밤 주기, 커스텀 궤도 카메라를 포함합니다.

프롬프트 내용저장

🌐
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Voxel Mountain Valley</title> <style> body { margin: 0; overflow: hidden; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } #info { position: absolute; bottom: 20px; left: 20px; background: rgba(0,0,0,0.5); color: white; padding: 8px 16px; border-radius: 20px; font-size: 14px; backdrop-filter: blur(4px); pointer-events: none; letter-spacing: 0.5px; border: 1px solid rgba(255,255,255,0.2); user-select: none; z-index: 100; } #info i { font-style: normal; display: inline-block; margin-right: 8px; } </style> </head> <body> <div id="info">🖱️ Drag to rotate - Scroll to zoom</div> <!-- Three.js from CDN --> <script type="importmap"> { "imports": { "three": "https://unpkg.com/three@0.128.0/build/three.module.js" } } </script> <script type="module"> import * as THREE from 'three'; // --- Setup Scene, Camera, Renderer --- const scene = new THREE.Scene(); scene.background = new THREE.Color(0x87CEEB); // sky blue, will change with day/night const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.set(25, 18, 30); camera.lookAt(0, 4, 0); const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; document.body.appendChild(renderer.domElement); // --- Lights --- // Ambient light (will be adjusted in day/night cycle) const ambientLight = new THREE.AmbientLight(0x404060); scene.add(ambientLight); // Directional light (sun/moon) const sunLight = new THREE.DirectionalLight(0xffeedd, 1.2); sunLight.position.set(30, 30, 20); sunLight.castShadow = true; sunLight.shadow.mapSize.width = 1024; sunLight.shadow.mapSize.height = 1024; const d = 30; sunLight.shadow.camera.left = -d; sunLight.shadow.camera.right = d; sunLight.shadow.camera.top = d; sunLight.shadow.camera.bottom = -d; sunLight.shadow.camera.near = 1; sunLight.shadow.camera.far = 60; scene.add(sunLight); // Stars (created later, hidden by default) const stars = new THREE.Group(); const starGeo = new THREE.BufferGeometry(); const starCount = 600; const starPos = new Float32Array(starCount * 3); for (let i = 0; i < starCount * 3; i += 3) { starPos[i] = (Math.random() - 0.5) * 200; starPos[i+1] = (Math.random() * 60) + 10; starPos[i+2] = (Math.random() - 0.5) * 200; } starGeo.setAttribute('position', new THREE.BufferAttribute(starPos, 3)); const starMat = new THREE.PointsMaterial({ color: 0xffffff, size: 0.15, transparent: true }); const starPoints = new THREE.Points(starGeo, starMat); stars.add(starPoints); scene.add(stars); // --- Ground / Terrain (voxel style) --- // We'll build a heightmap using a simple function: peaks at corners, valley in middle const terrainGroup = new THREE.Group(); const size = 30; // grid size (30x30) const voxelSize = 1.0; const heightMap = []; // Generate heights for (let x = 0; x < size; x++) { heightMap[x] = []; for (let z = 0; z < size; z++) { // distance from center const dx = (x - size/2) / (size/2); const dz = (z - size/2) / (size/2); const dist = Math.sqrt(dx*dx + dz*dz); // mountain peaks at corners, valley in middle let h = 0; // corner mountains const cornerFactor = Math.max(0, 1 - dist * 0.8); h += cornerFactor * 6.0; // rolling hills h += Math.sin(x * 0.5) * Math.cos(z * 0.5) * 1.5; // central valley lower h += (1 - Math.min(1, dist)) * 2.0; // clamp h = Math.max(0.5, Math.min(8, h)); heightMap[x][z] = Math.floor(h); } } // Create voxel cubes for terrain const terrainMat = new THREE.MeshStandardMaterial({ color: 0x6b8e23, roughness: 0.8 }); const snowMat = new THREE.MeshStandardMaterial({ color: 0xffffff, roughness: 0.6 }); const stoneMat = new THREE.MeshStandardMaterial({ color: 0x8a8a8a, roughness: 0.9 }); const dirtMat = new THREE.MeshStandardMaterial({ color: 0x8B5A2B, roughness: 0.9 }); // We'll store all cubes in a group for performance (but we'll merge later? For simplicity, individual cubes but limited count) // To keep voxel count modest, we use a single merged geometry? But for simplicity and to keep colors, we'll use individual cubes with shared geometry. // But 30x30x8 = 7200 cubes max, but we only place surface and some below. Let's optimize by only placing visible cubes. // We'll place a cube for each column at heightMap level, plus some below for thickness. const cubeGeo = new THREE.BoxGeometry(1, 1, 1); // To reduce draw calls, we'll use InstancedMesh? But for simplicity and to avoid complexity, we'll just add cubes. // But 30x30 = 900 columns, each with 1-3 cubes = ~2000 cubes, acceptable. // However, we want to keep it smooth. Let's use a merged geometry approach? Actually, let's just add cubes but with shared geometry and materials. // But we need different colors (snow, grass, stone). We'll use groups. const grassCubes = []; const snowCubes = []; const stoneCubes = []; for (let x = 0; x < size; x++) { for (let z = 0; z < size; z++) { const h = heightMap[x][z]; // place top cube const posX = x - size/2 + 0.5; const posZ = z - size/2 + 0.5; // determine material based on height let mat; if (h > 6.5) mat = snowMat; else if (h > 4.5) mat = stoneMat; else mat = terrainMat; const cube = new THREE.Mesh(cubeGeo, mat); cube.position.set(posX, h - 0.5, posZ); cube.castShadow = true; cube.receiveShadow = true; terrainGroup.add(cube); // add a couple below for thickness (only for non-snow to save) if (h > 1) { const cube2 = new THREE.Mesh(cubeGeo, h > 6.5 ? stoneMat : dirtMat); cube2.position.set(posX, h - 1.5, posZ); cube2.castShadow = true; cube2.receiveShadow = true; terrainGroup.add(cube2); } if (h > 3) { const cube3 = new THREE.Mesh(cubeGeo, dirtMat); cube3.position.set(posX, h - 2.5, posZ); cube3.castShadow = true; cube3.receiveShadow = true; terrainGroup.add(cube3); } } } scene.add(terrainGroup); // --- River (blue voxels) --- // Simple river through valley: from x=5 to x=25 at z=15 area const waterMat = new THREE.MeshStandardMaterial({ color: 0x3a6ea5, roughness: 0.2, transparent: true, opacity: 0.8 }); for (let x = 8; x < 22; x++) { for (let z = 12; z < 18; z++) { // only if terrain height is low (valley) const h = heightMap[x][z]; if (h < 3.5) { const waterCube = new THREE.Mesh(cubeGeo, waterMat); waterCube.position.set(x - size/2 + 0.5, 0.2, z - size/2 + 0.5); waterCube.scale.y = 0.3; waterCube.castShadow = false; waterCube.receiveShadow = true; terrainGroup.add(waterCube); } } } // --- Trees (pine trees) --- const treeGroup = new THREE.Group(); const trunkMat = new THREE.MeshStandardMaterial({ color: 0x8B4513 }); const leafMat = new THREE.MeshStandardMaterial({ color: 0x2e8b57 }); function addTree(x, z) { const h = heightMap[Math.floor(x + size/2)]?.[Math.floor(z + size/2)]; if (h === undefined || h > 5.5) return; // only in valley/grass areas const trunk = new THREE.Mesh(cubeGeo, trunkMat); trunk.position.set(x, h + 0.5, z); trunk.scale.set(0.4, 1.2, 0.4); trunk.castShadow = true; trunk.receiveShadow = true; treeGroup.add(trunk); // leaves (3 cubes) for (let i = 0; i < 3; i++) { const leaf = new THREE.Mesh(cubeGeo, leafMat); leaf.position.set(x + (i-1)*0.6, h + 1.8 + i*0.5, z + (i%2)*0.5); leaf.scale.set(0.8, 0.8, 0.8); leaf.castShadow = true; leaf.receiveShadow = true; treeGroup.add(leaf); } } // Place trees randomly but avoid water and high peaks for (let i = 0; i < 40; i++) { const x = (Math.random() - 0.5) * 20; const z = (Math.random() - 0.5) * 20; const h = heightMap[Math.floor(x + size/2)]?.[Math.floor(z + size/2)]; if (h !== undefined && h > 1.5 && h < 5.5) { // avoid river area if (z > -2 && z < 2 && x > -5 && x < 5) continue; addTree(x, z); } } scene.add(treeGroup); // --- Village houses (5-8) --- const houseGroup = new THREE.Group(); const wallMat = new THREE.MeshStandardMaterial({ color: 0xDEB887 }); const roofMat = new THREE.MeshStandardMaterial({ color: 0x8B0000 }); const windowMat = new THREE.MeshStandardMaterial({ color: 0xffaa00, emissive: 0xffaa00, emissiveIntensity: 0.5 }); function addHouse(x, z) { const h = heightMap[Math.floor(x + size/2)]?.[Math.floor(z + size/2)]; if (h === undefined || h > 4.5) return; // base const base = new THREE.Mesh(cubeGeo, wallMat); base.position.set(x, h + 0.5, z); base.scale.set(1.8, 1.0, 1.8); base.castShadow = true; base.receiveShadow = true; houseGroup.add(base); // roof const roof = new THREE.Mesh(cubeGeo, roofMat); roof.position.set(x, h + 1.5, z); roof.scale.set(2.0, 0.4, 2.0); roof.castShadow = true; roof.receiveShadow = true; houseGroup.add(roof); // windows (glowing) const win1 = new THREE.Mesh(cubeGeo, windowMat); win1.position.set(x - 0.6, h + 0.8, z + 0.9); win1.scale.set(0.4, 0.4, 0.1); win1.castShadow = false; houseGroup.add(win1); const win2 = new THREE.Mesh(cubeGeo, windowMat); win2.position.set(x + 0.6, h + 0.8, z + 0.9); win2.scale.set(0.4, 0.4, 0.1); win2.castShadow = false; houseGroup.add(win2); } // Place houses in a cluster const housePositions = [ [-4, -2], [-2, -3], [0, -2], [2, -3], [4, -2], [-3, 0], [3, 0] ]; housePositions.forEach(pos => addHouse(pos[0], pos[1])); scene.add(houseGroup); // --- Villagers and animals (blocky characters) --- const characters = []; const charMat = new THREE.MeshStandardMaterial({ color: 0xE0B0FF }); // lavender const sheepMat = new THREE.MeshStandardMaterial({ color: 0xFAFAD2 }); function createCharacter(type) { const group = new THREE.Group(); // body const body = new THREE.Mesh(cubeGeo, type === 'sheep' ? sheepMat : charMat); body.scale.set(0.6, 0.8, 0.6); body.position.y = 0.4; body.castShadow = true; body.receiveShadow = true; group.add(body); // head const head = new THREE.Mesh(cubeGeo, type === 'sheep' ? sheepMat : new THREE.MeshStandardMaterial({ color: 0xFFDAB9 })); head.scale.set(0.4, 0.4, 0.4); head.position.y = 1.0; head.castShadow = true; head.receiveShadow = true; group.add(head); // legs const legMat = new THREE.MeshStandardMaterial({ color: 0x8B4513 }); for (let i = 0; i < 4; i++) { const leg = new THREE.Mesh(cubeGeo, legMat); leg.scale.set(0.15, 0.3, 0.15); leg.position.set((i%2===0?-0.2:0.2), 0.15, (i<2?-0.2:0.2)); leg.castShadow = true; leg.receiveShadow = true; group.add(leg); } return group; } // Place characters for (let i = 0; i < 8; i++) { const char = createCharacter(i < 3 ? 'sheep' : 'villager'); const x = (Math.random() - 0.5) * 12; const z = (Math.random() - 0.5) * 12; const h = heightMap[Math.floor(x + size/2)]?.[Math.floor(z + size/2)]; if (h === undefined || h > 4.5) continue; char.position.set(x, h, z); char.userData = { target: new THREE.Vector3(x, h, z), speed: 0.5 + Math.random() * 0.3, pause: 0, waddlePhase: Math.random() * Math.PI * 2 }; scene.add(char); characters.push(char); } // --- Clouds (blocky) --- const cloudGroup = new THREE.Group(); const cloudMat = new THREE.MeshStandardMaterial({ color: 0xffffff, transparent: true, opacity: 0.8 }); for (let i = 0; i < 6; i++) { const cloud = new THREE.Group(); const cubes = []; for (let j = 0; j < 5; j++) { const c = new THREE.Mesh(cubeGeo, cloudMat); c.position.set((j-2)*0.8, Math.random()*0.3, (j%2)*0.5); c.scale.set(0.8, 0.3, 0.6); c.castShadow = false; cloud.add(c); } cloud.position.set((Math.random()-0.5)*30, 12 + Math.random()*3, (Math.random()-0.5)*30); cloud.userData.speed = 0.02 + Math.random() * 0.03; cloudGroup.add(cloud); } scene.add(cloudGroup); // --- Day/Night Cycle variables --- let time = 0; // 0 to 60 seconds const cycleDuration = 60; // --- Camera Orbit (custom) --- let cameraAngle = 0; let cameraRadius = 25; let cameraHeight = 15; let isDragging = false; let previousMouse = { x: 0, y: 0 }; let autoRotate = true; let autoRotateSpeed = 0.002; // Event listeners renderer.domElement.addEventListener('mousedown', (e) => { isDragging = true; autoRotate = false; previousMouse = { x: e.clientX, y: e.clientY }; }); window.addEventListener('mouseup', () => { isDragging = false; autoRotate = true; }); window.addEventListener('mousemove', (e) => { if (isDragging) { const dx = e.clientX - previousMouse.x; const dy = e.clientY - previousMouse.y; cameraAngle -= dx * 0.005; cameraHeight += dy * 0.05; cameraHeight = Math.max(5, Math.min(30, cameraHeight)); previousMouse = { x: e.clientX, y: e.clientY }; } }); renderer.domElement.addEventListener('wheel', (e) => { cameraRadius += e.deltaY * 0.02; cameraRadius = Math.max(10, Math.min(45, cameraRadius)); }); // --- Animation Loop --- function animate() { requestAnimationFrame(animate); // Day/night cycle time = (time + 0.016) % cycleDuration; // ~60 sec const t = time / cycleDuration; // Sun position const sunAngle = t * Math.PI * 2; sunLight.position.set(Math.sin(sunAngle) * 30, Math.cos(sunAngle) * 25 + 10, 20); // Light colors const dayColor = new THREE.Color(0xfff5e0); const nightColor = new THREE.Color(0x8a9bb8); const sunsetColor = new THREE.Color(0xffaa66); let lightColor; let intensity; let ambientIntensity; let bgColor; // Determine phase if (t < 0.25) { // morning const f = t / 0.25; lightColor = dayColor.clone().lerp(sunsetColor, f); intensity = 0.8 + f * 0.6; ambientIntensity = 0.3 + f * 0.2; bgColor = new THREE.Color(0x87CEEB).lerp(new THREE.Color(0xffcc88), f); } else if (t < 0.5) { // noon const f = (t - 0.25) / 0.25; lightColor = sunsetColor.clone().lerp(dayColor, f); intensity = 1.4; ambientIntensity = 0.5; bgColor = new THREE.Color(0xffcc88).lerp(new THREE.Color(0x87CEEB), f); } else if (t < 0.75) { // sunset const f = (t - 0.5) / 0.25; lightColor = dayColor.clone().lerp(sunsetColor, f); intensity = 1.4 - f * 0.8; ambientIntensity = 0.5 - f * 0.2; bgColor = new THREE.Color(0x87CEEB).lerp(new THREE.Color(0xff8844), f); } else { // night const f = (t - 0.75) / 0.25; lightColor = sunsetColor.clone().lerp(nightColor, f); intensity = 0.6 - f * 0.4; ambientIntensity = 0.2 - f * 0.1; bgColor = new THREE.Color(0xff8844).lerp(new THREE.Color(0x0a0a2a), f); } sunLight.color = lightColor; sunLight.intensity = intensity; ambientLight.intensity = ambientIntensity; scene.background = bgColor; // Stars visibility stars.visible = t > 0.75 || t < 0.1; // Window glow intensity const glowIntensity = t > 0.7 || t < 0.15 ? 1.0 : 0.2; houseGroup.children.forEach(child => { if (child.material && child.material.emissive) { child.material.emissiveIntensity = glowIntensity; } }); // Clouds movement cloudGroup.children.forEach(cloud => { cloud.position.x += cloud.userData.speed; if (cloud.position.x > 20) cloud.position.x = -20; }); // Characters wandering characters.forEach(char => { if (char.userData.pause > 0) { char.userData.pause -= 0.016; } else { // move towards target const target = char.userData.target; const dx = target.x - char.position.x; const dz = target.z - char.position.z; const dist = Math.sqrt(dx*dx + dz*dz); if (dist < 0.2) { // pick new target const h = heightMap[Math.floor(char.position.x + size/2)]?.[Math.floor(char.position.z + size/2)]; if (h !== undefined) { let newX, newZ, newH; let attempts = 0; do { newX = char.position.x + (Math.random() - 0.5) * 4; newZ = char.position.z + (Math.random() - 0.5) * 4; newH = heightMap[Math.floor(newX + size/2)]?.[Math.floor(newZ + size/2)]; attempts++; } while ((newH === undefined || newH > 5 || newH < 0.5) && attempts < 10); if (newH !== undefined && newH <= 5) { char.userData.target.set(newX, newH, newZ); } char.userData.pause = 1 + Math.random() * 2; } } else { // move const moveSpeed = char.userData.speed * 0.016; char.position.x += (dx / dist) * moveSpeed; char.position.z += (dz / dist) * moveSpeed; // waddle animation char.userData.waddlePhase += 0.1; char.position.y = char.userData.target.y + Math.sin(char.userData.waddlePhase) * 0.05; } } }); // Camera auto-rotate if (autoRotate) { cameraAngle += autoRotateSpeed; } // Update camera position camera.position.x = Math.sin(cameraAngle) * cameraRadius; camera.position.z = Math.cos(cameraAngle) * cameraRadius; camera.position.y = cameraHeight; camera.lookAt(0, 4, 0); renderer.render(scene, camera); } animate(); // Resize handler window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); // Start with a slight auto-rotate cameraAngle = 0.8; </script> </body> </html>

전체 프롬프트를 보려면 로그인하세요

Continue with:

By logging in, you agree to our Terms of Use and Privacy Policy

사용법

이 프롬프트는 coding와 함께 사용하도록 설계되었습니다. 위의 프롬프트 내용을 복사하여 원하는 AI 도구에 붙여넣으세요.

최상의 결과를 얻으려면 자리 표시자(대괄호 또는 대문자로 표시)를 특정 요구 사항으로 사용자 지정할 수 있습니다.

참고 자료

분류:coding| twitter| threejs| voxel-art

토론