인터랙티브 3D 태양계 시뮬레이터
Wikiprompt, 무료 프롬프트 백과사전에서
인터랙티브 3D 태양계 시뮬레이터 Three.js를 사용하여 세련된 3D 태양계 시뮬레이터를 구축하기 위한 종합적인 프롬프트로, 사실적인 궤도, 컨트롤, UI 및 시각적 향상을 포함합니다.
프롬프트 내용저장
🌐
다음은 3D 태양계 시뮬레이터를 위한 완전한 HTML 파일입니다. Three.js를 사용하여 구축되었으며, 모든 기능이 포함되어 있습니다.
```html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D 태양계 시뮬레이터</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
overflow: hidden;
background: #000;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
#canvas-container {
width: 100vw;
height: 100vh;
}
/* HUD 스타일 - 다크 글래스 UI */
.hud {
position: fixed;
z-index: 100;
background: rgba(10, 15, 30, 0.7);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
color: #fff;
padding: 15px 20px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
}
/* 하단 컨트롤 바 */
#controls {
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
align-items: center;
gap: 20px;
padding: 12px 25px;
}
#controls label {
font-size: 14px;
color: #aaa;
letter-spacing: 1px;
}
#speed-slider {
width: 200px;
accent-color: #4a9eff;
cursor: pointer;
}
#speed-value {
color: #4a9eff;
font-weight: bold;
min-width: 40px;
text-align: center;
}
.btn {
background: rgba(74, 158, 255, 0.2);
border: 1px solid rgba(74, 158, 255, 0.4);
color: #4a9eff;
padding: 8px 20px;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s ease;
letter-spacing: 0.5px;
}
.btn:hover {
background: rgba(74, 158, 255, 0.4);
color: #fff;
}
#pause-btn {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 100;
}
#reset-btn {
position: fixed;
bottom: 20px;
right: 120px;
z-index: 100;
}
#info-box {
top: 20px;
left: 20px;
font-size: 14px;
line-height: 1.8;
min-width: 180px;
}
#info-box .info-title {
color: #4a9eff;
font-weight: bold;
margin-bottom: 5px;
letter-spacing: 1px;
}
#info-box .info-item {
color: #ccc;
font-size: 13px;
}
#info-box .info-value {
color: #fff;
font-weight: 500;
}
/* 행성 라벨 */
.planet-label {
position: fixed;
color: #fff;
font-size: 12px;
pointer-events: none;
text-shadow: 0 0 10px rgba(0, 0, 0, 0.8);
background: rgba(0, 0, 0, 0.5);
padding: 2px 8px;
border-radius: 4px;
border: 1px solid rgba(255, 255, 255, 0.2);
white-space: nowrap;
z-index: 50;
transition: opacity 0.3s;
}
.planet-label .label-icon {
margin-right: 4px;
}
/* 행성 클릭 시 포커스 효과 */
.planet-focused {
outline: 2px solid #4a9eff;
outline-offset: 5px;
border-radius: 50%;
}
/* 반응형 */
@media (max-width: 768px) {
#controls {
flex-wrap: wrap;
width: 90%;
justify-content: center;
gap: 10px;
}
#speed-slider {
width: 120px;
}
#reset-btn {
right: 20px;
bottom: 80px;
}
#pause-btn {
bottom: 80px;
right: 20px;
}
}
</style>
</head>
<body>
<div id="canvas-container"></div>
<!-- HUD 요소들 -->
<div id="info-box" class="hud">
<div class="info-title">🌌 태양계 시뮬레이터</div>
<div class="info-item">속도 배율: <span class="info-value" id="speed-display">1.0x</span></div>
<div class="info-item">상태: <span class="info-value" id="state-display">▶ 재생 중</span></div>
<div class="info-item" style="margin-top: 8px; font-size: 11px; color: #888;">
🖱️ 드래그: 회전 | 스크롤: 줌<br>
⏱️ 스페이스바: 일시정지
</div>
</div>
<div id="controls" class="hud">
<label for="speed-slider">속도</label>
<input type="range" id="speed-slider" min="0" max="5" step="0.1" value="1">
<span id="speed-value">1.0x</span>
</div>
<button id="pause-btn" class="btn">⏸ 일시정지</button>
<button id="reset-btn" class="btn">⟲ 초기화</button>
<!-- Three.js CDN -->
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.160.0/build/three.module.js",
"three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
// --- 초기화 ---
const container = document.getElementById('canvas-container');
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x000011);
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 2000);
camera.position.set(0, 50, 120);
camera.lookAt(0, 0, 0);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.shadowMap.enabled = true;
container.appendChild(renderer.domElement);
// 컨트롤
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.minDistance = 10;
controls.maxDistance = 300;
controls.autoRotate = false;
// --- 조명 ---
const ambientLight = new THREE.AmbientLight(0x404060, 0.5);
scene.add(ambientLight);
const sunLight = new THREE.PointLight(0xffaa44, 2, 200);
sunLight.position.set(0, 0, 0);
scene.add(sunLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(50, 30, 50);
scene.add(directionalLight);
// --- 별 배경 생성 ---
function createStarfield() {
const starsGeometry = new THREE.BufferGeometry();
const starsCount = 5000;
const positions = new Float32Array(starsCount * 3);
const colors = new Float32Array(starsCount * 3);
const sizes = new Float32Array(starsCount);
for (let i = 0; i < starsCount; i++) {
const radius = 300 + Math.random() * 700;
const theta = Math.random() * Math.PI * 2;
const phi = Math.acos(2 * Math.random() - 1);
positions[i * 3] = radius * Math.sin(phi) * Math.cos(theta);
positions[i * 3 + 1] = radius * Math.sin(phi) * Math.sin(theta);
positions[i * 3 + 2] = radius * Math.cos(phi);
// 별 색상 (백색, 청백색, 황백색)
const colorChoice = Math.random();
if (colorChoice < 0.6) {
colors[i * 3] = 1;
colors[i * 3 + 1] = 1;
colors[i * 3 + 2] = 1;
} else if (colorChoice < 0.8) {
colors[i * 3] = 0.7;
colors[i * 3 + 1] = 0.8;
colors[i * 3 + 2] = 1;
} else {
colors[i * 3] = 1;
colors[i * 3 + 1] = 0.9;
colors[i * 3 + 2] = 0.7;
}
sizes[i] = 0.5 + Math.random() * 1.5;
}
starsGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
starsGeometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
starsGeometry.setAttribute('size', new THREE.BufferAttribute(sizes, 1));
const starsMaterial = new THREE.PointsMaterial({
size: 1.5,
vertexColors: true,
transparent: true,
opacity: 0.9,
sizeAttenuation: true
});
const stars = new THREE.Points(starsGeometry, starsMaterial);
scene.add(stars);
// 반짝이는 별 추가 (더 밝은 별)
const brightStarsGeometry = new THREE.BufferGeometry();
const brightCount = 200;
const brightPositions = new Float32Array(brightCount * 3);
for (let i = 0; i < brightCount; i++) {
const radius = 350 + Math.random() * 650;
const theta = Math.random() * Math.PI * 2;
const phi = Math.acos(2 * Math.random() - 1);
brightPositions[i * 3] = radius * Math.sin(phi) * Math.cos(theta);
brightPositions[i * 3 + 1] = radius * Math.sin(phi) * Math.sin(theta);
brightPositions[i * 3 + 2] = radius * Math.cos(phi);
}
brightStarsGeometry.setAttribute('position', new THREE.BufferAttribute(brightPositions, 3));
const brightStarsMaterial = new THREE.PointsMaterial({
size: 2.5,
color: 0xffffff,
transparent: true,
opacity: 0.8,
sizeAttenuation: true
});
const brightStars = new THREE.Points(brightStarsGeometry, brightStarsMaterial);
scene.add(brightStars);
return { stars, brightStars };
}
const starfield = createStarfield();
// --- 태양 생성 ---
function createSun() {
const sunGeometry = new THREE.SphereGeometry(5, 64, 64);
const sunMaterial = new THREE.MeshBasicMaterial({
color: 0xffaa33,
emissive: 0xff6600,
emissiveIntensity: 0.5
});
const sun = new THREE.Mesh(sunGeometry, sunMaterial);
scene.add(sun);
// 태양 글로우 효과
const glowGeometry = new THREE.SphereGeometry(5.5, 32, 32);
const glowMaterial = new THREE.MeshBasicMaterial({
color: 0xff8800,
transparent: true,
opacity: 0.3
});
const glow = new THREE.Mesh(glowGeometry, glowMaterial);
scene.add(glow);
// 외부 글로우
const outerGlowGeometry = new THREE.SphereGeometry(6.5, 32, 32);
const outerGlowMaterial = new THREE.MeshBasicMaterial({
color: 0xff6600,
transparent: true,
opacity: 0.1
});
const outerGlow = new THREE.Mesh(outerGlowGeometry, outerGlowMaterial);
scene.add(outerGlow);
return { sun, glow, outerGlow };
}
const sun = createSun();
// --- 행성 데이터 ---
const planetData = [
{ name: '수성', radius: 0.8, distance: 10, color: 0xaaaaaa, speed: 4.15, icon: '☿' },
{ name: '금성', radius: 1.2, distance: 15, color: 0xffcc66, speed: 1.62, icon: '♀' },
{ name: '지구', radius: 1.3, distance: 20, color: 0x4488ff, speed: 1.0, icon: '🌍' },
{ name: '화성', radius: 1.0, distance: 25, color: 0xff4422, speed: 0.53, icon: '♂' },
{ name: '목성', radius: 3.5, distance: 35, color: 0xffaa66, speed: 0.084, icon: '♃' },
{ name: '토성', radius: 3.0, distance: 45, color: 0xddcc88, speed: 0.034, icon: '♄' },
{ name: '천왕성', radius: 2.0, distance: 55, color: 0x88ccff, speed: 0.012, icon: '⛢' },
{ name: '해왕성', radius: 1.9, distance: 65, color: 0x4466ff, speed: 0.006, icon: '♆' }
];
// --- 행성 생성 ---
const planets = [];
const orbitLines = [];
const planetMeshes = [];
planetData.forEach((data, index) => {
// 행성 메시
const geometry = new THREE.SphereGeometry(data.radius, 32, 32);
const material = new THREE.MeshPhongMaterial({
color: data.color,
shininess: 30,
specular: 0x222222
});
const planet = new THREE.Mesh(geometry, material);
planet.userData = {
...data,
angle: Math.random() * Math.PI * 2,
orbitRadius: data.distance
};
scene.add(planet);
planets.push(planet);
planetMeshes.push(planet);
// 토성의 고리
if (data.name === '토성') {
const ringGeometry = new THREE.RingGeometry(data.radius * 1.4, data.radius * 2.2, 64);
const ringMaterial = new THREE.MeshBasicMaterial({
color: 0xccbb88,
side: THREE.DoubleSide,
transparent: true,
opacity: 0.6
});
const ring = new THREE.Mesh(ringGeometry, ringMaterial);
ring.rotation.x = Math.PI / 2.5;
planet.add(ring);
}
// 궤도 라인 생성
const orbitPoints = [];
const segments = 128;
for (let i = 0; i <= segments; i++) {
const angle = (i / segments) * Math.PI * 2;
const x = data.distance * Math.cos(angle);
const z = data.distance * Math.sin(angle);
orbitPoints.push(new THREE.Vector3(x, 0, z));
}
const orbitGeometry = new THREE.BufferGeometry().setFromPoints(orbitPoints);
const orbitMaterial = new THREE.LineBasicMaterial({
color: 0x4488ff,
transparent: true,
opacity: 0.15
});
const orbitLine = new THREE.Line(orbitGeometry, orbitMaterial);
scene.add(orbitLine);
orbitLines.push(orbitLine);
});
// --- 라벨 생성 ---
const labels = [];
function createLabel(text, icon) {
const label = document.createElement('div');
label.className = 'planet-label';
label.innerHTML = `<span class="label-icon">${icon}</span>${text}`;
document.body.appendChild(label);
return label;
}
planetData.forEach((data) => {
const label = createLabel(data.name, data.icon);
labels.push(label);
});
// --- 상태 변수 ---
let isPaused = false;
let speedMultiplier = 1.0;
let focusedPlanet = null;
let focusTarget = null;
let focusTransition = 0;
// --- UI 요소 참조 ---
const speedSlider = document.getElementById('speed-slider');
const speedValue = document.getElementById('speed-value');
const pauseBtn = document.getElementById('pause-btn');
const resetBtn = document.getElementById('reset-btn');
const speedDisplay = document.getElementById('speed-display');
const stateDisplay = document.getElementById('state-display');
// --- 이벤트 리스너 ---
speedSlider.addEventListener('input', (e) => {
speedMultiplier = parseFloat(e.target.value);
speedValue.textContent = speedMultiplier.toFixed(1) + 'x';
speedDisplay.textContent = speedMultiplier.toFixed(1) + 'x';
});
pauseBtn.addEventListener('click', togglePause);
resetBtn.addEventListener('click', () => {
camera.position.set(0, 50, 120);
camera.lookAt(0, 0, 0);
controls.target.set(0, 0, 0);
controls.update();
focusedPlanet = null;
focusTarget = null;
focusTransition = 0;
});
// 스페이스바 토글
document.addEventListener('keydown', (e) => {
if (e.code === 'Space') {
e.preventDefault();
togglePause();
}
});
function togglePause() {
isPaused = !isPaused;
pauseBtn.textContent = isPaused ? '▶ 재생' : '⏸ 일시정지';
stateDisplay.textContent = isPaused ? '⏸ 일시정지' : '▶ 재생 중';
}
// --- 행성 클릭 감지 (Raycaster) ---
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
renderer.domElement.addEventListener('click', (event) => {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(planetMeshes);
if (intersects.length > 0) {
const clickedPlanet = intersects[0].object;
focusedPlanet = clickedPlanet;
focusTarget = clickedPlanet.position.clone();
focusTransition = 0;
}
});
// --- 창 크기 조정 ---
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// --- 애니메이션 루프 ---
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
const elapsedTime = clock.getElapsedTime();
// 행성 위치 업데이트
if (!isPaused) {
planets.forEach((planet) => {
const data = planet.userData;
data.angle += delta * data.speed * speedMultiplier * 0.1;
const x = data.orbitRadius * Math.cos(data.angle);
const z = data.orbitRadius * Math.sin(data.angle);
planet.position.set(x, 0, z);
});
// 별 반짝임 효과
if (starfield.stars.material.opacity) {
starfield.stars.material.opacity = 0.7 + Math.sin(elapsedTime * 2) * 0.2;
}
}
// 태양 글로우 애니메이션
sun.glow.scale.setScalar(1 + Math.sin(elapsedTime * 2) * 0.05);
sun.outerGlow.scale.setScalar(1 + Math.sin(elapsedTime * 1.5) * 0.08);
// 카메라 포커스 전환
if (focusedPlanet) {
focusTransition += delta * 2;
if (focusTransition >= 1) {
focusTransition = 1;
}
const targetPos = focusedPlanet.position.clone();
targetPos.y += 5;
// 부드러운 카메라 이동
const lerpFactor = 0.05;
camera.position.lerp(targetPos.clone().add(new THREE.Vector3(0, 8, 15)), lerpFactor);
controls.target.lerp(focusedPlanet.position, lerpFactor);
controls.update();
if (focusTransition >= 1) {
// 포커스 완료
}
}
// 라벨 위치 업데이트
planets.forEach((planet, index) => {
const label = labels[index];
const position = planet.position.clone();
position.y += planet.userData.radius + 1.5;
// 화면 좌표로 변환
position.project(camera);
const x = (position.x * 0.5 + 0.5) * window.innerWidth;
const y = (-position.y * 0.5 + 0.5) * window.innerHeight;
// 화면 밖이면 숨김
if (position.z > 1) {
label.style.opacity = '0';
} else {
label.style.opacity = '1';
label.style.left = x + 'px';
label.style.top = y + 'px';
}
});
controls.update();
renderer.render(scene, camera);
}
// --- 시작 ---
animate();
// 초기 행성 위치 랜덤화
planets.forEach((planet) => {
const data = planet.userData;
data.angle = Math.random() * Math.PI * 2;
planet.position.set(
data.orbitRadius * Math.cos(data.angle),
0,
data.orbitRadius * Math.sin(data.angle)
);
});
console.log('🌌 태양계 시뮬레이터가 시작되었습니다!');
console.log('🖱️ 드래그: 회전 | 스크롤: 줌 | 스페이스바: 일시정지');
console.log('🪐 행성을 클릭하면 카메라가 이동합니다.');
</script>
</body>
</html>
```
이 HTML 파일은 다음과 같은 기능을 제공합니다:
## 주요 기능
1. **3D 태양계 시뮬레이션**
- 중앙에 발광하는 태양
- 8개 행성 (수성, 금성, 지구, 화성, 목성, 토성, 천왕성, 해왕성)
- 반투명 궤도 라인
- 수천 개의 반짝이는 별 배경
2. **컨트롤**
- 드래그로 회전, 스크롤로 줌
- 속도 슬라이더 (0x ~ 5x)
- 일시정지/재생 버튼
- 행성 클릭 시 카메라 포커스
- 초기화 버튼
3. **UI/HUD**
- 다크 글래스 스타일의 현대적 UI
- 행성 라벨 (이름 + 아이콘)
- 속도 및 상태 표시
- 스페이스바 단축키
4. **시각 효과**
- 태양 글로우 효과
- 토성의 고리
- 행성별 고유 색상
- 부드러운 카메라 전환
파일을 저장하고 브라우저에서 열면 바로 실행됩니다. Three.js는 CDN에서 로드되므로 인터넷 연결이 필요합니다.
전체 프롬프트를 보려면 로그인하세요
Continue with:
By logging in, you agree to our Terms of Use and Privacy Policy
사용법
이 프롬프트는 coding와 함께 사용하도록 설계되었습니다. 위의 프롬프트 내용을 복사하여 원하는 AI 도구에 붙여넣으세요.
최상의 결과를 얻으려면 자리 표시자(대괄호 또는 대문자로 표시)를 특정 요구 사항으로 사용자 지정할 수 있습니다.
토론
댓글 0개