在这个信息爆炸的时代,互联网不仅连接了全球的人们,也为我们打开了通往无限可能的大门。想象一下,如果你能通过简单的代码,在网页上创建一个逼真的3D地球,让你的访客无需离开座位就能探索这个蓝色星球的每一个角落,那会是多么酷的一件事!今天,我们将一起踏上这段奇妙的编程之旅,利用JavaScript和Three.js的力量,从零开始打造一个互动式的3D地球模型。
无论是对于想要提升技能的前端开发者,还是对编程充满好奇的新手来说,这篇文章都将为你提供一步步的指导,带你深入浅出地了解如何将创意变为现实。我们不仅仅是在编写代码;我们正在创造一个窗口,透过它可以看到我们美丽的地球——用数字化的方式呈现其壮观的地貌、丰富的色彩以及动态的变化。
准备好迎接挑战了吗?让我们一起开启这段激动人心的旅程,探索如何使用现代Web技术来讲述地球的故事。接下来,我将向你展示整个项目的框架搭建过程,从最基本的HTML结构到复杂的3D图形渲染,一步一步教你如何构建属于你自己的3D地球。继续阅读,让我们的冒险现在就开始吧!🌍✨
从零开始构建3D地球
准备工作
首先,我们需要设置HTML文档的基本结构。这包括定义字符编码、视口设置以确保移动设备友好性,以及加载Three.js库——这是我们的主要工具,它让WebGL变得简单易用。我们还设置了页面标题为“3D 地球”,以便用户一眼就能知道这个页面的主题。
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D 地球</title>
<script src="https://unpkg.com/three@0.128.0/build/three.min.js"></script>
</head>
构建3D环境
接下来,是时候创建我们的画布了,也就是3D地球将要展示的地方。我们通过<canvas>元素来定义这个空间,并为其分配一个ID(如webglcanvas),以便稍后在JavaScript中引用。
<body>
<canvas id="webglcanvas"></canvas>
</body>
<canvas id="webglcanvas"></canvas> 是一个HTML元素,它为网页提供了一个可以绘制图形的区域。这个元素本身并不显示任何内容,直到你使用JavaScript代码来操作它。<canvas> 元素是HTML5引入的一个重要特性,允许开发者在网页上进行图形渲染、动画制作和其他视觉效果开发。
具体来说:
<canvas>标签:定义了HTML文档中的一个绘图区。默认情况下,它是一个没有任何内容的矩形框,通常宽度为300像素,高度为150像素,除非你通过属性或CSS样式指定不同的尺寸。id="webglcanvas":这里的id属性给这个特定的<canvas>元素指定了一个唯一的标识符,名为“webglcanvas”。这个ID用于在JavaScript中引用该元素,以便对它进行编程控制。例如,你可以用document.getElementById('webglcanvas')来获取这个元素,并对其执行绘图操作。
在这个上下文中,<canvas id="webglcanvas"></canvas> 被用来作为Three.js库的渲染目标。Three.js是一个用于创建和展示3D图形的JavaScript库,它内部使用WebGL(一种低级3D图形API)来进行高效的图形渲染。通过将Three.js的渲染器指向这个<canvas>元素,我们可以在这个画布上呈现复杂的3D场景,如文章中提到的互动式3D地球。
当你结合Three.js与这个<canvas>元素时,实际上是在告诉Three.js:“我想要在这个特定的HTML <canvas>元素内绘制我的3D内容。” 之后,所有由Three.js生成的3D图形都将被渲染到这个具有id="webglcanvas"的画布上,从而让访问者可以在网页上看到一个交互式的3D地球模型。
编写JavaScript代码
1. 初始化全局变量
<script>
// 定义全局变量,用于存储3D环境的关键组件
let canvas, camera, scene, renderer, group;
// 鼠标移动时的坐标以及窗口中心点的位置
let mouseX = 0, mouseY = 0;
let windowHalfX = window.innerWidth / 2;
let windowHalfY = window.innerHeight / 2;
// 初始化并启动动画循环
init();
animate();
-
canvas:这是一个HTML<canvas>元素的引用,Three.js将在其上渲染3D内容。 -
camera:代表一个虚拟相机,用来模拟观察者的视角。在3D场景中,所有可见的内容都是通过这个相机“拍摄”出来的。 -
scene:这是3D世界的容器,所有的对象(如网格、灯光等)都必须添加到这个场景中才能被渲染出来。 -
renderer:负责将3D图形转换为2D图像并绘制到canvas元素上。这里使用的是WebGL渲染器,它是Three.js中最常用的渲染器之一。 -
group:是一个可以包含多个对象的容器。通过将对象添加到组中,可以更方便地对这些对象进行统一管理,比如同时移动或旋转它们。 -
mouseX,mouseY:这两个变量用于跟踪鼠标指针相对于窗口中心的偏移量。当用户移动鼠标时,这些值会被更新,从而允许我们根据鼠标的移动来调整相机的角度或其他互动效果。 -
windowHalfX,windowHalfY:分别表示窗口宽度和高度的一半。它们用来计算鼠标位置相对于窗口中心的偏移,确保即使窗口大小改变,鼠标控制仍然准确。 -
init():这是一个自定义函数,用于设置3D环境的基本组件,包括创建和配置canvas、camera、scene、renderer等。此外,它还负责加载地球表面的纹理图片,并构建地球模型。 -
animate():这是动画循环的入口函数。它使用了浏览器提供的requestAnimationFrame方法,以尽可能高的帧率递归调用自身,从而保持动画流畅播放。每次调用animate()时,都会执行一次render()函数,更新并绘制当前帧的画面。
2. 初始化函数 init()
// 初始化3D场景
function init() {
// 获取Canvas元素作为Three.js的渲染目标
canvas = document.getElementById('webglcanvas');
// 创建透视相机,并设定其属性(视场角、宽高比、近裁剪面、远裁剪面)
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 2000);
camera.position.z = 500; // 设置相机位置,使其远离原点以观察整个场景
// 创建一个空白场景,并设置背景颜色为白色
scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
// 创建一个组来管理场景中的物体,并将其添加到场景中
group = new THREE.Group();
scene.add(group);
// 使用纹理加载器加载地球表面的纹理图片
const loader = new THREE.TextureLoader();
loader.load('land_ocean_ice_cloud_2048.jpg', (texture) => {
// 创建球体几何形状,参数分别为半径、宽度分段数、高度分段数
const geometry = new THREE.SphereGeometry(200, 20, 20);
// 创建材质,并应用加载的纹理
const material = new THREE.MeshBasicMaterial({ map: texture });
// 使用几何形状和材质创建网格(Mesh),即我们的3D地球模型
const mesh = new THREE.Mesh(geometry, material);
group.add(mesh); // 将网格添加到组中
// 创建WebGL渲染器,并指定渲染的目标是之前获取的Canvas元素
renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true // 开启抗锯齿功能,提高图像质量
});
// 设置渲染器输出画布的尺寸,确保它能充满整个浏览器窗口
renderer.setSize(window.innerWidth, window.innerHeight);
// 监听鼠标移动事件,以便用户可以通过移动鼠标来改变视角
document.addEventListener('mousemove', onDocumentMouseMove, false);
});
}
-
这里的
init()函数负责初始化整个3D环境。首先,通过document.getElementById获取了页面中的一个具有id="webglcanvas"的<canvas>元素,这个元素将被用作Three.js的渲染目标。 -
接下来,创建了一个
THREE.PerspectiveCamera对象来模拟现实世界中的摄影机。这里指定了视角(60度),根据窗口尺寸计算出的宽高比,以及近平面和远平面的距离。然后设置了相机的位置,使它在Z轴上离原点足够远,这样就可以看到整个场景。 -
THREE.Scene用于创建一个新的场景,所有要显示的对象都需要添加到这个场景中。这里的背景色被设为白色(RGB值0xffffff)。 -
THREE.Group是一个特殊的对象,它可以包含其他3D对象。这有助于组织和管理多个对象,比如一起移动或旋转它们。这个组被添加到了场景中。 -
纹理加载器
THREE.TextureLoader被用来异步加载一张名为land_ocean_ice_cloud_2048.jpg的图片,这张图片将作为地球表面的纹理。 -
当纹理加载完成后,会创建一个球体几何结构和基本材质,并将加载的纹理应用到材质上。然后使用几何结构和材质创建一个网格(Mesh),代表3D地球模型,并将其添加到前面创建的组中。
-
最后,创建了一个WebGL渲染器,并设置了它的输出画布大小与浏览器窗口一致。还注册了一个监听器来响应鼠标的移动,允许用户通过移动鼠标来改变视角。
3. 用户交互处理
// 处理鼠标移动事件,更新mouseX和mouseY的值
function onDocumentMouseMove(event) {
mouseX = (event.clientX - windowHalfX) * 10;
mouseY = (event.clientY - windowHalfY) * 10;
}
event.clientX和event.clientY分别返回鼠标指针相对于视口(viewport)左边缘和顶边缘的位置(以像素为单位)。注意这里用的是视口而不是整个页面,因此即使页面有滚动条,clientX和clientY也只考虑当前可视区域。windowHalfX和windowHalfY应该是之前已经定义好的变量,它们分别代表了浏览器窗口宽度的一半和高度的一半。这允许我们计算鼠标相对于窗口中心的位置,而不仅仅是相对于左上角的位置。(event.clientX - windowHalfX)和(event.clientY - windowHalfY)计算出鼠标指针与窗口中心点之间的水平和垂直距离。如果鼠标位于窗口中心,则这两个表达式的值都是0;如果不在中心,那么它们会给出相应的正负值来表示偏移的方向和大小。- 最后,结果乘以10是为了放大这个偏移量,使得鼠标移动对场景中的变化更加明显。这样的放大因子可以根据具体需求调整,比如为了使交互更加灵敏或平缓。
4. 动画与渲染
// 动画循环,每帧调用自身一次,保持60帧/秒的刷新率
function animate() {
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); // 让相机始终看向场景的中心
// 使地球自转,创造动态效果
group.rotation.y -= 0.005;
// 使用渲染器将当前场景和相机状态绘制到Canvas上
renderer.render(scene, camera);
}
</script>
-
THREE.TextureLoader():这是Three.js提供的用于加载图像文件并将其转换为纹理对象的类。纹理可以应用于材质上,以赋予3D对象真实的外观。 -
loader.load(url, onLoad):调用load方法来异步加载指定路径(url)的图像文件。当图像成功加载后,会执行回调函数onLoad,并将加载好的纹理作为参数传递给它。这里的'land_ocean_ice_cloud_2048.jpg'是地球表面纹理图片的文件名。 -
THREE.SphereGeometry(radius, widthSegments, heightSegments):创建一个球体几何形状。参数分别是球体的半径(200),以及沿经度和纬度方向上的分段数量(20)。更多的分段可以使球体更加平滑,但也会增加计算量。 -
THREE.MeshBasicMaterial(options):定义了一个基础材质,适用于不考虑光照效果的简单场景。通过设置options.map属性为之前加载的纹理,可以让材质显示该纹理,从而实现地球表面的真实感。 -
THREE.Mesh(geometry, material):根据给定的几何形状和材质创建一个网格对象。在这个例子中,网格代表了我们想要在场景中展示的3D地球模型。 -
group.add(mesh):将创建的网格对象添加到前面提到的group中。这样做可以方便地管理多个3D对象,例如一起移动或旋转它们。 -
THREE.WebGLRenderer(options):创建一个WebGL渲染器,它负责将3D图形绘制到HTML<canvas>元素上。选项包括指定要使用的canvas元素,以及是否开启抗锯齿(antialias),后者有助于提升图像边缘的质量。 -
renderer.setSize(width, height):设置渲染器输出画布的尺寸,这里设置了与当前浏览器窗口相同的大小,以确保渲染的内容能够完全填充整个视窗。 -
document.addEventListener('mousemove', callback, useCapture):向文档添加了一个鼠标移动事件监听器。每当用户移动鼠标时,都会触发onDocumentMouseMove函数,允许根据用户的动作动态调整相机位置或其他交互效果。false表示事件处于冒泡阶段处理,默认行为。
完整代码以及图片
代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D 地球</title>
<!-- 画地球 选择框架 加速 -->
<script src="https://unpkg.com/three@0.128.0/build/three.min.js"></script>
</head>
<body>
<canvas id="webglcanvas"></canvas>
<script>
// 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();
// 准备
function init() {
canvas = document.getElementById('webglcanvas'); // DOM
camera = new THREE.PerspectiveCamera(60,
window.innerWidth / window.innerHeight, 1, 2000); // 实例化 相机
// 相机离scene场景
camera.position.z = 500;
scene = new THREE.Scene(); // 实例化 场景
scene.background = new THREE.Color(0xffffff); // 背景色
group = new THREE.Group();// 组
scene.add(group);
// 纹理加载器
let loader = new THREE.TextureLoader(); // 简单的加载器
loader.load('land_ocean_ice_cloud_2048.jpg', function (texture) {
let geometry = new THREE.SphereGeometry(200, 20, 20); // 球体
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
});
renderer.setSize(window.innerWidth, window.innerHeight);
// renderer.render(scene, camera);
document.addEventListener('mousemove', onDocumentMouseMove, false);
})
}
function onDocumentMouseMove(event) {
mouseX = (event.clientX - windowHalfX) * 10;
mouseY = (event.clientY - windowHalfY) * 10;
}
function animate() {
// 递归 屏幕的刷帧率 60帧/s
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);
group.rotation.y -= 0.005;
renderer.render(scene, camera);
// requestAnimationFrame(render);
}
</script>
</body>
</html>
图片
结语
通过这些简单的步骤,我们就能够创建出一个令人惊叹的互动3D地球展示。Three.js的强大之处在于它不仅简化了WebGL编程,还提供了丰富多样的功能,使得即使是编程新手也能轻松制作出专业级的3D效果。希望这篇文章能激发你的灵感,去探索更多关于3D图形和Web开发的可能性!