交互式3D太阳系模拟器
来自 Wikiprompt,自由的提示词百科全书
交互式3D太阳系模拟器 使用Three.js构建精致3D太阳系模拟器的全面提示,包括逼真轨道、控制、UI和视觉增强。
提示词内容收藏
🌐
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D 太阳系模拟器</title>
<style>
body {
margin: 0;
overflow: hidden;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: black;
color: white;
}
#info {
position: absolute;
top: 20px;
left: 20px;
background: rgba(20, 20, 30, 0.7);
backdrop-filter: blur(8px);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 12px;
padding: 12px 20px;
color: #ddd;
font-size: 14px;
pointer-events: none;
z-index: 100;
box-shadow: 0 4px 20px rgba(0,0,0,0.5);
letter-spacing: 0.5px;
}
#info span {
color: #ffaa00;
font-weight: bold;
}
#controls {
position: absolute;
bottom: 30px;
left: 50%;
transform: translateX(-50%);
background: rgba(20, 20, 30, 0.7);
backdrop-filter: blur(8px);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 40px;
padding: 12px 30px;
display: flex;
gap: 30px;
align-items: center;
z-index: 200;
box-shadow: 0 4px 20px rgba(0,0,0,0.6);
color: #eee;
font-size: 16px;
font-weight: 500;
}
#controls label {
display: flex;
align-items: center;
gap: 10px;
}
#controls input[type="range"] {
width: 180px;
cursor: pointer;
background: transparent;
accent-color: #ffaa00;
}
#controls button {
background: rgba(255, 170, 0, 0.2);
border: 1px solid #ffaa00;
color: #ffaa00;
padding: 8px 20px;
border-radius: 30px;
font-weight: bold;
cursor: pointer;
transition: all 0.2s;
letter-spacing: 0.5px;
font-size: 14px;
}
#controls button:hover {
background: #ffaa00;
color: black;
}
#reset-btn {
background: rgba(100, 200, 255, 0.2);
border-color: #64c8ff;
color: #64c8ff;
}
#reset-btn:hover {
background: #64c8ff;
color: black;
}
#status {
position: absolute;
bottom: 100px;
right: 30px;
background: rgba(20, 20, 30, 0.6);
backdrop-filter: blur(4px);
border: 1px solid rgba(255,255,255,0.1);
border-radius: 8px;
padding: 8px 16px;
font-size: 14px;
color: #aaa;
z-index: 150;
pointer-events: none;
}
#planet-label {
position: absolute;
top: 80px;
left: 20px;
background: rgba(0,0,0,0.5);
padding: 6px 12px;
border-radius: 20px;
font-size: 14px;
color: #ffaa00;
border-left: 3px solid #ffaa00;
pointer-events: none;
opacity: 0;
transition: opacity 0.3s;
}
.label-3d {
color: #fff;
text-shadow: 0 0 10px rgba(0,0,0,0.8);
font-size: 12px;
font-weight: bold;
background: rgba(0,0,0,0.4);
padding: 2px 8px;
border-radius: 12px;
border: 1px solid rgba(255,255,255,0.2);
backdrop-filter: blur(2px);
white-space: nowrap;
pointer-events: none;
}
@media (max-width: 700px) {
#controls { flex-wrap: wrap; width: 90%; padding: 15px; gap: 15px; justify-content: center; }
#controls input[type="range"] { width: 120px; }
}
</style>
</head>
<body>
<div id="info">🌌 <span>太阳系模拟器</span> · 拖动旋转 · 滚轮缩放 · 点击行星聚焦</div>
<div id="planet-label"></div>
<div id="status">速度: <span id="speed-display">1.0x</span> · 状态: <span id="pause-status">运行中</span></div>
<div id="controls">
<label>⚡ 速度 <input type="range" id="speed-slider" min="0" max="5" step="0.1" value="1"></label>
<button id="pause-btn">⏸️ 暂停</button>
<button id="reset-btn">↺ 重置视角</button>
</div>
<!-- 引入 Three.js 核心库和扩展 -->
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.128.0/build/three.module.js",
"three/addons/": "https://unpkg.com/three@0.128.0/examples/jsm/"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';
// --- 初始化场景、相机、渲染器 ---
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x050510);
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 30, 60);
camera.lookAt(0, 0, 0);
// WebGL渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = false; // 为了性能
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);
// CSS2D渲染器 (用于标签)
const labelRenderer = new CSS2DRenderer();
labelRenderer.setSize(window.innerWidth, window.innerHeight);
labelRenderer.domElement.style.position = 'absolute';
labelRenderer.domElement.style.top = '0px';
labelRenderer.domElement.style.left = '0px';
labelRenderer.domElement.style.pointerEvents = 'none'; // 让标签不阻挡点击
document.body.appendChild(labelRenderer.domElement);
// --- 控制器 ---
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.autoRotate = false;
controls.enableZoom = true;
controls.rotateSpeed = 0.8;
controls.zoomSpeed = 1.2;
controls.minDistance = 5;
controls.maxDistance = 200;
controls.target.set(0, 0, 0);
// --- 光照 ---
// 环境光提供基础照明
const ambientLight = new THREE.AmbientLight(0x404060);
scene.add(ambientLight);
// 点光源模拟太阳光
const sunLight = new THREE.PointLight(0xffdd99, 2, 0, 0);
sunLight.position.set(0, 0, 0);
scene.add(sunLight);
// 辅助光让行星背面可见
const backLight = new THREE.DirectionalLight(0x446688, 0.3);
backLight.position.set(-10, 10, -20);
scene.add(backLight);
// 添加一点暖色补光
const fillLight = new THREE.DirectionalLight(0xffaa88, 0.2);
fillLight.position.set(10, 5, 10);
scene.add(fillLight);
// --- 星空背景 (粒子系统) ---
const starsGeometry = new THREE.BufferGeometry();
const starsCount = 6000;
const starsPositions = new Float32Array(starsCount * 3);
for (let i = 0; i < starsCount * 3; i += 3) {
// 随机分布在球壳上
const r = 150 + Math.random() * 100;
const theta = Math.random() * Math.PI * 2;
const phi = Math.acos((Math.random() * 2) - 1);
starsPositions[i] = r * Math.sin(phi) * Math.cos(theta);
starsPositions[i+1] = r * Math.sin(phi) * Math.sin(theta);
starsPositions[i+2] = r * Math.cos(phi);
}
starsGeometry.setAttribute('position', new THREE.BufferAttribute(starsPositions, 3));
const starsMaterial = new THREE.PointsMaterial({ color: 0xffffff, size: 0.25, transparent: true, opacity: 0.8 });
const stars = new THREE.Points(starsGeometry, starsMaterial);
scene.add(stars);
// 额外的小星星闪烁效果 (用第二层粒子)
const twinkleStarsGeo = new THREE.BufferGeometry();
const twinkleCount = 2000;
const twinklePos = new Float32Array(twinkleCount * 3);
for (let i = 0; i < twinkleCount * 3; i += 3) {
const r = 200 + Math.random() * 150;
const theta = Math.random() * Math.PI * 2;
const phi = Math.acos((Math.random() * 2) - 1);
twinklePos[i] = r * Math.sin(phi) * Math.cos(theta);
twinklePos[i+1] = r * Math.sin(phi) * Math.sin(theta);
twinklePos[i+2] = r * Math.cos(phi);
}
twinkleStarsGeo.setAttribute('position', new THREE.BufferAttribute(twinklePos, 3));
const twinkleMaterial = new THREE.PointsMaterial({ color: 0x88aaff, size: 0.15, transparent: true, opacity: 0.6 });
const twinkleStars = new THREE.Points(twinkleStarsGeo, twinkleMaterial);
scene.add(twinkleStars);
// --- 太阳 ---
const sunGeometry = new THREE.SphereGeometry(3.5, 64, 64);
const sunMaterial = new THREE.MeshStandardMaterial({
color: 0xffaa33,
emissive: 0xff5500,
emissiveIntensity: 1.2,
roughness: 0.4,
});
const sun = new THREE.Mesh(sunGeometry, sunMaterial);
scene.add(sun);
// 太阳光晕 (使用点光源 + 发光粒子)
const glowParticlesGeo = new THREE.BufferGeometry();
const glowCount = 800;
const glowPos = new Float32Array(glowCount * 3);
for (let i = 0; i < glowCount * 3; i += 3) {
const r = 4.0 + Math.random() * 1.5;
const theta = Math.random() * Math.PI * 2;
const phi = Math.acos((Math.random() * 2) - 1);
glowPos[i] = r * Math.sin(phi) * Math.cos(theta);
glowPos[i+1] = r * Math.sin(phi) * Math.sin(theta);
glowPos[i+2] = r * Math.cos(phi);
}
glowParticlesGeo.setAttribute('position', new THREE.BufferAttribute(glowPos, 3));
const glowMaterial = new THREE.PointsMaterial({ color: 0xffaa44, size: 0.08, transparent: true, blending: THREE.AdditiveBlending });
const glow = new THREE.Points(glowParticlesGeo, glowMaterial);
scene.add(glow);
// --- 行星数据 ---
// 为了视觉效果,大小和距离不按真实比例,但保持相对关系
const planetsData = [
{ name: '水星', radius: 0.4, distance: 6.0, color: 0xaaaaaa, speed: 0.02, emissive: 0x222222 },
{ name: '金星', radius: 0.6, distance: 9.0, color: 0xffcc88, speed: 0.015, emissive: 0x442200 },
{ name: '地球', radius: 0.65, distance: 12.0, color: 0x4488ff, speed: 0.012, emissive: 0x002244 },
{ name: '火星', radius: 0.55, distance: 15.0, color: 0xff6633, speed: 0.01, emissive: 0x331100 },
{ name: '木星', radius: 1.4, distance: 19.0, color: 0xddbb99, speed: 0.008, emissive: 0x332211 },
{ name: '土星', radius: 1.2, distance: 24.0, color: 0xeeddbb, speed: 0.006, emissive: 0x332211, hasRing: true },
{ name: '天王星', radius: 0.9, distance: 29.0, color: 0x88ccff, speed: 0.005, emissive: 0x113344 },
{ name: '海王星', radius: 0.9, distance: 34.0, color: 0x3366ff, speed: 0.004, emissive: 0x001133 }
];
// 存储行星对象以便动画
const planets = [];
const orbitLines = [];
// 创建行星和轨道
planetsData.forEach((data, index) => {
// 创建行星球体
const geometry = new THREE.SphereGeometry(data.radius, 32, 32);
const material = new THREE.MeshStandardMaterial({
color: data.color,
emissive: data.emissive,
emissiveIntensity: 0.1,
roughness: 0.5,
metalness: 0.1
});
const planet = new THREE.Mesh(geometry, material);
// 初始位置
const angle = Math.random() * Math.PI * 2;
planet.position.x = data.distance * Math.cos(angle);
planet.position.z = data.distance * Math.sin(angle);
// 存储自定义属性
planet.userData = {
distance: data.distance,
speed: data.speed,
angle: angle,
name: data.name
};
scene.add(planet);
planets.push(planet);
// 如果是土星,添加光环
if (data.hasRing) {
const ringGeo = new THREE.TorusGeometry(data.radius * 1.8, 0.2, 16, 100);
const ringMat = new THREE.MeshStandardMaterial({
color: 0xccbb99,
emissive: 0x332211,
side: THREE.DoubleSide,
transparent: true,
opacity: 0.6
});
const ring = new THREE.Mesh(ringGeo, ringMat);
ring.rotation.x = Math.PI / 2;
ring.rotation.z = 0.3;
planet.add(ring);
}
// 创建轨道线 (半透明圆环)
const orbitPoints = [];
const segments = 128;
for (let i = 0; i <= segments; i++) {
const theta = (i / segments) * Math.PI * 2;
const x = data.distance * Math.cos(theta);
const z = data.distance * Math.sin(theta);
orbitPoints.push(new THREE.Vector3(x, 0, z));
}
const orbitGeometry = new THREE.BufferGeometry().setFromPoints(orbitPoints);
const orbitMaterial = new THREE.LineBasicMaterial({ color: 0x88aaff, opacity: 0.2, transparent: true });
const orbitLine = new THREE.Line(orbitGeometry, orbitMaterial);
scene.add(orbitLine);
orbitLines.push(orbitLine);
// 创建CSS2D标签
const labelDiv = document.createElement('div');
labelDiv.className = 'label-3d';
labelDiv.textContent = data.name;
const label = new CSS2DObject(labelDiv);
label.position.set(0, data.radius + 0.8, 0);
planet.add(label);
});
// 添加小行星带 (装饰)
const asteroidGeo = new THREE.BufferGeometry();
const astCount = 800;
const astPos = new Float32Array(astCount * 3);
for (let i = 0; i < astCount * 3; i += 3) {
const r = 17 + Math.random() * 2.5;
const theta = Math.random() * Math.PI * 2;
const y = (Math.random() - 0.5) * 0.8;
astPos[i] = r * Math.cos(theta);
astPos[i+1] = y;
astPos[i+2] = r * Math.sin(theta);
}
asteroidGeo.setAttribute('position', new THREE.BufferAttribute(astPos, 3));
const astMat = new THREE.PointsMaterial({ color: 0xaa9977, size: 0.08 });
const asteroids = new THREE.Points(asteroidGeo, astMat);
scene.add(asteroids);
// --- UI 元素绑定 ---
const speedSlider = document.getElementById('speed-slider');
const speedDisplay = document.getElementById('speed-display');
const pauseBtn = document.getElementById('pause-btn');
const resetBtn = document.getElementById('reset-btn');
const pauseStatus = document.getElementById('pause-status');
const planetLabel = document.getElementById('planet-label');
let isPaused = false;
let speedMultiplier = 1.0;
speedSlider.addEventListener('input', (e) => {
speedMultiplier = parseFloat(e.target.value);
speedDisplay.textContent = speedMultiplier.toFixed(1) + 'x';
});
pauseBtn.addEventListener('click', () => {
isPaused = !isPaused;
pauseBtn.textContent = isPaused ? '▶️ 继续' : '⏸️ 暂停';
pauseStatus.textContent = isPaused ? '已暂停' : '运行中';
});
resetBtn.addEventListener('click', () => {
camera.position.set(0, 30, 60);
controls.target.set(0, 0, 0);
controls.update();
});
// 点击行星聚焦 (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(planets);
if (intersects.length > 0) {
const targetPlanet = intersects[0].object;
// 平滑移动相机到行星附近 (简单实现)
const planetPos = targetPlanet.position.clone();
const offset = new THREE.Vector3(2, 2, 2).normalize().multiplyScalar(targetPlanet.userData.distance * 0.3);
camera.position.copy(planetPos.clone().add(offset));
controls.target.copy(planetPos);
controls.update();
// 显示标签
planetLabel.textContent = '🔭 聚焦: ' + targetPlanet.userData.name;
planetLabel.style.opacity = 1;
setTimeout(() => { planetLabel.style.opacity = 0; }, 2000);
}
});
// 键盘空格暂停
window.addEventListener('keydown', (e) => {
if (e.code === 'Space') {
e.preventDefault();
pauseBtn.click();
}
});
// 窗口大小自适应
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
labelRenderer.setSize(window.innerWidth, window.innerHeight);
});
// --- 动画循环 ---
let clock = new THREE.Clock();
function animate() {
const delta = clock.getDelta();
const elapsedTime = performance.now() * 0.001; // seconds
if (!isPaused) {
// 更新行星位置
planets.forEach(planet => {
const data = planet.userData;
data.angle += data.speed * speedMultiplier * delta * 2; // 乘2让速度更明显
planet.position.x = data.distance * Math.cos(data.angle);
planet.position.z = data.distance * Math.sin(data.angle);
});
// 小行星带旋转
asteroids.rotation.y += 0.001 * speedMultiplier;
}
// 星星缓慢旋转
stars.rotation.y += 0.0001;
twinkleStars.rotation.y -= 0.00005;
// 太阳自转
sun.rotation.y += 0.002;
glow.rotation.y += 0.001;
// 更新控制器
controls.update();
// 渲染
renderer.render(scene, camera);
labelRenderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
// 初始状态显示
speedDisplay.textContent = '1.0x';
pauseStatus.textContent = '运行中';
</script>
</body>
</html>
用法
此提示词专为 coding 设计。复制上方内容并粘贴到你常用的 AI 工具中。
为获得最佳效果,可将占位符(方括号或大写字母标示)替换为你的具体需求。
讨论
0 条评论