新建会话
Ctrl
K
Kimi+
历史会话

3D粒子星系与动态光影效果

Create a 3D particle galaxy with swirling nebulas, dynamic lighting
HTML
预览
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>3D Particle Galaxy</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
            background-color: #000;
        }
        canvas {
            display: block;
        }
        .info {
            position: absolute;
            top: 10px;
            left: 10px;
            color: white;
            font-family: Arial, sans-serif;
            font-size: 14px;
            background-color: rgba(0, 0, 0, 0.5);
            padding: 10px;
            border-radius: 5px;
            pointer-events: none;
        }
    </style>
</head>
<body>
    <div class="info">Interactive 3D Galaxy - Drag to rotate, scroll to zoom</div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/controls/OrbitControls.min.js"></script>
    <script>
        // Initialize the scene, camera, and renderer
        const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 2000);
        const renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.setPixelRatio(window.devicePixelRatio);
        document.body.appendChild(renderer.domElement);

        // Set camera position
        camera.position.z = 100;

        // Add orbit controls
        const controls = new THREE.OrbitControls(camera, renderer.domElement);
        controls.enableDamping = true;
        controls.dampingFactor = 0.05;
        controls.rotateSpeed = 0.5;

        // Create galaxy parameters
        const galaxyParams = {
            count: 200000,
            size: 0.01,
            radius: 100,
            branches: 5,
            spin: 1,
            randomness: 0.2,
            randomnessPower: 3,
            insideColor: '#ff6030',
            outsideColor: '#1b3984',
            nebulaCount: 5000,
            nebulaSize: 0.15
        };

        // Create galaxy particles
        let galaxyGeometry = null;
        let galaxyMaterial = null;
        let galaxyPoints = null;

        // Create nebula particles
        let nebulaGeometry = null;
        let nebulaMaterial = null;
        let nebulaPoints = null;

        // Create star particles (background)
        let starsGeometry = null;
        let starsMaterial = null;
        let starsPoints = null;

        // Generate galaxy
        function generateGalaxy() {
            // Dispose of old galaxy if it exists
            if (galaxyPoints !== null) {
                galaxyGeometry.dispose();
                galaxyMaterial.dispose();
                scene.remove(galaxyPoints);
            }

            // Create geometry
            galaxyGeometry = new THREE.BufferGeometry();
            const positions = new Float32Array(galaxyParams.count * 3);
            const colors = new Float32Array(galaxyParams.count * 3);
            const scales = new Float32Array(galaxyParams.count);
            const randomness = new Float32Array(galaxyParams.count * 3);
            const insideColor = new THREE.Color(galaxyParams.insideColor);
            const outsideColor = new THREE.Color(galaxyParams.outsideColor);

            for (let i = 0; i < galaxyParams.count; i++) {
                // Position
                const i3 = i * 3;
                const radius = Math.random() * galaxyParams.radius;
                const spinAngle = radius * galaxyParams.spin;
                const branchAngle = (i % galaxyParams.branches) / galaxyParams.branches * Math.PI * 2;
                
                const randomX = Math.pow(Math.random(), galaxyParams.randomnessPower) * (Math.random() < 0.5 ? 1 : -1) * galaxyParams.randomness * radius;
                const randomY = Math.pow(Math.random(), galaxyParams.randomnessPower) * (Math.random() < 0.5 ? 1 : -1) * galaxyParams.randomness * radius;
                const randomZ = Math.pow(Math.random(), galaxyParams.randomnessPower) * (Math.random() < 0.5 ? 1 : -1) * galaxyParams.randomness * radius;
                
                positions[i3] = Math.cos(branchAngle + spinAngle) * radius + randomX;
                positions[i3 + 1] = randomY;
                positions[i3 + 2] = Math.sin(branchAngle + spinAngle) * radius + randomZ;
                
                randomness[i3] = randomX;
                randomness[i3 + 1] = randomY;
                randomness[i3 + 2] = randomZ;
                
                // Color
                const mixedColor = insideColor.clone();
                mixedColor.lerp(outsideColor, radius / galaxyParams.radius);
                
                colors[i3] = mixedColor.r;
                colors[i3 + 1] = mixedColor.g;
                colors[i3 + 2] = mixedColor.b;
                
                // Scale
                scales[i] = Math.random() * 3;
            }

            galaxyGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
            galaxyGeometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
            galaxyGeometry.setAttribute('aScale', new THREE.BufferAttribute(scales, 1));
            galaxyGeometry.setAttribute('aRandomness', new THREE.BufferAttribute(randomness, 3));

            // Material
            galaxyMaterial = new THREE.PointsMaterial({
                size: galaxyParams.size,
                sizeAttenuation: true,
                depthWrite: false,
                blending: THREE.AdditiveBlending,
                vertexColors: true
            });

            // Points
            galaxyPoints = new THREE.Points(galaxyGeometry, galaxyMaterial);
            scene.add(galaxyPoints);
            
            // Create nebula
            createNebula();
            
            // Create background stars
            createStars();
        }

        // Create nebula cloud
        function createNebula() {
            if (nebulaPoints !== null) {
                nebulaGeometry.dispose();
                nebulaMaterial.dispose();
                scene.remove(nebulaPoints);
            }
            
            nebulaGeometry = new THREE.BufferGeometry();
            const positions = new Float32Array(galaxyParams.nebulaCount * 3);
            const colors = new Float32Array(galaxyParams.nebulaCount * 3);
            const sizes = new Float32Array(galaxyParams.nebulaCount);
            
            const colorPalette = [
                new THREE.Color(0xff0040), // red
                new THREE.Color(0x4000ff), // blue
                new THREE.Color(0xff8000), // orange
                new THREE.Color(0x8000ff), // purple
                new THREE.Color(0x00ff80)  // cyan
            ];
            
            for (let i = 0; i < galaxyParams.nebulaCount; i++) {
                const i3 = i * 3;
                
                // Position in a more cloud-like distribution
                const radius = Math.random() * galaxyParams.radius * 1.2;
                const theta = Math.random() * Math.PI * 2;
                const phi = Math.acos(2 * Math.random() - 1);
                
                positions[i3] = radius * Math.sin(phi) * Math.cos(theta);
                positions[i3 + 1] = (Math.random() - 0.5) * galaxyParams.radius * 0.3;
                positions[i3 + 2] = radius * Math.sin(phi) * Math.sin(theta);
                
                // Color
                const color = colorPalette[Math.floor(Math.random() * colorPalette.length)];
                colors[i3] = color.r;
                colors[i3 + 1] = color.g;
                colors[i3 + 2] = color.b;
                
                // Size
                sizes[i] = galaxyParams.nebulaSize * (Math.random() * 0.5 + 0.5);
            }
            
            nebulaGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
            nebulaGeometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
            nebulaGeometry.setAttribute('size', new THREE.BufferAttribute(sizes, 1));
            
            nebulaMaterial = new THREE.PointsMaterial({
                size: galaxyParams.nebulaSize,
                sizeAttenuation: true,
                depthWrite: false,
                blending: THREE.AdditiveBlending,
                vertexColors: true,
                transparent: true,
                opacity: 0.5
            });
            
            nebulaPoints = new THREE.Points(nebulaGeometry, nebulaMaterial);
            scene.add(nebulaPoints);
        }

        // Create background stars
        function createStars() {
            if (starsPoints !== null) {
                starsGeometry.dispose();
                starsMaterial.dispose();
                scene.remove(starsPoints);
            }
            
            starsGeometry = new THREE.BufferGeometry();
            const count = 5000;
            const positions = new Float32Array(count * 3);
            
            for (let i = 0; i < count * 3; i++) {
                positions[i] = (Math.random() - 0.5) * 2000;
            }
            
            starsGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
            
            starsMaterial = new THREE.PointsMaterial({
                color: 0xffffff,
                size: 0.5,
                sizeAttenuation: true
            });
            
            starsPoints = new THREE.Points(starsGeometry, starsMaterial);
            scene.add(starsPoints);
        }

        // Add central light source
        const centralLight = new THREE.PointLight(0xffffff, 2, 300);
        scene.add(centralLight);

        // Add ambient light for visibility
        const ambientLight = new THREE.AmbientLight(0x333333);
        scene.add(ambientLight);

        // Generate the galaxy
        generateGalaxy();

        // Handle window resize
        window.addEventListener('resize', () => {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        });

        // Animation loop
        const clock = new THREE.Clock();
        
        function animate() {
            requestAnimationFrame(animate);
            
            const elapsedTime = clock.getElapsedTime();
            
            // Rotate galaxy slowly
            if (galaxyPoints) {
                galaxyPoints.rotation.y = elapsedTime * 0.05;
            }
            
            // Rotate nebula
            if (nebulaPoints) {
                nebulaPoints.rotation.y = elapsedTime * 0.03;
                nebulaPoints.rotation.x = Math.sin(elapsedTime * 0.02) * 0.1;
            }
            
            // Update controls
            controls.update();
            
            // Render
            renderer.render(scene, camera);
        }
        
        animate();
    </script>
</body>
</html>