你还在为平面而感到没有视觉效果吗?
下面将手把手建立一个3D立体地球,实现自转;那个进行鼠标拖拽以及窗口调整。
直入主题,先想清楚要怎么利用three.js进行搭建
一、制作思路
从拍照视角: 在构建一个引人入胜的3D地球展示时,我们首先需要搭建好舞台。这个"舞台"由三个主要元素构成:场景(scene)、相机(camera)和渲染器(renderer)。场景是所有3D对象的容器;相机决定了从哪个视角观察这些对象;而渲染器则负责将整个3D世界转化为2D图像呈现在屏幕上。
从角色视角:为了确保我们的主角------3D地球(earth)能够以最佳状态展现给观众,我们不仅需要精心设置其几何形状和材质,还可能要添加光源(light)来照亮它,以及实现交互功能,比如让地球自转或通过鼠标拖拽来改变视角。这一切都将在createEarth函数中得以实现,该函数负责创建并配置地球模型。
二、使用Three.js进行构建
要在网页上嵌入3D内容,我们需要引入Three.js库。这是通过在HTML文档的头部加入如下脚本来完成的:
html
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/build/three.min.js"></script>
三、实例代码解析
1. 初始化场景、相机与光源
初始化是构建3D应用的第一步。在这里,我们将设置场景的背景颜色为白色,并创建一个透视相机,它模拟了人类视觉的效果,使得近大远小。同时,我们还需要设置一个WebGL渲染器,并将其尺寸调整为与浏览器窗口匹配。为了让地球看起来更真实,我们还添加了一个方向光,它模仿太阳光,照亮了整个场景。(细心吧~~)
html
function init() {
// 创建场景并设定背景色
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff); // 白色背景
// 创建透视相机
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 2000);
camera.position.z = 500; // 设置相机位置
// 创建WebGL渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 添加地球到场景
createEarth(scene);
// 添加方向光源
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(500, 500, 500).normalize();
scene.add(light);
// 监听窗口大小变化
window.addEventListener('resize', () => onWindowResize(camera, renderer), false);
// 鼠标事件监听
document.addEventListener('mousemove', (event) => onMouseMove(event, camera), false);
}
监听窗口和鼠标点击事件,后文给出代码
2. 构建地球模型
地球模型的创建依赖于加载外部的纹理图片,这可以通过Three.js提供的TextureLoader来完成。我们将纹理应用到球体几何体上,从而形成一个逼真的地球表面。此外,我们还设置了材质的高光属性,以增强视觉效果。
html
function createEarth(scene) {
const loader = new THREE.TextureLoader();
loader.load('./earth.jpg', (texture) => {
const geometry = new THREE.SphereGeometry(200, 32, 32); // 增加细分段数提高质量
const material = new THREE.MeshPhongMaterial({
map: texture,
shininess: 50 // 提高高光强度
});
const earth = new THREE.Mesh(geometry, material);
scene.add(earth);
});
}
平面图,需要放到地球模型框架形成3D。有需要自取:
3. 窗口大小调整处理
当用户调整浏览器窗口大小时,我们希望3D场景能够自动适应新的尺寸。为此,我们定义了一个onWindowResize函数,它会更新相机的宽高比和投影矩阵,并调整渲染器的大小。
html
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();// 更新相机投影矩阵
renderer.setSize(window.innerWidth, window.innerHeight);
}
4. 鼠标交互
为了让用户体验更加生动,我们实现了基于鼠标的交互功能。通过捕捉鼠标移动事件,我们可以平滑地调整相机的位置,从而让用户从不同角度观察地球。
html
function onMouseMove(event) {
mouseX = (event.clientX - windowHalfX)/10;
//使鼠标在窗口中心时 mouseY 的值为0,向上移动时为负值,向下移动时为正值。
mouseY = (event.clientX - windowHalfX)/10;// y 一半
}
5. 动画效果
为了让地球"活"起来,我们需要创建动画循环。Three.js提供了requestAnimationFrame方法,它可以确保动画以每秒60帧的速度流畅播放。在这个循环中,我们会调用render函数来更新场景和相机的状态,并最终将它们渲染到屏幕上。
html
function animate() {
// 加载动画帧 60桢/s
requestAnimationFrame(animate);
render();
}
function render() {
camera.position.x +=(mouseX-camera.position.x )* 0.05;
camera.position.y +=(mouseX-camera.position.x )* 0.05;
camera.lookAt(scene.position); // 相机看向场景
// 旋转地球
if (earth) {
earth.rotation.y -= 0.005;
}
// 渲染场景
renderer.render(scene, camera);
}
// 启动动画
init();
animate();
下面再看看效果
完整代码
快去试一下吧,下面有进行注释解释代码
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D 411</title>
<style>
body { margin: 0; }
canvas { display: block; }
</style>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/build/three.min.js"></script>
</head>
<body>
<script>
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() {
// 创建场景
scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff); // 白色背景
group = new THREE.Group();// 组
scene.add(group);
// 创建相机
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 2000);
camera.position.z = 500;
// 创建渲染器并添加到DOM
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 添加地球
createEarth();
// 添加光源
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(500, 500, 500).normalize();
scene.add(light);
// 监听窗口大小改变
window.addEventListener('resize', onWindowResize, false);
// 鼠标
document.addEventListener('mousemove', onMouseMove, false);
}
function onMouseMove(event) {
mouseX = (event.clientX - windowHalfX)/10;
//使鼠标在窗口中心时 mouseY 的值为0,向上移动时为负值,向下移动时为正值。
mouseY = (event.clientX - windowHalfX)/10;// y 一半
}
function createEarth() {
// 加载地球纹理
const loader = new THREE.TextureLoader();
// 加载指定的纹理图像,并在加载完成后执行回调函数
loader.load('./earch.jpg', function (texture) {
// 创建地球几何体和材质
const geometry = new THREE.SphereGeometry(200, 20,20);
const material = new THREE.MeshPhongMaterial({
map: texture, // 漫反射贴图
shininess: 10 // 高光
});
// 创建地球网格并添加到场景中
earth = new THREE.Mesh(geometry, material);
group.add(earth);
});
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();// 更新相机投影矩阵
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
// 加载动画帧 60桢/s
requestAnimationFrame(animate);
render();
}
function render() {
camera.position.x +=(mouseX-camera.position.x )* 0.05;
camera.position.y +=(mouseX-camera.position.x )* 0.05;
camera.lookAt(scene.position); // 相机看向场景
// 旋转地球
if (earth) {
earth.rotation.y -= 0.005;
}
// 渲染场景
renderer.render(scene, camera);
}
</script>
</body>
</html>