一、实现效果
使用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动画原理的最佳实践方式!