一、实现效果
使用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小狗,包含以下特征:
-
使用圆柱体作为身体和四肢
-
球体作为头部
-
圆锥体作为耳朵
-
黑色小球作为鼻子,眼睛
-
简单的摇尾巴动画
-
支持鼠标控制视角(旋转、缩放)
-
响应式布局(自动适应窗口大小)
使用方法:
-
将代码保存为HTML文件
-
用现代浏览器打开
-
你可以通过鼠标:
-
左键拖动旋转视角
-
右键拖动平移视角
-
滚轮缩放
-
可以调整的部分:
-
颜色值(修改各个材质的color参数)
-
几何体尺寸(修改各个Geometry的参数)
-
动画效果(修改animate函数中的变换参数)
-
光照参数(调整光源位置和强度)
要创建更逼真的模型,可以考虑:
-
使用更复杂的几何体
-
添加纹理贴图
-
使用3D建模软件创建模型后导入
-
添加骨骼动画系统
-
增加更多细节(眼睛、毛发等)
三、重点代码片段讲解
片段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;
}
重点解析:
-
组(Group)对象:
-
THREE.Group()
创建了一个空容器,用于组合所有小狗部件 -
类似于HTML中的
<div>
容器,可以统一控制所有子元素的位置/旋转/缩放
-
-
坐标系操作:
-
body.rotation.z = Math.PI/2
将圆柱体旋转90度,使其水平放置 -
head.position.set(1.5, 0, 0)
将头部定位在身体右侧(Three.js坐标系:x右,y上,z屏幕外)
-
-
部件层级关系:
-
通过
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);
}
重点解析:
-
动画循环机制:
-
requestAnimationFrame
是浏览器提供的动画API,以约60fps的频率循环调用animate函数 -
每次循环都会重新渲染场景(
renderer.render
)
-
-
数学函数实现动画:
-
Math.sin(Date.now() * 0.005)
通过正弦函数生成周期性数值 -
*0.5 -0.5
将数值范围从[-1,1]映射到[-1,0],实现尾巴摆动效果 -
随时间变化的参数 (
Date.now()
) 驱动动画持续进行
-
-
部件索引访问:
-
dog.children[6]
访问之前添加到组中的第7个元素(尾巴) -
通过修改
rotation.z
实现绕z轴旋转(Three.js使用右手坐标系)
-
补充说明:
-
这两个片段分别代表了Three.js的:
-
场景构建:通过组合基本几何体创建复杂模型
-
动态交互:使用数学函数驱动动画变化
-
-
实际开发中可以通过给部件命名(而不是使用children索引)来更安全地访问特定部件
-
通过调整正弦函数的参数(如将0.005改为0.01),可以改变尾巴摆动的速度
可以尝试修改这些参数观察效果变化,这是理解Three.js动画原理的最佳实践方式!