当前位置: 首页 > news >正文

threeJs实现裸眼3D小狗

一、实现效果

使用threeJs实现裸眼3D小狗,效果如下,其实如果将小狗换成建模小狗,效果更好,这个是模拟了一只小狗。

二、实现代码

代码如下:

<!DOCTYPE html>
<html>
<head>
    <title>星空小狗</title>
    <style>
        body { margin: 0; overflow: hidden; }
        canvas { display: block; }
    </style>
</head>
<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
    <script>
        // 初始化场景、相机和渲染器
        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(0x000000); // 黑色背景
        document.body.appendChild(renderer.domElement);

        // 添加星空背景
        function createStarField() {
            const starsGeometry = new THREE.BufferGeometry();
            const starCount = 5000;
            const positions = new Float32Array(starCount * 3);

            for(let i = 0; i < starCount * 3; i += 3) {
                positions[i] = (Math.random() - 0.5) * 2000;
                positions[i+1] = (Math.random() - 0.5) * 2000;
                positions[i+2] = (Math.random() - 0.5) * 2000;
            }

            starsGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
            const starsMaterial = new THREE.PointsMaterial({
                color: 0xFFFFFF,
                size: 0.7,
                transparent: true
            });
            const starField = new THREE.Points(starsGeometry, starsMaterial);
            scene.add(starField);
        }
        createStarField();

        // 光源配置
        const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
        scene.add(ambientLight);
        const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
        directionalLight.position.set(0, 100, 50);
        scene.add(directionalLight);

        // 创建小狗模型
        function createDog() {
            const dog = new THREE.Group();

            // 身体
            const bodyGeometry = new THREE.CylinderGeometry(1, 1, 2, 8);
            const bodyMaterial = new THREE.MeshPhongMaterial({ color: 0x8B4513 });
            const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
            body.rotation.z = Math.PI/2;
            dog.add(body);

            // 头部
            const headGeometry = new THREE.SphereGeometry(0.8);
            const head = new THREE.Mesh(headGeometry, bodyMaterial);
            head.position.set(1.5, 0, 0);
            dog.add(head);

            // 眼睛
            const eyeGeometry = new THREE.SphereGeometry(0.1);
            const eyeMaterial = new THREE.MeshPhongMaterial({ color: 0x000000 });
            const leftEye = new THREE.Mesh(eyeGeometry, eyeMaterial);
            const rightEye = new THREE.Mesh(eyeGeometry, eyeMaterial);
            leftEye.position.set(0.7, 0.3, 0.3);
            rightEye.position.set(0.7, 0.3, -0.3);
            head.add(leftEye, rightEye);

            // 鼻子
            const nose = new THREE.Mesh(
                new THREE.SphereGeometry(0.2),
                new THREE.MeshPhongMaterial({ color: 0x000000 })
            );
            nose.position.set(0.7, 0, 0);
            head.add(nose);

            // 耳朵
            const earGeometry = new THREE.ConeGeometry(0.3, 0.8, 8);
            const earMaterial = new THREE.MeshPhongMaterial({ color: 0x4B3621 });
            const leftEar = new THREE.Mesh(earGeometry, earMaterial);
            const rightEar = new THREE.Mesh(earGeometry, earMaterial);
            leftEar.position.set(1.2, 0.6, 0.4);
            rightEar.position.set(1.2, 0.6, -0.4);
            leftEar.rotation.z = -0.3;
            rightEar.rotation.z = 0.3;
            dog.add(leftEar, rightEar);

            // 腿
            const legGeometry = new THREE.CylinderGeometry(0.2, 0.2, 1);
            const legs = [
                [0.5, -1.2, 0.5],   // 前左
                [0.5, -1.2, -0.5],  // 前右
                [-0.5, -1.2, 0.5],  // 后左
                [-0.5, -1.2, -0.5]  // 后右
            ];
            legs.forEach(pos => {
                const leg = new THREE.Mesh(legGeometry, bodyMaterial);
                leg.position.set(...pos);
                dog.add(leg);
            });

            // 尾巴
            const tailGeometry = new THREE.CylinderGeometry(0.1, 0.05, 1);
            const tail = new THREE.Mesh(tailGeometry, bodyMaterial);
            tail.position.set(-1.2, -0.5, 0);
            tail.rotation.z = -0.5;
			tail.rotation.x = 10;
            dog.add(tail);

            return dog;
        }

        const dog = createDog();
        scene.add(dog);

        // 相机初始位置
        camera.position.set(0, 3, 8);
        camera.lookAt(0, 0, 0);

        // 轨道控制器
        const controls = new THREE.OrbitControls(camera, renderer.domElement);
        controls.enableDamping = true;
        controls.dampingFactor = 0.05;

        // 动画循环
        function animate() {
            requestAnimationFrame(animate);
            
            // 尾巴摆动
            dog.children[8].rotation.x = Math.sin(Date.now() * 0.005) * 0.5 - 0.5;
            
            controls.update();
            renderer.render(scene, camera);
        }

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

        animate();
    </script>
</body>
</html>

这个示例创建了一个由基本几何体组成的卡通风格3D小狗,包含以下特征:

  1. 使用圆柱体作为身体和四肢

  2. 球体作为头部

  3. 圆锥体作为耳朵

  4. 黑色小球作为鼻子,眼睛

  5. 简单的摇尾巴动画

  6. 支持鼠标控制视角(旋转、缩放)

  7. 响应式布局(自动适应窗口大小)

使用方法:

  1. 将代码保存为HTML文件

  2. 用现代浏览器打开

  3. 你可以通过鼠标:

    • 左键拖动旋转视角

    • 右键拖动平移视角

    • 滚轮缩放

可以调整的部分:

  • 颜色值(修改各个材质的color参数)

  • 几何体尺寸(修改各个Geometry的参数)

  • 动画效果(修改animate函数中的变换参数)

  • 光照参数(调整光源位置和强度)

要创建更逼真的模型,可以考虑:

  1. 使用更复杂的几何体

  2. 添加纹理贴图

  3. 使用3D建模软件创建模型后导入

  4. 添加骨骼动画系统

  5. 增加更多细节(眼睛、毛发等)

三、重点代码片段讲解

片段1:小狗的组(group)创建与部件组合

function createDog() {
    const dog = new THREE.Group();

    // 身体 (圆柱体)
    const bodyGeometry = new THREE.CylinderGeometry(1, 1, 2, 8);
    const bodyMaterial = new THREE.MeshPhongMaterial({ color: 0x8B4513 });
    const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
    body.rotation.z = Math.PI/2; // 让圆柱体横置
    dog.add(body);

    // 头部 (球体)
    const head = new THREE.Mesh(
        new THREE.SphereGeometry(0.8),
        new THREE.MeshPhongMaterial({ color: 0x8B4513 })
    );
    head.position.set(1.5, 0, 0); // 向右偏移
    dog.add(head);

    // 其他部件...
    return dog;
}

重点解析:

  1. 组(Group)对象

    • THREE.Group() 创建了一个空容器,用于组合所有小狗部件

    • 类似于HTML中的<div>容器,可以统一控制所有子元素的位置/旋转/缩放

  2. 坐标系操作

    • body.rotation.z = Math.PI/2 将圆柱体旋转90度,使其水平放置

    • head.position.set(1.5, 0, 0) 将头部定位在身体右侧(Three.js坐标系:x右,y上,z屏幕外)

  3. 部件层级关系

    • 通过dog.add()将各个身体部件添加到组中

    • 最终返回的dog对象包含所有子部件的完整层级结构

片段2:动画循环与尾巴摆动

function animate() {
    requestAnimationFrame(animate);
    
    // 摇尾巴动画
    dog.children[8].rotation.z = Math.sin(Date.now() * 0.005) * 0.5 - 0.5;
    
    controls.update();
    renderer.render(scene, camera);
}

重点解析:

  1. 动画循环机制

    • requestAnimationFrame 是浏览器提供的动画API,以约60fps的频率循环调用animate函数

    • 每次循环都会重新渲染场景(renderer.render)

  2. 数学函数实现动画

    • Math.sin(Date.now() * 0.005) 通过正弦函数生成周期性数值

    • *0.5 -0.5 将数值范围从[-1,1]映射到[-1,0],实现尾巴摆动效果

    • 随时间变化的参数 (Date.now()) 驱动动画持续进行

  3. 部件索引访问

    • dog.children[6] 访问之前添加到组中的第7个元素(尾巴)

    • 通过修改rotation.z实现绕z轴旋转(Three.js使用右手坐标系)


补充说明:

  • 这两个片段分别代表了Three.js的:

    1. 场景构建:通过组合基本几何体创建复杂模型

    2. 动态交互:使用数学函数驱动动画变化

  • 实际开发中可以通过给部件命名(而不是使用children索引)来更安全地访问特定部件

  • 通过调整正弦函数的参数(如将0.005改为0.01),可以改变尾巴摆动的速度

可以尝试修改这些参数观察效果变化,这是理解Three.js动画原理的最佳实践方式!

http://www.dtcms.com/a/123396.html

相关文章:

  • 每天记录一道Java面试题---day38
  • Python设计模式-工厂模式
  • Python设计模式-抽象工厂模式
  • 探索 C 语言数据结构:从基础到实践
  • Design Compiler:中断命令/脚本的执行
  • 【汽车产品开发项目管理——端到端的汽车产品诞生流程】
  • Mysql表的操作(2)
  • (自用)蓝桥杯准备(需要写的基础)
  • 谷歌浏览器极速安装指南
  • 前端面试题(七):什么是vuex,请解释一下它在Vue中的作用
  • minio提供nfs服务
  • 全新突破 | 更全面 · 更安全 · 更灵活
  • 神经网络语言模型与统计语言模型的比较
  • Selenium中`driver.get(htmlfile)`方法可能出现的超时问题
  • 分布式id生成算法(雪花算法 VS 步长id生成)
  • Python Cookbook-5.12 检查序列的成员
  • DAY06:【pytorch】图像增强
  • day29-贪心__134. 加油站__135. 分发糖果__860.柠檬水找零__406.根据身高重建队列
  • 三分钟学会使用java RandomAccessFile随机读写IO
  • 数字内容体验的技术支持包含什么?
  • 公司内部建立apt源
  • Animated Movement Color
  • 【书籍】DeepSeek谈《持续交付2.0》
  • 第5篇:Linux程序访问控制FPGA端LEDR<三>
  • 如何用 nvm alias default 18.20.8 实现全局 Node.js 版本管理?一篇保姆级指南!!!
  • 深入解析回环检测:从原理到C++实战
  • 批量清空图片的相机参数、地理位置等敏感元数据
  • 电商素材革命:影刀RPA魔法指令3.0驱动批量去水印,实现秒级素材净化
  • 【C++】右值引用、移动语义与完美转发
  • 【倍增】P10264 [GESP202403 八级] 接竹竿|普及+