1、效果-支持鼠标互动

在这里插入图片描述

2、在线体验

3D太阳系

3、源码

把下面内容保存到 txt,另存为 .html 格式,再使用浏览器打开即可

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <title>3D 太阳系 - Three.js</title>
    <style>
        body { margin: 0; overflow: hidden; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; }
        #info {
            position: absolute;
            top: 20px;
            left: 20px;
            color: rgba(255,255,255,0.8);
            background: rgba(0,0,0,0.5);
            padding: 8px 16px;
            border-radius: 20px;
            pointer-events: none;
            font-size: 14px;
            backdrop-filter: blur(4px);
            border: 1px solid rgba(255,255,255,0.1);
            z-index: 100;
        }
        #controls-hint {
            position: absolute;
            bottom: 20px;
            left: 50%;
            transform: translateX(-50%);
            color: rgba(255,255,255,0.4);
            background: rgba(0,0,0,0.3);
            padding: 6px 14px;
            border-radius: 20px;
            font-size: 12px;
            backdrop-filter: blur(2px);
            border: 1px solid rgba(255,255,255,0.05);
            pointer-events: none;
            z-index: 100;
        }
    </style>
</head>
<body>
    <div id="info">🌞 太阳系 3D · 鼠标拖动/滚轮</div>
    <div id="controls-hint">行星公转 · 自转 · 光照</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;
        document.body.appendChild(renderer.domElement);

        // CSS2渲染器用于文字标签
        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.target.set(0, 0, 0);
        controls.maxPolarAngle = Math.PI / 2;
        controls.minDistance = 10;
        controls.maxDistance = 150;

        // --- 光照 ---
        // 环境光提供基础照明
        const ambientLight = new THREE.AmbientLight(0x404060);
        scene.add(ambientLight);

        // 太阳本身作为点光源
        const sunLight = new THREE.PointLight(0xffeedd, 2, 0, 0);
        sunLight.position.set(0, 0, 0);
        scene.add(sunLight);
        // 辅助照亮行星的正面
        const fillLight = new THREE.DirectionalLight(0xffffff, 0.5);
        fillLight.position.set(1, 1, 1);
        scene.add(fillLight);
        // 背面补光
        const backLight = new THREE.DirectionalLight(0x446688, 0.3);
        backLight.position.set(-1, -0.5, -1);
        scene.add(backLight);

        // --- 星空背景 (粒子系统) ---
        const starsGeometry = new THREE.BufferGeometry();
        const starsCount = 4000;
        const starsPositions = new Float32Array(starsCount * 3);
        for (let i = 0; i < starsCount * 3; i += 3) {
            // 分布在半径 200-500 的球壳内
            const radius = 200 + Math.random() * 300;
            const theta = Math.random() * Math.PI * 2;
            const phi = Math.acos((Math.random() * 2) - 1);
            starsPositions[i] = radius * Math.sin(phi) * Math.cos(theta);
            starsPositions[i+1] = radius * Math.sin(phi) * Math.sin(theta);
            starsPositions[i+2] = radius * Math.cos(phi);
        }
        starsGeometry.setAttribute('position', new THREE.BufferAttribute(starsPositions, 3));
        const starsMaterial = new THREE.PointsMaterial({
            color: 0xffffff,
            size: 0.35,
            transparent: true,
            opacity: 0.9,
            blending: THREE.AdditiveBlending
        });
        const stars = new THREE.Points(starsGeometry, starsMaterial);
        scene.add(stars);

        // 额外小星星(更远更暗)
        const starsGeometry2 = new THREE.BufferGeometry();
        const starsCount2 = 2000;
        const starsPositions2 = new Float32Array(starsCount2 * 3);
        for (let i = 0; i < starsCount2 * 3; i += 3) {
            const radius = 500 + Math.random() * 400;
            const theta = Math.random() * Math.PI * 2;
            const phi = Math.acos((Math.random() * 2) - 1);
            starsPositions2[i] = radius * Math.sin(phi) * Math.cos(theta);
            starsPositions2[i+1] = radius * Math.sin(phi) * Math.sin(theta);
            starsPositions2[i+2] = radius * Math.cos(phi);
        }
        starsGeometry2.setAttribute('position', new THREE.BufferAttribute(starsPositions2, 3));
        const starsMaterial2 = new THREE.PointsMaterial({
            color: 0x88aaff,
            size: 0.2,
            transparent: true,
            opacity: 0.5,
            blending: THREE.AdditiveBlending
        });
        const stars2 = new THREE.Points(starsGeometry2, starsMaterial2);
        scene.add(stars2);

        // --- 辅助对象:轨道线 ---
        function createOrbit(radius, color = 0x446688) {
            const points = [];
            const segments = 64;
            for (let i = 0; i <= segments; i++) {
                const angle = (i / segments) * Math.PI * 2;
                points.push(new THREE.Vector3(radius * Math.cos(angle), 0, radius * Math.sin(angle)));
            }
            const geometry = new THREE.BufferGeometry().setFromPoints(points);
            const material = new THREE.LineBasicMaterial({ color: color, transparent: true, opacity: 0.25 });
            const orbitLine = new THREE.Line(geometry, material);
            return orbitLine;
        }

        // 行星数据:名称, 颜色, 大小(半径比例), 轨道半径, 公转速度, 自转速度
        // 为了视觉效果,大小和距离做了非等比调整
        const planetsData = [
            { name: '水星', color: 0xaaaaaa, size: 0.4, distance: 5, speed: 0.02, rotationSpeed: 0.004 },
            { name: '金星', color: 0xffcc88, size: 0.6, distance: 7, speed: 0.015, rotationSpeed: 0.002 },
            { name: '地球', color: 0x4488ff, size: 0.7, distance: 9, speed: 0.012, rotationSpeed: 0.006 },
            { name: '火星', color: 0xff6633, size: 0.5, distance: 11, speed: 0.01, rotationSpeed: 0.005 },
            { name: '木星', color: 0xddbb99, size: 1.4, distance: 15, speed: 0.007, rotationSpeed: 0.009 },
            { name: '土星', color: 0xeeddbb, size: 1.2, distance: 19, speed: 0.005, rotationSpeed: 0.007, hasRing: true },
            { name: '天王星', color: 0x88ccff, size: 0.9, distance: 23, speed: 0.004, rotationSpeed: 0.003 },
            { name: '海王星', color: 0x3366aa, size: 0.9, distance: 27, speed: 0.003, rotationSpeed: 0.003 }
        ];

        // 存储行星对象以便动画更新
        const planets = [];

        // --- 创建太阳 (发光) ---
        const sunGeometry = new THREE.SphereGeometry(2.5, 32, 32);
        const sunMaterial = new THREE.MeshStandardMaterial({
            color: 0xffaa44,
            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; i++) {
            const radius = 2.8 + Math.random() * 1.2;
            const theta = Math.random() * Math.PI * 2;
            const phi = Math.acos((Math.random() * 2) - 1);
            glowPos[i*3] = radius * Math.sin(phi) * Math.cos(theta);
            glowPos[i*3+1] = radius * Math.sin(phi) * Math.sin(theta);
            glowPos[i*3+2] = radius * Math.cos(phi);
        }
        glowParticlesGeo.setAttribute('position', new THREE.BufferAttribute(glowPos, 3));
        const glowParticlesMat = new THREE.PointsMaterial({
            color: 0xffaa33,
            size: 0.08,
            transparent: true,
            blending: THREE.AdditiveBlending,
            depthWrite: false
        });
        const glowParticles = new THREE.Points(glowParticlesGeo, glowParticlesMat);
        scene.add(glowParticles);

        // 太阳标签
        const sunLabelDiv = document.createElement('div');
        sunLabelDiv.textContent = '☀️ 太阳';
        sunLabelDiv.style.color = '#ffaa44';
        sunLabelDiv.style.fontSize = '18px';
        sunLabelDiv.style.fontWeight = 'bold';
        sunLabelDiv.style.textShadow = '0 0 20px rgba(255,100,0,0.8)';
        const sunLabel = new CSS2DObject(sunLabelDiv);
        sunLabel.position.set(0, 3.5, 0);
        scene.add(sunLabel);

        // --- 创建行星及其轨道 ---
        planetsData.forEach((data, index) => {
            // 轨道
            const orbitColor = 0x88aacc;
            const orbit = createOrbit(data.distance, orbitColor);
            scene.add(orbit);

            // 行星本体
            const geometry = new THREE.SphereGeometry(data.size, 32, 32);
            const material = new THREE.MeshStandardMaterial({
                color: data.color,
                roughness: 0.5,
                metalness: 0.1,
                emissive: new THREE.Color(data.color).multiplyScalar(0.1)
            });
            const planet = new THREE.Mesh(geometry, material);
            planet.castShadow = false;
            planet.receiveShadow = false;

            // 随机初始角度
            const initAngle = Math.random() * Math.PI * 2;
            planet.position.x = data.distance * Math.cos(initAngle);
            planet.position.z = data.distance * Math.sin(initAngle);

            scene.add(planet);

            // 如果是土星,添加光环
            if (data.hasRing) {
                const ringGeometry = new THREE.RingGeometry(data.size * 1.4, data.size * 2.2, 64);
                const ringMaterial = new THREE.MeshStandardMaterial({
                    color: 0xccbb99,
                    side: THREE.DoubleSide,
                    transparent: true,
                    opacity: 0.6,
                    roughness: 0.7
                });
                const ring = new THREE.Mesh(ringGeometry, ringMaterial);
                ring.rotation.x = Math.PI / 2.2; // 倾斜
                ring.rotation.z = 0.3;
                planet.add(ring); // 添加到行星,随行星移动
            }

            // 行星标签
            const labelDiv = document.createElement('div');
            labelDiv.textContent = data.name;
            labelDiv.style.color = '#ddd';
            labelDiv.style.fontSize = '14px';
            labelDiv.style.fontWeight = '300';
            labelDiv.style.textShadow = '0 0 10px rgba(0,0,0,0.8)';
            labelDiv.style.background = 'rgba(0,0,0,0.4)';
            labelDiv.style.padding = '2px 8px';
            labelDiv.style.borderRadius = '12px';
            labelDiv.style.border = '1px solid rgba(255,255,255,0.1)';
            labelDiv.style.backdropFilter = 'blur(2px)';
            const label = new CSS2DObject(labelDiv);
            label.position.set(0, data.size + 0.6, 0);
            planet.add(label);

            // 保存行星数据用于动画
            planets.push({
                mesh: planet,
                distance: data.distance,
                speed: data.speed,
                rotationSpeed: data.rotationSpeed,
                angle: initAngle
            });
        });

        // 添加一些随机的小行星带(装饰)
        const asteroidBeltGeometry = new THREE.BufferGeometry();
        const asteroidCount = 2000;
        const asteroidPositions = new Float32Array(asteroidCount * 3);
        for (let i = 0; i < asteroidCount * 3; i += 3) {
            const radius = 13 + Math.random() * 1.5;
            const angle = Math.random() * Math.PI * 2;
            const yOffset = (Math.random() - 0.5) * 0.8;
            asteroidPositions[i] = radius * Math.cos(angle);
            asteroidPositions[i+1] = yOffset;
            asteroidPositions[i+2] = radius * Math.sin(angle);
        }
        asteroidBeltGeometry.setAttribute('position', new THREE.BufferAttribute(asteroidPositions, 3));
        const asteroidMaterial = new THREE.PointsMaterial({
            color: 0xccaa88,
            size: 0.08,
            transparent: true,
            opacity: 0.6
        });
        const asteroidBelt = new THREE.Points(asteroidBeltGeometry, asteroidMaterial);
        scene.add(asteroidBelt);

        // 再添加一些更远的柯伊伯带装饰
        const outerBeltGeometry = new THREE.BufferGeometry();
        const outerCount = 1500;
        const outerPositions = new Float32Array(outerCount * 3);
        for (let i = 0; i < outerCount * 3; i += 3) {
            const radius = 30 + Math.random() * 4;
            const angle = Math.random() * Math.PI * 2;
            const yOffset = (Math.random() - 0.5) * 1.5;
            outerPositions[i] = radius * Math.cos(angle);
            outerPositions[i+1] = yOffset;
            outerPositions[i+2] = radius * Math.sin(angle);
        }
        outerBeltGeometry.setAttribute('position', new THREE.BufferAttribute(outerPositions, 3));
        const outerMaterial = new THREE.PointsMaterial({
            color: 0x88aadd,
            size: 0.1,
            transparent: true,
            opacity: 0.3
        });
        const outerBelt = new THREE.Points(outerBeltGeometry, outerMaterial);
        scene.add(outerBelt);

        // --- 动画循环 ---
        let clock = new THREE.Clock();

        function animate() {
            const delta = clock.getDelta();
            const elapsedTime = performance.now() / 1000; // seconds

            // 更新行星位置 (公转)
            planets.forEach(p => {
                p.angle += p.speed * delta * 8; // 乘系数调整速度
                p.mesh.position.x = p.distance * Math.cos(p.angle);
                p.mesh.position.z = p.distance * Math.sin(p.angle);
                // 自转
                p.mesh.rotation.y += p.rotationSpeed * delta * 20;
            });

            // 太阳自转
            sun.rotation.y += 0.002 * delta * 20;
            glowParticles.rotation.y += 0.0005 * delta * 20;

            // 小行星带缓慢旋转
            asteroidBelt.rotation.y += 0.001 * delta * 20;
            outerBelt.rotation.y -= 0.0005 * delta * 20;

            // 星星缓慢旋转
            stars.rotation.y += 0.0001 * delta * 20;
            stars2.rotation.x += 0.00005 * delta * 20;

            // 更新控制器
            controls.update();

            // 渲染
            renderer.render(scene, camera);
            labelRenderer.render(scene, camera);

            requestAnimationFrame(animate);
        }

        animate();

        // --- 窗口自适应 ---
        window.addEventListener('resize', onWindowResize, false);
        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
            labelRenderer.setSize(window.innerWidth, window.innerHeight);
        }

        // 提示用户操作
        console.log('3D 太阳系已启动!');
    </script>
</body>
</html>
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐