New Chat
  • Slides
  • Websites
  • Docs
  • Deep Research
  • Sheets
  • Agent Swarm
Kimi Code
Kimi Claw
Chat History
  • Get App
  • About Us
    • Visit Moonshot AI
    • Kimi Platform
    • Features
    • Terms of Service
    • Privacy Policy
  • Language
  • User Feedback

3D粒子星系与旋转星云动态灯光效果

Create a 3D particle galaxy with swirling nebulas, dynamic lighting
HTML
Preview
Copy
<!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 with Swirling Nebulas</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
            background-color: #000;
            font-family: 'Arial', sans-serif;
            color: white;
        }
        canvas {
            display: block;
        }
        .info {
            position: absolute;
            top: 10px;
            left: 10px;
            z-index: 100;
            background: rgba(0,0,0,0.5);
            padding: 10px;
            border-radius: 5px;
            font-size: 12px;
        }
        .controls {
            position: absolute;
            top: 10px;
            right: 10px;
            z-index: 100;
            background: rgba(0,0,0,0.5);
            padding: 10px;
            border-radius: 5px;
            font-size: 12px;
        }
        .controls div {
            margin-bottom: 5px;
        }
        .controls label {
            display: inline-block;
            width: 120px;
        }
    </style>
</head>
<body>
    <div class="info">
        <h3>3D Particle Galaxy</h3>
        <p>Mouse: Rotate view</p>
        <p>Scroll: Zoom in/out</p>
    </div>
    <div class="controls">
        <div>
            <label for="particleCount">Particles: </label>
            <input type="range" id="particleCount" min="5000" max="50000" value="20000" step="1000">
            <span id="particleCountValue">20000</span>
        </div>
        <div>
            <label for="rotationSpeed">Rotation: </label>
            <input type="range" id="rotationSpeed" min="0" max="0.005" value="0.001" step="0.0001">
            <span id="rotationSpeedValue">0.001</span>
        </div>
        <div>
            <label for="nebulaOpacity">Nebula Opacity: </label>
            <input type="range" id="nebulaOpacity" min="0" max="1" value="0.7" step="0.1">
            <span id="nebulaOpacityValue">0.7</span>
        </div>
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/controls/OrbitControls.min.js"></script>
    <script>
        // Main variables
        let scene, camera, renderer, controls;
        let galaxyParticles, nebulaClouds, dustParticles;
        let galaxyGroup = new THREE.Group();
        let clock = new THREE.Clock();
        let rotationSpeed = 0.001;
        let nebulaOpacity = 0.7;
        
        // Galaxy parameters
        const galaxyParams = {
            count: 20000,
            size: 0.02,
            radius: 5,
            branches: 5,
            spin: 1,
            randomness: 0.2,
            randomnessPower: 3,
            insideColor: '#ff6030',
            outsideColor: '#1b3984',
            nebulaCount: 20,
            nebulaSize: 0.5,
            dustCount: 10000,
            dustSize: 0.01,
            dustRadius: 8
        };
        
        // Initialize the scene
        init();
        animate();
        
        // Setup GUI controls
        setupGUI();
        
        function init() {
            // Create scene
            scene = new THREE.Scene();
            
            // Create camera
            camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100);
            camera.position.set(0, 3, 10);
            
            // Create renderer
            renderer = new THREE.WebGLRenderer({ antialias: true });
            renderer.setSize(window.innerWidth, window.innerHeight);
            renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
            document.body.appendChild(renderer.domElement);
            
            // Setup orbit controls
            controls = new THREE.OrbitControls(camera, renderer.domElement);
            controls.enableDamping = true;
            
            // Create galaxy
            generateGalaxy();
            
            // Add galaxy group to scene
            scene.add(galaxyGroup);
            
            // Add ambient light
            const ambientLight = new THREE.AmbientLight(0x404040);
            scene.add(ambientLight);
            
            // Add directional light
            const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
            directionalLight.position.set(0, 5, 5);
            scene.add(directionalLight);
            
            // Add point lights for dynamic lighting
            const pointLight1 = new THREE.PointLight(0x1e90ff, 2, 20);
            pointLight1.position.set(2, 3, 2);
            scene.add(pointLight1);
            
            const pointLight2 = new THREE.PointLight(0xff4500, 2, 20);
            pointLight2.position.set(-2, -3, -2);
            scene.add(pointLight2);
            
            // Handle window resize
            window.addEventListener('resize', () => {
                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();
                renderer.setSize(window.innerWidth, window.innerHeight);
            });
            
            // Update particle count display
            document.getElementById('particleCountValue').textContent = galaxyParams.count;
            document.getElementById('rotationSpeedValue').textContent = rotationSpeed;
            document.getElementById('nebulaOpacityValue').textContent = nebulaOpacity;
            
            // Add event listeners for controls
            document.getElementById('particleCount').addEventListener('input', (e) => {
                galaxyParams.count = parseInt(e.target.value);
                document.getElementById('particleCountValue').textContent = galaxyParams.count;
                regenerateGalaxy();
            });
            
            document.getElementById('rotationSpeed').addEventListener('input', (e) => {
                rotationSpeed = parseFloat(e.target.value);
                document.getElementById('rotationSpeedValue').textContent = rotationSpeed;
            });
            
            document.getElementById('nebulaOpacity').addEventListener('input', (e) => {
                nebulaOpacity = parseFloat(e.target.value);
                document.getElementById('nebulaOpacityValue').textContent = nebulaOpacity;
                if (nebulaClouds) {
                    nebulaClouds.material.opacity = nebulaOpacity;
                }
            });
        }

        function generateGalaxy() {
            // Clear previous galaxy objects
            galaxyGroup.clear();
            
            // Generate galaxy particles
            galaxyParticles = createGalaxyParticles();
            galaxyGroup.add(galaxyParticles);
            
            // Generate nebula clouds
            nebulaClouds = createNebulaClouds();
            galaxyGroup.add(nebulaClouds);
            
            // Generate dust particles
            dustParticles = createDustParticles();
            galaxyGroup.add(dustParticles);
        }
        
        function createGalaxyParticles() {
            const geometry = new THREE.BufferGeometry();
            const positions = new Float32Array(galaxyParams.count * 3);
            const colors = new Float32Array(galaxyParams.count * 3);
            const scales = new Float32Array(galaxyParams.count);
            
            const insideColor = new THREE.Color(galaxyParams.insideColor);
            const outsideColor = new THREE.Color(galaxyParams.outsideColor);
            
            for (let i = 0; i < galaxyParams.count; i++) {
                const i3 = i * 3;
                
                // Position
                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;
                
                // 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() * 2;
            }
            
            geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
            geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
            geometry.setAttribute('aScale', new THREE.BufferAttribute(scales, 1));
            
            // Material
            const material = new THREE.PointsMaterial({
                size: galaxyParams.size,
                sizeAttenuation: true,
                depthWrite: false,
                blending: THREE.AdditiveBlending,
                vertexColors: true
            });
            
            // Points
            const particles = new THREE.Points(geometry, material);
            return particles;
        }
        
        function createNebulaClouds() {
            const nebulaGroup = new THREE.Group();
            
            for (let i = 0; i < galaxyParams.nebulaCount; i++) {
                const radius = Math.random() * (galaxyParams.radius + 2) + 1;
                const theta = Math.random() * Math.PI * 2;
                const phi = Math.acos(2 * Math.random() - 1);
                
                const x = radius * Math.sin(phi) * Math.cos(theta);
                const y = radius * Math.sin(phi) * Math.sin(theta);
                const z = radius * Math.cos(phi);
                
                const color = new THREE.Color();
                color.setHSL(Math.random(), 0.7, 0.5);
                
                const nebulaGeometry = new THREE.SphereGeometry(
                    Math.random() * galaxyParams.nebulaSize + 0.5,
                    16,
                    16
                );
                
                const nebulaMaterial = new THREE.MeshBasicMaterial({
                    color: color,
                    transparent: true,
                    opacity: nebulaOpacity,
                    blending: THREE.AdditiveBlending
                });
                
                const nebula = new THREE.Mesh(nebulaGeometry, nebulaMaterial);
                nebula.position.set(x, y, z);
                
                nebulaGroup.add(nebula);
            }
            
            return nebulaGroup;
        }
        
        function createDustParticles() {
            const geometry = new THREE.BufferGeometry();
            const positions = new Float32Array(galaxyParams.dustCount * 3);
            const colors = new Float32Array(galaxyParams.dustCount * 3);
            
            for (let i = 0; i < galaxyParams.dustCount; i++) {
                const i3 = i * 3;
                
                // Position
                const radius = Math.random() * galaxyParams.dustRadius;
                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] = radius * Math.sin(phi) * Math.sin(theta) * 0.2; // Flatten the dust
                positions[i3 + 2] = radius * Math.cos(phi);
                
                // Color
                const brightness = Math.random() * 0.5 + 0.5;
                colors[i3] = brightness * 0.8;
                colors[i3 + 1] = brightness * 0.9;
                colors[i3 + 2] = brightness;
            }
            
            geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
            geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
            
            // Material
            const material = new THREE.PointsMaterial({
                size: galaxyParams.dustSize,
                sizeAttenuation: true,
                depthWrite: false,
                blending: THREE.AdditiveBlending,
                vertexColors: true,
                transparent: true,
                opacity: 0.3
            });
            
            // Points
            const dust = new THREE.Points(geometry, material);
            return dust;
        }
        
        function regenerateGalaxy() {
            generateGalaxy();
        }
        
        function setupGUI() {
            const gui = new dat.GUI({ width: 300 });
            
            gui.add(galaxyParams, 'count', 5000, 50000, 1000).name('Stars').onChange(() => {
                regenerateGalaxy();
            });
            gui.add(galaxyParams, 'size', 0.001, 0.1, 0.001).name('Star Size').onChange(() => {
                galaxyParticles.material.size = galaxyParams.size;
            });
            gui.add(galaxyParams, 'radius', 2, 10, 0.1).name('Galaxy Radius').onChange(() => {
                regenerateGalaxy();
            });
            gui.add(galaxyParams, 'branches', 2, 10, 1).name('Branches').onChange(() => {
                regenerateGalaxy();
            });
            gui.add(galaxyParams, 'spin', -2, 2, 0.1).name('Spin').onChange(() => {
                regenerateGalaxy();
            });
            gui.add(galaxyParams, 'randomness', 0, 1, 0.01).name('Randomness').onChange(() => {
                regenerateGalaxy();
            });
            gui.add(galaxyParams, 'randomnessPower', 1, 10, 0.1).name('Random Power').onChange(() => {
                regenerateGalaxy();
            });
            gui.addColor(galaxyParams, 'insideColor').name('Inside Color').onChange(() => {
                regenerateGalaxy();
            });
            gui.addColor(galaxyParams, 'outsideColor').name('Outside Color').onChange(() => {
                regenerateGalaxy();
            });
            gui.add(galaxyParams, 'nebulaCount', 0, 50, 1).name('Nebula Count').onChange(() => {
                regenerateGalaxy();
            });
            gui.add(galaxyParams, 'nebulaSize', 0.1, 2, 0.1).name('Nebula Size').onChange(() => {
                regenerateGalaxy();
            });
            gui.add(galaxyParams, 'dustCount', 0, 20000, 1000).name('Dust Particles').onChange(() => {
                regenerateGalaxy();
            });
        }
        
        function animate() {
            requestAnimationFrame(animate);
            
            const elapsedTime = clock.getElapsedTime();
            
            // Rotate galaxy
            galaxyGroup.rotation.y = elapsedTime * rotationSpeed;
            
            // Animate nebula clouds
            if (nebulaClouds) {
                nebulaClouds.children.forEach((nebula, index) => {
                    nebula.rotation.x = elapsedTime * 0.05 * (1 + index * 0.01);
                    nebula.rotation.y = elapsedTime * 0.03 * (1 + index * 0.01);
                    
                    // Subtle pulsing effect
                    const scale = 1 + Math.sin(elapsedTime * 0.5 + index) * 0.1;
                    nebula.scale.set(scale, scale, scale);
                });
            }
            
            // Animate dust particles
            if (dustParticles) {
                dustParticles.rotation.y = elapsedTime * 0.0005;
            }
            
            controls.update();
            renderer.render(scene, camera);
        }
    </script>
</body>
</html>