引言
本次我们将使用Three.js
创建一个旋转的3D地球。
我们先看成果图:
少说废话,正式开始!
Three.js
我们先来了解一下Three.js
。
Three.js
是一款基于原生WebGL
封装通用Web 3D
引擎。它让Web
开发者能够轻松地创建复杂的3D
图形和互动体验。通过掌握其核心概念——场景、摄像机、渲染器、几何体、材质、灯光以及动画和交互机制——你可以构建出丰富多彩的3D
应用。
HTML结构 和 背景图
我们需要引入Three.js
库。并以<canvas>
元素作为渲染目标。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D 地球</title>
<!-- 引入Three.js库 -->
<script src="https://unpkg.com/three@0.128.0/build/three.min.js"></script>
</head>
<body>
<canvas id="webglcanvas"></canvas>
</body>
</html>
这是我在本例中使用到的背景图:
JavaScript部分
准备“设备”
我们需要一些“设备”来协助我们拍摄3d地球
canvas
:这是一个HTML<canvas>
元素的引用,Three.js
将使用它作为WebGL的绘图表面。所有渲染的3D内容都将绘制在这个画布上。camera
:代表虚拟摄像机的对象,决定了用户从哪个视角观看3D场景。摄像机的位置、方向和类型(如透视或正交)都会影响最终的视觉效果。scene
:场景是所有3D对象、灯光和其他元素的容器。它是3D世界的“舞台”,所有的物体都需要被添加到这个场景中才能被渲染出来。renderer
:负责将场景中的3D图形转换为2D图像并绘制在canvas
上。Three.js支持多种渲染器,但最常用的是基于WebGL的WebGLRenderer
。group
:是一个THREE.Group
对象,它可以用来组织多个3D对象作为一个整体进行变换(如移动、旋转)。这对于管理复杂的3D结构非常有用。
// 3d 地球
let canvas, // 3d 容器
camera, // 镜头 摄像机
scene, // 场景
renderer, // 渲染器
group; // 组
// 物品
let mouseX = 0, mouseY = 0; // mousemove 坐标
// 球心
let windowHalfX = window.innerWidth / 2; // 窗口宽度的一半
let windowHalfY = window.innerHeight / 2; // 窗口高度的一半
// 待会需要封装两个函数 调用这两个函数
init();
animate();
初始化和设置3D场景的关键组件
接下来在init()
函数中我们要干几件事
- 获取DOM元素
canvas = document.getElementById('webglcanvas'); // 获取DOM
- 创建透视摄像机
Three
中有很多种摄像机,我们在本次使用透视摄像机(PerspectiveCamera
)。并设置相机距离scene
的距离。
// 透视摄像机(PerspectiveCamera)
// 视野角度 宽高比(width/height 横屏) 近裁剪面距离 远裁剪面距离(1~2000 相机可见距离)
camera = new THREE.PerspectiveCamera(60,
window.innerWidth / window.innerHeight, 1, 2000); // 实例化 相机
// 相机离scene场景 500 个单位长度
camera.position.z = 500; //
- 实例化场景和背景颜色
scene = new THREE.Scene(); // 实例化 场景
scene.background = new THREE.Color(0xffffff); // 背景色我们设置为白色
- 创建组并将组添加到场景中
组可以容纳多个3D对象,便于管理和操作一组相关的物体。将组添加到场景中,使其成为渲染流程的一部分,任何添加到该组的对象都会被渲染器处理。
group = new THREE.Group();// 组
scene.add(group); // 添加到场景
- 加载纹理、创建3D地球模型(球体)、设置材质,并将这些元素添加到场景中,同时初始化渲染器和设置鼠标交互功能(鼠标移动事件)。
// 纹理加载器 TextureLoader 适用于异步加载标准的图像文件
let loader = new THREE.TextureLoader(); // 简单的加载器
// 加载纹理并创建3D地球(异步)
// 参数一: 图片路径 参数二:纹理加载完毕后执行回调函数
loader.load('land_ocean_ice_cloud_2048.jpg', function (texture) {
// 创建球体几何体 参数: 半径 宽度分段数 高度分段数 (球心)
let geometry = new THREE.SphereGeometry(200, 20, 20); // 球体
// 设置材质 map指定刚才加载的纹理贴图 到球体上
let material = new THREE.MeshBasicMaterial({
map: texture
});
// 创建网格并添加到组 以便它能够统一管理
let mesh = new THREE.Mesh(geometry, material);
group.add(mesh);
// 初始化渲染器 目标是canvas
renderer = new THREE.WebGLRenderer({
canvas: canvas,
// 抗锯齿 提高图形边缘的质量
antialias: true
});
// 设置渲染器的尺寸与浏览器窗口一致,确保3D内容能够占据整个视窗
renderer.setSize(window.innerWidth, window.innerHeight);
// 添加鼠标移动事件监听器 影响摄像机的角度和地球的旋转 冒泡阶段监听事件
document.addEventListener('mousemove', onDocumentMouseMove, false);
})
- 鼠标移动事件
每当鼠标在页面上移动时,更新mouseX
和mouseY
变量,它们表示鼠标相对于窗口中心的偏移量。
// 获取鼠标相对于窗口中心的具体位置
function onDocumentMouseMove(event) {
// 正为右下 负为左上
mouseX = event.clientX - windowHalfX;
mouseY = event.clientY - windowHalfY;
}
动画循环
animate()
函数是一个递归调用,利用requestAnimationFrame
确保动画以每秒60帧的速度运行。每次调用时,它会执行render()
函数,后者负责实际的渲染逻辑。
function animate() {
// 递归 屏幕的刷帧率 60帧/s
// 浏览器API调用
requestAnimationFrame(animate)
// 实际的渲染工作
render()
}
function render() {
// 摄像机位置更新
// 根据鼠标的位置平滑地调整摄像机的位置
// 缓慢、平滑的过渡
camera.position.x += (mouseX - camera.position.x) * 0.05;
camera.position.y += (mouseY - camera.position.y) * 0.05;
// 摄像机对准场景中心
camera.lookAt(scene.position);
// 地球旋转 绕y轴 每次减小一点弧度
group.rotation.y -= 0.002;
// 调用渲染器的`render`方法,将当前状态下的场景和摄像机信息绘制到画布上
renderer.render(scene, camera);
}
至此,我们已经完成了3d地球的制作!!
总结
通过3d地球的制作,我们学习到Three.js的核心概念和技术,包括场景管理、摄像机控制、几何体创建、材质应用以及渲染过程。此外,还结合了如何通过事件监听器实现基本的用户交互,使得3d地球可以响应用户的鼠标动作。