返回博主主页

抽象圣诞树3

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>3D Christmas Tree</title>
    <script src="https://cdn.jsdelivr.net/npm/three@0.140.1/build/three.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/three@0.140.1/examples/js/controls/OrbitControls.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/three@0.140.1/examples/js/loaders/GLTFLoader.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/gsap@3.12.2/dist/gsap.min.js"></script>
    <style>
        body {
            margin: 0;
            overflow: hidden;
            background-color: #1a1a2e;
        }
        #info {
            position: absolute;
            top: 10px;
            left: 10px;
            color: white;
            font-family: Arial, sans-serif;
            z-index: 100;
        }
    </style>
</head>
<body>
    <div id="info">
        <p>Click and drag to rotate the scene</p>
        <p>Scroll to zoom in/out</p>
    </div>
    <script>
        // Scene setup
        const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        const renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.setClearColor(0x1a1a2e);
        document.body.appendChild(renderer.domElement);

        // Controls
        const controls = new THREE.OrbitControls(camera, renderer.domElement);
        controls.enableDamping = true;
        controls.dampingFactor = 0.05;
        camera.position.set(0, 5, 15);

        // Enhanced snow ground with texture and depth
        const groundGeometry = new THREE.PlaneGeometry(100, 100, 50, 50);
        const groundMaterial = new THREE.MeshStandardMaterial({ 
            color: 0xffffff,
            roughness: 0.95,
            metalness: 0.05,
            bumpScale: 0.1
        });
        const ground = new THREE.Mesh(groundGeometry, groundMaterial);
        ground.rotation.x = -Math.PI / 2;
        ground.position.y = -0.5;
        
        // Add subtle snow dunes and irregularities
        const vertices = groundGeometry.attributes.position.array;
        for (let i = 0; i < vertices.length; i += 3) {
            const x = vertices[i];
            const z = vertices[i + 2];
            // Create gentle snow drifts
            const noise = Math.sin(x * 0.1) * Math.sin(z * 0.1) * 0.1;
            vertices[i + 1] = noise;
        }
        groundGeometry.attributes.position.needsUpdate = true;
        
        // Add shadow properties
        ground.receiveShadow = true;
        scene.add(ground);
        
        // Add snow piles and irregularities
        for (let i = 0; i < 50; i++) {
            const pileGeometry = new THREE.SphereGeometry(0.3 + Math.random() * 0.5, 16, 16);
            const pileMaterial = new THREE.MeshStandardMaterial({ 
                color: 0xffffff,
                roughness: 0.9,
                metalness: 0.1
            });
            const pile = new THREE.Mesh(pileGeometry, pileMaterial);
            pile.position.x = (Math.random() - 0.5) * 40;
            pile.position.z = (Math.random() - 0.5) * 40;
            pile.position.y = -0.3 + Math.random() * 0.2;
            pile.scale.set(1 + Math.random(), 0.5 + Math.random() * 0.5, 1 + Math.random());
            pile.receiveShadow = true;
            scene.add(pile);
        }

        // Christmas tree (Pixar style with improved realism)
        function createChristmasTree() {
            const treeGroup = new THREE.Group();

            // Tree trunk with bark texture effect
            const trunkGeometry = new THREE.CylinderGeometry(0.5, 0.7, 2, 32);
            const trunkMaterial = new THREE.MeshStandardMaterial({ 
                color: 0x8B4513,
                roughness: 0.95,
                metalness: 0.05,
                bumpScale: 0.1
            });
            const trunk = new THREE.Mesh(trunkGeometry, trunkMaterial);
            trunk.position.y = 1;
            
            // Add subtle bark details
            trunk.scale.set(1, 1, 1);
            trunk.userData = { isTrunk: true };
            treeGroup.add(trunk);

            // Improved tree leaves with more natural shape and color variation
            const leafColors = [0x006400, 0x228B22, 0x008000, 0x32CD32, 0x00FF00];
            for (let i = 0; i < 8; i++) {
                const radius = 2.5 - i * 0.28;
                const height = 1.8 - i * 0.2;
                const segments = 48;
                
                // Create a more organic cone shape
                const leafGeometry = new THREE.ConeGeometry(radius, height, segments);
                const leafMaterial = new THREE.MeshStandardMaterial({ 
                    color: leafColors[i % leafColors.length],
                    roughness: 0.85,
                    metalness: 0.15,
                    shininess: 40,
                    bumpScale: 0.05
                });
                const leaf = new THREE.Mesh(leafGeometry, leafMaterial);
                
                // Stagger the positions for more natural look
                leaf.position.y = 2.8 + i * 0.7;
                
                // Add slight curve and asymmetry for organic appearance
                const curveFactor = Math.sin(i * 0.5) * 0.1;
                leaf.scale.set(1 + curveFactor, 1.1, 1 - curveFactor);
                
                // Slight rotation for each layer
                leaf.rotation.y = i * 0.1;
                
                leaf.userData = { isLeaf: true };
                treeGroup.add(leaf);
            }

            // Tree topper (star)
            const starGeometry = new THREE.IcosahedronGeometry(0.5, 0);
            const starMaterial = new THREE.MeshStandardMaterial({ 
                color: 0xFFD700,
                emissive: 0xFFD700,
                emissiveIntensity: 0.3,
                roughness: 0.2,
                metalness: 0.8
            });
            const star = new THREE.Mesh(starGeometry, starMaterial);
            star.position.y = 7.5;
            star.scale.set(1.2, 1.5, 1.2);
            treeGroup.add(star);

            // Decorations
            const decorationColors = [0xFF0000, 0xFFA500, 0xFFC0CB, 0x800080, 0x00BFFF];
            for (let i = 0; i < 30; i++) {
                const decorationGeometry = new THREE.SphereGeometry(0.2, 16, 16);
                const decorationMaterial = new THREE.MeshStandardMaterial({ 
                    color: decorationColors[i % decorationColors.length],
                    emissive: decorationColors[i % decorationColors.length],
                    emissiveIntensity: 0.2,
                    roughness: 0.1,
                    metalness: 0.9
                });
                const decoration = new THREE.Mesh(decorationGeometry, decorationMaterial);
                
                // Random position on tree
                const angle = Math.random() * Math.PI * 2;
                const radius = Math.random() * 2 + 0.5;
                const height = Math.random() * 5 + 2;
                
                decoration.position.x = Math.cos(angle) * radius;
                decoration.position.z = Math.sin(angle) * radius;
                decoration.position.y = height;
                
                // Check if decoration is inside the tree
                const maxRadiusAtHeight = 2.5 - (height - 2) * 0.4;
                if (Math.sqrt(decoration.position.x ** 2 + decoration.position.z ** 2) < maxRadiusAtHeight) {
                    treeGroup.add(decoration);
                }
            }

            return treeGroup;
        }

        const christmasTree = createChristmasTree();
        scene.add(christmasTree);

        // Improved Santa Claus model with better proportions and details
        function createSanta() {
            const santaGroup = new THREE.Group();
            
            // Body with better proportions
            const bodyGeometry = new THREE.CylinderGeometry(0.7, 0.9, 1.5, 32);
            const bodyMaterial = new THREE.MeshStandardMaterial({ 
                color: 0xC41E3A, // Deeper red
                roughness: 0.7,
                metalness: 0.3,
                bumpScale: 0.05
            });
            const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
            body.position.y = 1.2;
            santaGroup.add(body);
            
            // Arms
            const armGeometry = new THREE.CylinderGeometry(0.2, 0.15, 1.2, 16);
            const armMaterial = new THREE.MeshStandardMaterial({ 
                color: 0xC41E3A,
                roughness: 0.7,
                metalness: 0.3
            });
            
            const leftArm = new THREE.Mesh(armGeometry, armMaterial);
            leftArm.position.set(-0.9, 1.3, 0);
            leftArm.rotation.z = Math.PI * 0.3;
            santaGroup.add(leftArm);
            
            const rightArm = new THREE.Mesh(armGeometry, armMaterial);
            rightArm.position.set(0.9, 1.3, 0);
            rightArm.rotation.z = -Math.PI * 0.3;
            santaGroup.add(rightArm);
            
            // Hands
            const handGeometry = new THREE.SphereGeometry(0.15, 16, 16);
            const handMaterial = new THREE.MeshStandardMaterial({ 
                color: 0xFFDAB9,
                roughness: 0.9,
                metalness: 0.1
            });
            
            const leftHand = new THREE.Mesh(handGeometry, handMaterial);
            leftHand.position.set(-1.5, 1.5, 0);
            santaGroup.add(leftHand);
            
            const rightHand = new THREE.Mesh(handGeometry, handMaterial);
            rightHand.position.set(1.5, 1.5, 0);
            santaGroup.add(rightHand);
            
            // Head with more realistic shape
            const headGeometry = new THREE.SphereGeometry(0.45, 32, 32);
            const headMaterial = new THREE.MeshStandardMaterial({ 
                color: 0xFFDAB9,
                roughness: 0.85,
                metalness: 0.15
            });
            const head = new THREE.Mesh(headGeometry, headMaterial);
            head.position.y = 2.4;
            santaGroup.add(head);
            
            // Face details
            const eyeGeometry = new THREE.SphereGeometry(0.05, 16, 16);
            const eyeMaterial = new THREE.MeshStandardMaterial({ color: 0x000000 });
            
            const leftEye = new THREE.Mesh(eyeGeometry, eyeMaterial);
            leftEye.position.set(-0.15, 2.5, 0.4);
            santaGroup.add(leftEye);
            
            const rightEye = new THREE.Mesh(eyeGeometry, eyeMaterial);
            rightEye.position.set(0.15, 2.5, 0.4);
            santaGroup.add(rightEye);
            
            // Nose
            const noseGeometry = new THREE.SphereGeometry(0.08, 16, 16);
            const noseMaterial = new THREE.MeshStandardMaterial({ color: 0xFF6347 });
            const nose = new THREE.Mesh(noseGeometry, noseMaterial);
            nose.position.set(0, 2.4, 0.42);
            santaGroup.add(nose);
            
            // Hat with fur trim
            const hatBaseGeometry = new THREE.CylinderGeometry(0.45, 0.45, 0.2, 32);
            const hatBaseMaterial = new THREE.MeshStandardMaterial({ color: 0xC41E3A });
            const hatBase = new THREE.Mesh(hatBaseGeometry, hatBaseMaterial);
            hatBase.position.y = 2.75;
            santaGroup.add(hatBase);
            
            const hatTopGeometry = new THREE.ConeGeometry(0.2, 0.8, 32);
            const hatTop = new THREE.Mesh(hatTopGeometry, hatBaseMaterial);
            hatTop.position.y = 3.15;
            santaGroup.add(hatTop);
            
            // Fur trim
            const furGeometry = new THREE.TorusGeometry(0.5, 0.08, 16, 32);
            const furMaterial = new THREE.MeshStandardMaterial({ color: 0xFFFFFF });
            const fur = new THREE.Mesh(furGeometry, furMaterial);
            fur.position.y = 2.75;
            fur.rotation.x = Math.PI / 2;
            santaGroup.add(fur);
            
            // Beard
            const beardGeometry = new THREE.ConeGeometry(0.4, 0.6, 32);
            const beardMaterial = new THREE.MeshStandardMaterial({ color: 0xFFFFFF });
            const beard = new THREE.Mesh(beardGeometry, beardMaterial);
            beard.position.y = 2.2;
            beard.rotation.x = Math.PI;
            santaGroup.add(beard);

            // Position Santa to the side
            santaGroup.position.set(-5, 0, 3);
            santaGroup.scale.set(1, 1, 1);
            
            return santaGroup;
        }

        const santa = createSanta();
        scene.add(santa);

        // Improved snowflakes with better visuals and behavior
        function createSnowflakes(count) {
            const snowflakes = [];
            
            // Create different snowflake geometries for variety
            const snowflakeGeometries = [
                new THREE.IcosahedronGeometry(0.05, 0),
                new THREE.OctahedronGeometry(0.05, 0),
                new THREE.TetrahedronGeometry(0.05, 0)
            ];
            
            const snowflakeMaterial = new THREE.MeshStandardMaterial({ 
                color: 0xFFFFFF,
                transparent: true,
                opacity: 0.9,
                roughness: 0.1,
                metalness: 0.9,
                emissive: 0xFFFFFF,
                emissiveIntensity: 0.1
            });

            for (let i = 0; i < count; i++) {
                // Randomly choose a snowflake geometry
                const geometry = snowflakeGeometries[Math.floor(Math.random() * snowflakeGeometries.length)];
                const snowflake = new THREE.Mesh(geometry, snowflakeMaterial);
                
                // Random position with more variation
                snowflake.position.x = (Math.random() - 0.5) * 60;
                snowflake.position.y = Math.random() * 25 + 5;
                snowflake.position.z = (Math.random() - 0.5) * 60;
                
                // Random size with more realistic range
                const size = Math.random() * 0.06 + 0.01;
                snowflake.scale.set(size, size, size);
                
                // Add to scene and array with more properties for realistic movement
                scene.add(snowflake);
                snowflakes.push({
                    mesh: snowflake,
                    speed: Math.random() * 0.02 + 0.005,
                    rotationSpeed: Math.random() * 0.03 - 0.015,
                    swaySpeed: Math.random() * 0.02 + 0.01,
                    swayAmount: Math.random() * 0.3 + 0.1,
                    initialX: snowflake.position.x,
                    initialZ: snowflake.position.z,
                    timeOffset: Math.random() * Math.PI * 2
                });
            }
            
            return snowflakes;
        }

        const snowflakes = createSnowflakes(200);

        // Enhanced lighting for more realistic and festive atmosphere
        function createLights() {
            // Main directional light (sun) with warm winter tone
            const dirLight = new THREE.DirectionalLight(0xFFFAF0, 0.8);
            dirLight.position.set(8, 15, 10);
            dirLight.castShadow = true;
            
            // Configure shadow properties for better quality
            dirLight.shadow.mapSize.width = 2048;
            dirLight.shadow.mapSize.height = 2048;
            dirLight.shadow.camera.near = 0.5;
            dirLight.shadow.camera.far = 50;
            dirLight.shadow.camera.left = -20;
            dirLight.shadow.camera.right = 20;
            dirLight.shadow.camera.top = 20;
            dirLight.shadow.camera.bottom = -20;
            
            scene.add(dirLight);

            // Ambient light with cool winter tone
            const ambientLight = new THREE.AmbientLight(0xB0E0E6, 0.4);
            scene.add(ambientLight);

            // Rim light for depth
            const rimLight = new THREE.DirectionalLight(0x87CEEB, 0.3);
            rimLight.position.set(-5, 5, -5);
            scene.add(rimLight);

            // Colored lights for Christmas atmosphere with pulsing effect
            const coloredLights = [];
            const lightColors = [0xFF0000, 0x00FF00, 0xFFFF00, 0xFF69B4, 0x00BFFF];
            
            for (let i = 0; i < 8; i++) {
                const color = lightColors[i % lightColors.length];
                const intensity = 0.6 + Math.random() * 0.4;
                const range = 8 + Math.random() * 4;
                
                const light = new THREE.PointLight(color, intensity, range);
                
                // Random position around the tree
                const angle = Math.random() * Math.PI * 2;
                const radius = 2 + Math.random() * 2;
                const height = 2 + Math.random() * 5;
                
                light.position.x = Math.cos(angle) * radius;
                light.position.y = height;
                light.position.z = Math.sin(angle) * radius;
                
                // Add light to scene and store for animation
                scene.add(light);
                coloredLights.push({
                    light: light,
                    baseIntensity: intensity,
                    pulseSpeed: 1 + Math.random() * 2,
                    pulseOffset: Math.random() * Math.PI * 2
                });
            }
            
            // Store lights for animation
            scene.userData.coloredLights = coloredLights;
        }

        // Animate colored lights
        function animateLights() {
            if (!scene.userData.coloredLights) return;
            
            const time = Date.now() * 0.001;
            
            scene.userData.coloredLights.forEach(lightData => {
                const pulse = Math.sin(time * lightData.pulseSpeed + lightData.pulseOffset) * 0.3 + 0.7;
                lightData.light.intensity = lightData.baseIntensity * pulse;
            });
        }

        createLights();

        // Enhanced snowflake animation with realistic movement
        function animateSnowflakes() {
            const time = Date.now() * 0.001;
            
            snowflakes.forEach(snowflake => {
                // Move down with slight acceleration
                snowflake.mesh.position.y -= snowflake.speed * (1 + Math.sin(time * 0.5) * 0.2);
                
                // Rotate with varying speed
                snowflake.mesh.rotation.x += snowflake.rotationSpeed * (1 + Math.cos(time * 0.3) * 0.3);
                snowflake.mesh.rotation.y += snowflake.rotationSpeed * (1 + Math.sin(time * 0.4) * 0.3);
                
                // Swaying motion for more natural falling
                const swayTime = time * snowflake.swaySpeed + snowflake.timeOffset;
                snowflake.mesh.position.x = snowflake.initialX + Math.sin(swayTime) * snowflake.swayAmount;
                snowflake.mesh.position.z = snowflake.initialZ + Math.cos(swayTime) * snowflake.swayAmount * 0.5;
                
                // Gentle pulsing effect
                const pulse = Math.sin(time * 2 + snowflake.timeOffset) * 0.1 + 0.9;
                snowflake.mesh.material.opacity = 0.8 + pulse * 0.1;
                
                // Reset if fallen below ground with new random properties
                if (snowflake.mesh.position.y < -1) {
                    snowflake.mesh.position.y = 25;
                    snowflake.initialX = (Math.random() - 0.5) * 60;
                    snowflake.initialZ = (Math.random() - 0.5) * 60;
                    snowflake.mesh.position.x = snowflake.initialX;
                    snowflake.mesh.position.z = snowflake.initialZ;
                    snowflake.timeOffset = Math.random() * Math.PI * 2;
                }
            });
        }

        // Animate decorations
        function animateDecorations() {
            christmasTree.children.forEach(child => {
                if (child.material && child.material.emissive) {
                    // Pulsing effect for decorations
                    const time = Date.now() * 0.001;
                    const pulse = Math.sin(time * 3) * 0.2 + 0.8;
                    child.material.emissiveIntensity = pulse * 0.3;
                }
            });
        }

        // Enhanced main animation loop
        function animate() {
            requestAnimationFrame(animate);
            
            controls.update();
            animateSnowflakes();
            animateDecorations();
            animateLights();
            
            // More natural Santa animation
            const time = Date.now() * 0.001;
            santa.rotation.y = Math.sin(time * 0.5) * 0.3;
            
            // Subtle breathing effect for Santa
            const breathScale = Math.sin(time * 2) * 0.02 + 1;
            santa.scale.set(breathScale, breathScale, breathScale);
            
            // Gentle tree swaying in the wind
            const treeSway = Math.sin(time * 0.3) * 0.03;
            christmasTree.rotation.y = treeSway;
            
            renderer.render(scene, camera);
        }

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

        // Start animation
        animate();

        // Add some initial animation with GSAP
        gsap.from(christmasTree.scale, {
            x: 0.1,
            y: 0.1,
            z: 0.1,
            duration: 1.5,
            ease: "back.out(1.7)"
        });

        gsap.from(santa.position, {
            x: -10,
            duration: 2,
            delay: 0.5,
            ease: "power2.out"
        });

        gsap.from(snowflakes, {
            opacity: 0,
            duration: 2,
            stagger: 0.01
        });
    </script>
</body>
</html>
posted @ 2025-12-25 21:44  懒惰的星期六  阅读(8)  评论(0)    收藏  举报