Discusión

Simulador Interactivo de Sistema Solar en 3D

De Wikiprompt, la enciclopedia libre de prompts

Eyisha Zyer
Contribuido porEyisha ZyerXFuente

11 jul 2026

Simulador Interactivo de Sistema Solar en 3D Un prompt integral para construir un simulador 3D pulido del sistema solar con Three.js, que incluya órbitas realistas, controles, interfaz de usuario y mejoras visuales.

Contenido del PromptGuardar

🌐
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Interactive 3D Solar System</title> <style> body { margin: 0; overflow: hidden; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: radial-gradient(ellipse at center, #0a0e14 0%, #05070d 100%); color: white; } #info { position: absolute; top: 20px; left: 20px; background: rgba(20, 25, 35, 0.7); backdrop-filter: blur(10px); padding: 12px 20px; border-radius: 12px; border: 1px solid rgba(255,255,255,0.1); font-size: 14px; letter-spacing: 0.5px; box-shadow: 0 4px 20px rgba(0,0,0,0.5); user-select: none; pointer-events: none; z-index: 10; } #info .label { color: #8ab4f8; margin-right: 6px; } #controls { position: absolute; bottom: 40px; left: 50%; transform: translateX(-50%); display: flex; align-items: center; gap: 20px; background: rgba(8, 15, 25, 0.75); backdrop-filter: blur(15px); padding: 15px 25px; border-radius: 30px; border: 1px solid rgba(255,255,255,0.1); box-shadow: 0 8px 30px rgba(0,0,0,0.6); z-index: 10; } #controls button { background: #2563eb; border: none; color: white; padding: 8px 18px; border-radius: 20px; cursor: pointer; font-weight: 500; transition: all 0.3s; } #controls button:hover { background: #3b82f6; transform: scale(1.05); } #controls button:active { transform: scale(0.95); } #speed-slider { -webkit-appearance: none; width: 180px; height: 4px; background: #334155; border-radius: 5px; outline: none; } #speed-slider::-webkit-slider-thumb { -webkit-appearance: none; width: 18px; height: 18px; border-radius: 50%; background: #2563eb; cursor: pointer; box-shadow: 0 0 10px rgba(37, 99, 235, 0.6); } #speed-display { min-width: 60px; text-align: center; color: #94a3b8; } #reset-btn { background: #475569 !important; } #reset-btn:hover { background: #64748b !important; } #pause-btn.active { background: #eab308 !important; } #hint { position: absolute; bottom: 90px; left: 50%; transform: translateX(-50%); color: #64748b; font-size: 13px; letter-spacing: 0.5px; pointer-events: none; z-index: 10; opacity: 0.6; } /* planet hover */ .planet-hover { cursor: pointer; } @media (max-width: 768px) { #controls { bottom: 20px; flex-wrap: wrap; justify-content: center; width: 90%; padding: 10px 15px; } #speed-slider { width: 120px; } #info { font-size 12px; padding: 8px 14px; } } </style> </head> <body> <div id="info"> <span class="label">Speed:</span> <span id="speed-val">1.00x</span> | <span class="label">Time Scale:</span> <span id="time-scale">1.00</span> <span style="margin-left: 15px; color: #475569;">🔭 Click a planet to focus</span> </div> <div id="controls"> <button id="pause">Pause</button> <span style="color:#94a3b8;">Speed</span> <input type="range" id="speed-slider" min="0" max="5" step="0.1" value="1"> <span id="speed-display">1.0x</span> <button id="reset" class="reset-btn">Reset</button> </div> <div id="hint">Drag to rotate · Scroll to zoom</div> <script type="importmap"> { "imports": { "three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js", "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/" } } </script> <script type="module"> import * as THREE from 'three'; import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; // ---------- SETUP ---------- const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 0.01, 2000); camera.position.set(0, 30, 80); 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.toneMapping = THREE.ACESFilmicToneMapping; renderer.toneMappingExposure = 1.2; document.body.appendChild(renderer.domElement); // Controls const controls = new OrbitControls(camera, renderer.domElement); controls.enableDamping = true; controls.dampingFactor = 0.08; controls.minDistance = 5; controls.maxDistance = 300; controls.target.set(0, 0, 0); // Lighting const ambientLight = new THREE.AmbientLight(0x404060); scene.add(ambientLight); const sunLight = new THREE.PointLight(0xffdd99, 2.5, 150); sunLight.position.set(0, 0, 0); scene.add(sunLight); // Extra dim fill light to see back of planets const fillLight = new THREE.DirectionalLight(0x4466aa, 0.3); fillLight.position.set(50, 50, 50); scene.add(fillLight); // ---------- STARFIELD ---------- // Shader for twinkling points const starShaderMaterial = new THREE.ShaderMaterial({ uniforms: { time: { value: 0 } }, vertexShader: ` attribute float scale; attribute float speed; attribute float phase; varying float vAlpha; void main() { vAlpha = 0.5 + 0.5 * sin(time * speed + phase); vec4 mvPosition = modelViewMatrix * vec4(position, 1.0); gl_PointSize = scale * (200.0 / -mvPosition.z); gl_Position = projectionMatrix * mvPosition; } `, fragmentShader: ` varying float vAlpha; void main() { float d = distance(gl_PointCoord, vec2(0.5, 0.5)); if (d > 0.5) discard; gl_FragColor = vec4(vec3(1.0, 0.95, 0.8), vAlpha * (1.0 - d) * 0.9); } `, transparent: true, blending: THREE.AdditiveBlending, depthWrite: false }); const starCount = 6000; const starGeo = new THREE.BufferGeometry(); const starPos = new Float32Array(starCount * 3); const starScales = new Float32Array(starCount); const starSpeeds = new Float32Array(starCount); const starPhases = new Float32Array(starCount); for (let i = 0; i < starCount; i++) { // Random positions on a large sphere const radius = 150 + Math.random() * 300; const theta = Math.random() * Math.PI * 2; const phi = Math.acos((Math.random() * 2) - 1); starPos[i*3] = radius * Math.sin(phi) * Math.cos(theta); starPos[i*3+1] = radius * Math.sin(phi) * Math.sin(theta); starPos[i*3+2] = radius * Math.cos(phi); starScales[i] = 0.8 + Math.random() * 2.0; starSpeeds[i] = 0.5 + Math.random() * 2.0; starPhases[i] = Math.random() * Math.PI * 2; } starGeo.setAttribute('position', new THREE.BufferAttribute(starPos, 3)); starGeo.setAttribute('scale', new THREE.BufferAttribute(starScales, 1)); starGeo.setAttribute('speed', new THREE.BufferAttribute(starSpeeds, 1)); starGeo.setAttribute('phase', new THREE.BufferAttribute(starPhases, 1)); const stars = new THREE.Points(starGeo, starShaderMaterial); scene.add(stars); // ---------- SUN ---------- const sunGeo = new THREE.SphereGeometry(4.5, 32, 32); const sunMat = new THREE.MeshBasicMaterial({ color: 0xffaa33, emissive: 0xffaa33 }); const sun = new THREE.Mesh(sunGeo, sunMat); scene.add(sun); // Sun glow const glowTexture = new THREE.CanvasTexture(createGlowTexture()); const glowMaterial = new THREE.SpriteMaterial({ map: glowTexture, blending: THREE.AdditiveBlending, transparent: true, depthWrite: false }); const glowSprite = new THREE.Sprite(glowMaterial); glowSprite.scale.set(18, 18, 1); sun.add(glowSprite); function createGlowTexture() { const canvas = document.createElement('canvas'); canvas.width = 256; canvas.height = 256; const ctx = canvas.getContext('2d'); const gradient = ctx.createRadialGradient(128, 128, 0, 128, 128, 128); gradient.addColorStop(0, 'rgba(255, 180, 50, 1)'); gradient.addColorStop(0.3, 'rgba(255, 120, 30, 0.6)'); gradient.addColorStop(1, 'rgba(255, 80, 0, 0)'); ctx.fillStyle = gradient; ctx.fillRect(0, 0, 256, 256); return canvas; } // ---------- PLANET DATA ---------- // approximate distances (units) and sizes const planetInfo = [ { name: 'Mercury', distance: 6, radius: 0.5, color: 0x9a8a7a, period: 0.24 }, { name: 'Venus', distance: 8, radius: 0.9, color: 0xe8b86e, period: 0.615 }, { name: 'Earth', distance: 11, radius: 1.0, color: 0x4a6fa5, period: 1.0 }, { name: 'Mars', distance: 14, radius: 0.6, color: 0xc1440e, period: 1.88 }, { name: 'Jupiter', distance: 20, radius: 2.2, color: 0xc8b0a0, period: 11.86 }, { name: 'Saturn', distance: 26, radius: 1.9, color: 0xe7d9a0, period: 29.45, hasRing: true }, { name: 'Uranus', distance: 34, radius: 1.4, color: 0x76c6e8, period: 84.0 }, { name: 'Neptune', distance: 42, radius: 1.3, color: 0x3f55c9, period: 164.8 } ]; // Create planets and orbit lines const planets = []; const labels = []; const orbitLines = []; // Base angular speed: Earth completes orbit in 60 seconds at 1x speed const baseSpeed = (2 * Math.PI) / 60; planetInfo.forEach((info) => { // Orbital radius in world units const r = info.distance; // Create planet mesh const geom = new THREE.SphereGeometry(info.radius, 32, 32); const mat = new THREE.MeshStandardMaterial({ color: info.color, roughness: 0.7, metalness: 0.2 }); const mesh = new THREE.Mesh(geom, mat); mesh.userData = info; // attach info for later mesh.castShadow = false; mesh.receiveShadow = false; scene.add(mesh); // Add Saturn's rings if (info.hasRing) { const ringGeo = new THREE.RingGeometry(info.radius * 1.4, info.radius * 2.0, 64); const ringMat = new THREE.MeshBasicMaterial({ color: 0xd8cbb0, side: THREE.DoubleSide, transparent: true, opacity: 0.7 }); const ringMesh = new THREE.Mesh(ringGeo, ringMat); ringMesh.rotation.x = Math.PI / 2.2; mesh.add(ringMesh); } // Orbit path (line) const orbitPoints = []; const segments = 128; for (let i = 0; i <= segments; i++) { const angle = (i / segments) * Math.PI * 2; orbitPoints.push(new THREE.Vector3(Math.cos(angle) * r, 0, Math.sin(angle) * r)); } const orbitGeo = new THREE.BufferGeometry().setFromPoints(orbitPoints); const orbitMat = new THREE.LineBasicMaterial({ color: 0x3a4a6a, transparent: true, opacity: 0.35, dashed: true, dashSize: 0.8, gapSize: 0.4 }); const orbitLine = new THREE.Line(orbitGeo, orbitMat); orbitLine.computeLineDistances(); scene.add(orbitLine); orbitLines.push(orbitLine); // Create label sprite const label = createLabelSprite(info.name); // Position label initially, will update per frame mesh.add(label); label.position.set(0, info.radius * 1.2 + 0.8, 0); labels.push(label); // Initialize random angle const angle = Math.random() * Math.PI * 2; planets.push({ mesh, info, angle, orbitalRadius: r, angularSpeed: baseSpeed * (1 / info.period) }); }); // Create a canvas texture for a label function createLabelSprite(text) { const canvas = document.createElement('canvas'); canvas.width = 128; canvas.height = 64; const ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, canvas.width, canvas.height); // Semi-transparent background ctx.fillStyle = 'rgba(0,0,0,0.6)'; ctx.roundRect(20, 10, 88, 44, 12); ctx.fill(); // Border ctx.strokeStyle = 'rgba(100,150,255,0.4)'; ctx.lineWidth = 1; ctx.roundRect(20, 10, 88, 44, 12); ctx.stroke(); // Text ctx.fillStyle = '#ffffff'; ctx.font = 'bold 20px "Segoe UI", Arial'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(text, 64, 32); const texture = new THREE.CanvasTexture(canvas); texture.minFilter = THREE.LinearFilter; const material = new THREE.SpriteMaterial({ map: texture, depthTest: false, transparent: true }); const sprite = new THREE.Sprite(material); sprite.scale.set(2.5, 1.5, 1); sprite.userData.hoverable = false; return sprite; } // Helper: add roundRect method CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) { if (w < 2 * r) r = w / 2; if (h < 2 * r) r = h / 2; this.beginPath(); this.moveTo(x + r, y); this.arcTo(x + w, y, x + w, y + h, r); this.arcTo(x + w, y + h, x, y + h, r); this.arcTo(x, y + h, x, y, r); this.arcTo(x, y, x + w, y, r); this.closePath(); return this; }; // ---------- SIMULATION STATE ---------- let isPaused = false; let speedMultiplier = 1.0; let simulationTime = 0; // UI Elements const pauseBtn = document.getElementById('pause'); const speedSlider = document.getElementById('speed-slider'); const speedDisplay = document.getElementById('speed-display'); const speedValue = document.getElementById('speed-value'); const timeScaleDisplay = document.getElementById('time-scale'); const resetBtn = document.getElementById('reset'); // ---------- RAYCASTING FOR CLICKING ---------- const raycaster = new THREE.Raycaster(); const mouse = new THREE.Vector2(); let focusedPlanet = null; let focusLerp = 0; let targetPos = null; function onMouseClick(event) { mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; raycaster.setFromCamera(mouse, camera); const meshes = planets.map(p => p.mesh); const intersects = raycaster.intersectObjects(meshes, true); if (intersects.length > 0) { // Find top-level planet mesh (maybe from ring/child) let obj = intersects[0].object; while (obj.parent && !obj.userData.name) { obj = obj.parent; } const planet = planets.find(p => p.mesh === obj); if (planet) { focusedPlanet = planet; targetPos = planet.mesh.position.clone(); // Highlight } } } renderer.domElement.addEventListener('click', onMouseClick); // ---------- KEYBOARD ---------- document.addEventListener('keydown', (e) => { if (e.code === 'Space') { e.preventDefault(); togglePause(); } }); function togglePause() { isPaused = !isPaused; pauseBtn.textContent = isPaused ? 'Resume' : 'Pause'; pauseBtn.classList.toggle('active', isPaused); } // ---------- ANIMATION LOOP ---------- let elapsed = 0; function animate() { requestAnimationFrame(animate); const delta = clock.getDelta(); if (!isPaused) { simulationTime += delta * speedMultiplier; elapsed = simulationTime; } // Update star shader time starShaderMaterial.uniforms.time.value = elapsed; // Update planet positions planets.forEach(planet => { const angle = planet.angularSpeed * simulationTime; planet.mesh.position.x = Math.cos(angle) * planet.orbitalRadius; planet.mesh.position.z = Math.sin(angle) * planet.orbitalRadius; }); // Update labels to face camera? Not necessary for sprites, but they auto-face. // Focus on planet if target set if (targetPos) { // Keep camera looking at planet, but object is moving controls.target.lerp(targetPos, 0.05); // Move camera to slight offset const currentCamDir = camera.position.clone().sub(controls.target).normalize(); const desiredDist = 10 + focusedPlanet.info.radius * 2; const newCamPos = controls.target.clone().add(currentCamDir.multiplyScalar(desiredDist)); camera.position.lerp(newCamPos, 0.05); } else { controls.target.set(0, 0, 0); } controls.update(); renderer.render(scene, camera); } const clock = new THREE.Clock(); // ---------- UI EVENT HANDLING ---------- speedSlider.addEventListener('input', (e) => { speedMultiplier = parseFloat(e.target.value); speedDisplay.textContent = speedMultiplier.toFixed(1) + 'x'; speedValue.textContent = speedMultiplier.toFixed(1) + 'x'; }); resetBtn.addEventListener('click', () => { // Reset speed to 1 speedSlider.value = '1'; speedMultiplier = 1.0; speedDisplay.textContent = '1.0x'; speedValue.textContent = '1.0x'; // Reset time simulationTime = 0; // Reset camera camera.position.set(0, 30, 80); controls.target.set(0, 0, 0); focusedPlanet = null; targetPos = null; }); pauseBtn.addEventListener('click', togglePause); // ---------- WINDOW RESIZE ---------- window.addEventListener('resize', onWindowResize); function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } // Start animation animate(); // Set initial UI speedDisplay.textContent = '1.0x'; speedValue.textContent = '1.0x'; </script> </body> </html>

Iniciá sesión para ver el prompt completo

Continuar con:

Al iniciar sesión, aceptás nuestros Términos de uso y Política de privacidad

Uso

Este prompt está diseñado para usarse con coding. Copiá el contenido de arriba y pegalo en tu herramienta de IA preferida.

Para mejores resultados, personalizá los marcadores (indicados con corchetes o mayúsculas) con tus requisitos específicos.

Referencias

Categorías:coding| twitter| three-js| 3d-simulation

Discusión