一、实现效果
使用Three.js实现裸眼3D地球仪
二、实现代码
代码如下:
html
<!DOCTYPE html>
<html>
<head>
<title>3D Earth</title>
<style>
body { margin: 0; }
canvas { display: block; }
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.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();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 创建地球几何体
const geometry = new THREE.SphereGeometry(5, 50, 50);
// 加载纹理
const textureLoader = new THREE.TextureLoader();
const earthTexture = textureLoader.load('https://threejs.org/examples/textures/planets/earth_atmos_2048.jpg');
const normalTexture = textureLoader.load('https://threejs.org/examples/textures/planets/earth_normal_2048.jpg');
// 创建材质
const material = new THREE.MeshPhongMaterial({
map: earthTexture,
normalMap: normalTexture,
normalScale: new THREE.Vector2(0.8, 0.8),
specular: new THREE.Color('grey'),
shininess: 5
});
// 创建地球网格
const earth = new THREE.Mesh(geometry, material);
scene.add(earth);
// 添加光照
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
const pointLight = new THREE.PointLight(0xffffff, 1.5);
pointLight.position.set(10, 10, 10);
scene.add(pointLight);
// 设置相机位置
camera.position.z = 15;
// 添加自动旋转动画
function animate() {
requestAnimationFrame(animate);
earth.rotation.y += 0.002;
renderer.render(scene, camera);
}
// 处理窗口大小变化
window.addEventListener('resize', onWindowResize, false);
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
// 添加鼠标交互
let isDragging = false;
let previousMousePosition = {
x: 0,
y: 0
};
document.addEventListener('mousedown', (e) => {
isDragging = true;
previousMousePosition = {
x: e.clientX,
y: e.clientY
};
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
const deltaMove = {
x: e.clientX - previousMousePosition.x,
y: e.clientY - previousMousePosition.y
};
earth.rotation.y += deltaMove.x * 0.005;
earth.rotation.x += deltaMove.y * 0.005;
previousMousePosition = {
x: e.clientX,
y: e.clientY
};
});
document.addEventListener('mouseup', () => {
isDragging = false;
});
// 启动动画
animate();
</script>
</body>
</html>
这个示例代码包含以下主要功能:
-
创建了一个基本的Three.js场景,包含:
-
球体几何图形
-
地球纹理贴图(颜色贴图和法线贴图)
-
光影效果(环境光 + 点光源)
-
-
主要特性:
-
地球自动缓慢旋转
-
支持鼠标拖拽交互旋转
-
响应式窗口大小调整
-
法线贴图实现表面细节
-
适当的光照效果
-
-
使用说明:
-
需要网络连接加载纹理贴图
-
可以替换纹理贴图URL使用自定义贴图
-
通过调整SphereGeometry参数控制地球精度
-
修改rotation速度可以改变旋转速度
-
调整camera.position.z改变观察距离
-
-
扩展建议:
-
添加云层(使用透明贴图的第二个球体)
-
添加星空背景
-
添加标记点或国家边界
-
添加放大/缩小功能
-
添加旋转控制按钮
-
注意:由于纹理加载需要时间,首次加载可能会看到短暂的空白场景。实际使用时建议:
-
使用本地纹理文件
-
添加加载进度提示
-
使用更高分辨率的贴图
-
添加错误处理
可以将代码保存为HTML文件直接运行(需要网络连接),也可以部署到Web服务器使用本地资源。
三、重点代码片段
1. 地球材质创建(核心渲染)
javascript
// 加载纹理
const textureLoader = new THREE.TextureLoader();
const earthTexture = textureLoader.load('earth_texture.jpg');
const normalTexture = textureLoader.load('earth_normal.jpg');
// 创建材质
const material = new THREE.MeshPhongMaterial({
map: earthTexture, // 基础颜色贴图
normalMap: normalTexture, // 法线贴图
normalScale: new THREE.Vector2(0.8, 0.8), // 法线强度
specular: new THREE.Color('grey'), // 高光颜色
shininess: 5 // 高光强度
});
-
材质类型选择:
-
使用
MeshPhongMaterial
(冯氏材质),这是实现光照交互的关键 -
支持高光反射效果,适合表现具有反光特性的地球表面
-
-
纹理映射:
-
map
:基础颜色贴图,直接显示地球表面图像 -
normalMap
:法线贴图,通过RGB值模拟表面凹凸细节(无需增加几何体复杂度)
-
-
材质参数:
-
normalScale
:控制法线贴图的强度(值越大凹凸感越强) -
specular
+shininess
:组合控制高光效果(金属感/塑料感调节)
-
-
性能优化:
-
纹理尺寸建议保持2的幂次(如1024x512),否则可能触发Three.js的自动缩放
-
可以添加
bumpMap
或specularMap
实现更复杂的表面细节
-
2. 交互旋转实现(核心交互)
javascript
// 鼠标拖动交互
let isDragging = false;
let previousMousePosition = { x: 0, y: 0 };
document.addEventListener('mousedown', (e) => {
isDragging = true;
previousMousePosition = { x: e.clientX, y: e.clientY };
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
const deltaMove = {
x: e.clientX - previousMousePosition.x,
y: e.clientY - previousMousePosition.y
};
earth.rotation.y += deltaMove.x * 0.005;
earth.rotation.x += deltaMove.y * 0.005;
previousMousePosition = { x: e.clientX, y: e.clientY };
});
document.addEventListener('mouseup', () => {
isDragging = false;
});
-
交互原理:
-
通过监听鼠标事件实现拖拽操作
-
计算两次鼠标位置的差值(
deltaMove
)转换为旋转量
-
-
数学转换:
-
delta.x
影响 Y 轴旋转(水平拖动→左右旋转) -
delta.y
影响 X 轴旋转(垂直拖动→上下旋转) -
0.005
是灵敏度系数,值越大拖动反应越灵敏
-
-
坐标系注意:
-
Three.js使用右手坐标系,Y轴向上
-
旋转顺序遵循物体自身坐标系(可能导致万向节锁问题,复杂交互需要四元数)
-
-
性能优化:
-
使用
requestAnimationFrame
保证动画流畅性 -
避免在事件回调中直接修改DOM
-
