|
|
<!DOCTYPE html> |
|
|
<html lang="ko"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>λ²κ½ ν©λ 리λ μ λλ©μ΄μ
</title> |
|
|
<script src="https://cdn.tailwindcss.com"></script> |
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> |
|
|
<style> |
|
|
body { |
|
|
margin: 0; |
|
|
overflow: hidden; |
|
|
background: linear-gradient(to bottom, #87CEEB, #E0F7FA); |
|
|
} |
|
|
canvas { |
|
|
display: block; |
|
|
} |
|
|
.info { |
|
|
position: absolute; |
|
|
bottom: 20px; |
|
|
width: 100%; |
|
|
text-align: center; |
|
|
color: white; |
|
|
font-family: 'Arial', sans-serif; |
|
|
text-shadow: 1px 1px 2px rgba(0,0,0,0.5); |
|
|
pointer-events: none; |
|
|
} |
|
|
.controls { |
|
|
position: absolute; |
|
|
top: 20px; |
|
|
right: 20px; |
|
|
z-index: 100; |
|
|
} |
|
|
.control-btn { |
|
|
background: rgba(255,255,255,0.7); |
|
|
border: none; |
|
|
border-radius: 50%; |
|
|
width: 40px; |
|
|
height: 40px; |
|
|
margin: 5px; |
|
|
cursor: pointer; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
box-shadow: 0 2px 5px rgba(0,0,0,0.2); |
|
|
transition: all 0.3s ease; |
|
|
} |
|
|
.control-btn:hover { |
|
|
background: rgba(255,255,255,0.9); |
|
|
transform: scale(1.1); |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body> |
|
|
<div class="info"> |
|
|
<h1 class="text-4xl font-bold mb-2">μλ¦λ€μ΄ λ²κ½λ무</h1> |
|
|
<p class="text-xl">λ΄μ κ²½μ΄λ‘μμ λ껴보μΈμ</p> |
|
|
</div> |
|
|
|
|
|
<div class="controls"> |
|
|
<button class="control-btn" id="zoomIn">+</button> |
|
|
<button class="control-btn" id="zoomOut">-</button> |
|
|
<button class="control-btn" id="resetView">β»</button> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
|
|
|
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); |
|
|
renderer.setPixelRatio(window.devicePixelRatio); |
|
|
document.body.appendChild(renderer.domElement); |
|
|
|
|
|
|
|
|
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6); |
|
|
scene.add(ambientLight); |
|
|
|
|
|
const directionalLight = new THREE.DirectionalLight(0xffffff, 1.2); |
|
|
directionalLight.position.set(5, 10, 7); |
|
|
scene.add(directionalLight); |
|
|
|
|
|
|
|
|
const hemisphereLight = new THREE.HemisphereLight(0xffffbb, 0x080820, 0.5); |
|
|
scene.add(hemisphereLight); |
|
|
|
|
|
|
|
|
const trunkGeometry = new THREE.CylinderGeometry(1.5, 2.5, 15, 12); |
|
|
const trunkMaterial = new THREE.MeshPhongMaterial({ |
|
|
color: 0x8B4513, |
|
|
flatShading: true, |
|
|
shininess: 30 |
|
|
}); |
|
|
const trunk = new THREE.Mesh(trunkGeometry, trunkMaterial); |
|
|
trunk.position.y = 7.5; |
|
|
trunk.castShadow = true; |
|
|
scene.add(trunk); |
|
|
|
|
|
|
|
|
const branchMaterial = new THREE.MeshPhongMaterial({ |
|
|
color: 0x8B4513, |
|
|
flatShading: true |
|
|
}); |
|
|
|
|
|
function createBranch(length, angle, position, parent = null) { |
|
|
const branchGeometry = new THREE.CylinderGeometry( |
|
|
length * 0.05, |
|
|
length * 0.1, |
|
|
length, |
|
|
8 |
|
|
); |
|
|
const branch = new THREE.Mesh(branchGeometry, branchMaterial); |
|
|
|
|
|
if (parent) { |
|
|
parent.add(branch); |
|
|
branch.position.copy(position); |
|
|
} else { |
|
|
scene.add(branch); |
|
|
branch.position.copy(position); |
|
|
} |
|
|
|
|
|
branch.rotation.z = angle; |
|
|
|
|
|
|
|
|
branch.rotation.x = Math.random() * 0.3 - 0.15; |
|
|
branch.rotation.y = Math.random() * 0.3 - 0.15; |
|
|
|
|
|
branch.castShadow = true; |
|
|
return branch; |
|
|
} |
|
|
|
|
|
|
|
|
const mainBranches = []; |
|
|
const branchCount = 8; |
|
|
|
|
|
for (let i = 0; i < branchCount; i++) { |
|
|
const angle = (i / branchCount) * Math.PI * 2; |
|
|
const length = 8 + Math.random() * 4; |
|
|
const height = 10 + Math.random() * 5; |
|
|
|
|
|
const x = Math.cos(angle) * 1.5; |
|
|
const z = Math.sin(angle) * 1.5; |
|
|
|
|
|
const branch = createBranch(length, angle * 0.8, new THREE.Vector3(x, height, z)); |
|
|
mainBranches.push(branch); |
|
|
|
|
|
|
|
|
const secondaryCount = 3 + Math.floor(Math.random() * 3); |
|
|
for (let j = 0; j < secondaryCount; j++) { |
|
|
const secLength = length * (0.4 + Math.random() * 0.2); |
|
|
const secAngle = angle + (Math.random() * 0.6 - 0.3); |
|
|
const secHeight = height - length * (0.2 + j * 0.2); |
|
|
|
|
|
const secBranch = createBranch( |
|
|
secLength, |
|
|
secAngle, |
|
|
new THREE.Vector3(0, -length * (0.2 + j * 0.2), 0), |
|
|
branch |
|
|
); |
|
|
|
|
|
|
|
|
if (Math.random() > 0.5) { |
|
|
const terCount = 2 + Math.floor(Math.random() * 2); |
|
|
for (let k = 0; k < terCount; k++) { |
|
|
const terLength = secLength * (0.4 + Math.random() * 0.2); |
|
|
const terAngle = secAngle + (Math.random() * 0.4 - 0.2); |
|
|
|
|
|
createBranch( |
|
|
terLength, |
|
|
terAngle, |
|
|
new THREE.Vector3(0, -secLength * (0.3 + k * 0.2), 0), |
|
|
secBranch |
|
|
); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const petals = []; |
|
|
const petalCount = 1500; |
|
|
const petalColors = [ |
|
|
0xFFB6C1, 0xFF69B4, 0xFF1493, 0xFFC0CB, 0xFFE4E1, |
|
|
0xFFA07A, 0xFF82AB, 0xFF6EB4, 0xFF83A8, 0xFF8FBC |
|
|
]; |
|
|
|
|
|
|
|
|
const petalShape = new THREE.Shape(); |
|
|
petalShape.moveTo(0, 0); |
|
|
petalShape.bezierCurveTo(0.5, 0.1, 0.8, 0.3, 0.8, 0.6); |
|
|
petalShape.bezierCurveTo(0.8, 0.9, 0.5, 1.0, 0.2, 0.8); |
|
|
petalShape.bezierCurveTo(-0.1, 0.6, -0.1, 0.3, 0, 0); |
|
|
|
|
|
const petalGeometry = new THREE.ShapeGeometry(petalShape); |
|
|
petalGeometry.scale(0.08, 0.08, 0.08); |
|
|
|
|
|
|
|
|
for (let i = 0; i < petalCount; i++) { |
|
|
const petalMaterial = new THREE.MeshBasicMaterial({ |
|
|
color: petalColors[Math.floor(Math.random() * petalColors.length)], |
|
|
transparent: true, |
|
|
opacity: 0.7 + Math.random() * 0.2, |
|
|
side: THREE.DoubleSide |
|
|
}); |
|
|
|
|
|
const petal = new THREE.Mesh(petalGeometry, petalMaterial); |
|
|
|
|
|
|
|
|
petal.position.x = (Math.random() - 0.5) * 30; |
|
|
petal.position.y = 5 + Math.random() * 25; |
|
|
petal.position.z = (Math.random() - 0.5) * 30; |
|
|
|
|
|
|
|
|
petal.rotation.x = Math.random() * Math.PI; |
|
|
petal.rotation.y = Math.random() * Math.PI; |
|
|
petal.rotation.z = Math.random() * Math.PI; |
|
|
|
|
|
const scale = 0.8 + Math.random() * 0.4; |
|
|
petal.scale.set(scale, scale, scale); |
|
|
|
|
|
|
|
|
petal.userData = { |
|
|
velocity: new THREE.Vector3( |
|
|
(Math.random() - 0.5) * 0.02, |
|
|
-0.03 - Math.random() * 0.05, |
|
|
(Math.random() - 0.5) * 0.02 |
|
|
), |
|
|
rotationSpeed: new THREE.Vector3( |
|
|
Math.random() * 0.02, |
|
|
Math.random() * 0.02, |
|
|
Math.random() * 0.02 |
|
|
), |
|
|
wobble: Math.random() * 0.02, |
|
|
wobbleSpeed: 0.01 + Math.random() * 0.02 |
|
|
}; |
|
|
|
|
|
scene.add(petal); |
|
|
petals.push(petal); |
|
|
} |
|
|
|
|
|
|
|
|
const groundGeometry = new THREE.CircleGeometry(50, 64); |
|
|
const groundMaterial = new THREE.MeshPhongMaterial({ |
|
|
color: 0x7CFC00, |
|
|
side: THREE.DoubleSide, |
|
|
shininess: 10 |
|
|
}); |
|
|
const ground = new THREE.Mesh(groundGeometry, groundMaterial); |
|
|
ground.rotation.x = -Math.PI / 2; |
|
|
ground.position.y = 0; |
|
|
ground.receiveShadow = true; |
|
|
scene.add(ground); |
|
|
|
|
|
|
|
|
const grassGeometry = new THREE.CircleGeometry(50, 64); |
|
|
const grassMaterial = new THREE.MeshPhongMaterial({ |
|
|
color: 0x5A8F29, |
|
|
side: THREE.DoubleSide, |
|
|
transparent: true, |
|
|
opacity: 0.3 |
|
|
}); |
|
|
const grass = new THREE.Mesh(grassGeometry, grassMaterial); |
|
|
grass.rotation.x = -Math.PI / 2; |
|
|
grass.position.y = 0.01; |
|
|
scene.add(grass); |
|
|
|
|
|
|
|
|
for (let i = 0; i < 300; i++) { |
|
|
const petal = new THREE.Mesh(petalGeometry, new THREE.MeshBasicMaterial({ |
|
|
color: petalColors[Math.floor(Math.random() * petalColors.length)], |
|
|
transparent: true, |
|
|
opacity: 0.6 + Math.random() * 0.3, |
|
|
side: THREE.DoubleSide |
|
|
})); |
|
|
|
|
|
petal.position.x = (Math.random() - 0.5) * 45; |
|
|
petal.position.y = 0.01; |
|
|
petal.position.z = (Math.random() - 0.5) * 45; |
|
|
|
|
|
petal.rotation.x = Math.random() * Math.PI; |
|
|
petal.rotation.y = Math.random() * Math.PI; |
|
|
petal.rotation.z = Math.random() * Math.PI; |
|
|
|
|
|
const scale = 0.06 + Math.random() * 0.04; |
|
|
petal.scale.set(scale, scale, scale); |
|
|
|
|
|
scene.add(petal); |
|
|
} |
|
|
|
|
|
|
|
|
for (let i = 0; i < 10; i++) { |
|
|
const angle = (i / 10) * Math.PI * 2; |
|
|
const distance = 35 + Math.random() * 10; |
|
|
|
|
|
const tree = new THREE.Mesh( |
|
|
new THREE.ConeGeometry(3, 8, 5), |
|
|
new THREE.MeshPhongMaterial({ |
|
|
color: 0x2E8B57, |
|
|
flatShading: true |
|
|
}) |
|
|
); |
|
|
|
|
|
tree.position.x = Math.cos(angle) * distance; |
|
|
tree.position.z = Math.sin(angle) * distance; |
|
|
tree.position.y = 4; |
|
|
|
|
|
scene.add(tree); |
|
|
} |
|
|
|
|
|
|
|
|
camera.position.set(0, 15, 25); |
|
|
camera.lookAt(0, 10, 0); |
|
|
|
|
|
|
|
|
window.addEventListener('resize', () => { |
|
|
camera.aspect = window.innerWidth / window.innerHeight; |
|
|
camera.updateProjectionMatrix(); |
|
|
renderer.setSize(window.innerWidth, window.innerHeight); |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('zoomIn').addEventListener('click', () => { |
|
|
camera.position.z *= 0.9; |
|
|
}); |
|
|
|
|
|
document.getElementById('zoomOut').addEventListener('click', () => { |
|
|
camera.position.z *= 1.1; |
|
|
}); |
|
|
|
|
|
document.getElementById('resetView').addEventListener('click', () => { |
|
|
camera.position.set(0, 15, 25); |
|
|
camera.lookAt(0, 10, 0); |
|
|
}); |
|
|
|
|
|
|
|
|
let time = 0; |
|
|
function animate() { |
|
|
requestAnimationFrame(animate); |
|
|
time += 0.01; |
|
|
|
|
|
|
|
|
petals.forEach(petal => { |
|
|
|
|
|
petal.position.x += petal.userData.velocity.x + Math.sin(time * petal.userData.wobbleSpeed) * petal.userData.wobble; |
|
|
petal.position.y += petal.userData.velocity.y; |
|
|
petal.position.z += petal.userData.velocity.z + Math.cos(time * petal.userData.wobbleSpeed) * petal.userData.wobble; |
|
|
|
|
|
|
|
|
petal.rotation.x += petal.userData.rotationSpeed.x; |
|
|
petal.rotation.y += petal.userData.rotationSpeed.y; |
|
|
petal.rotation.z += petal.userData.rotationSpeed.z; |
|
|
|
|
|
|
|
|
if (petal.position.y < -5) { |
|
|
petal.position.x = (Math.random() - 0.5) * 20; |
|
|
petal.position.y = 25 + Math.random() * 10; |
|
|
petal.position.z = (Math.random() - 0.5) * 20; |
|
|
|
|
|
|
|
|
petal.userData.velocity.set( |
|
|
(Math.random() - 0.5) * 0.02, |
|
|
-0.03 - Math.random() * 0.05, |
|
|
(Math.random() - 0.5) * 0.02 |
|
|
); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
camera.position.x = Math.sin(time * 0.2) * 5; |
|
|
camera.position.z = 25 + Math.cos(time * 0.2) * 5; |
|
|
camera.lookAt(0, 10, 0); |
|
|
|
|
|
renderer.render(scene, camera); |
|
|
} |
|
|
|
|
|
animate(); |
|
|
</script> |
|
|
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 𧬠<a href="https://enzostvs-deepsite.hf.space?remix=joung/bloom" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
|
|
</html> |