Three.js 打造 3D 地球:从零开始的 WebGL 之旅

Three.js 打造 3D 地球:从零开始的 WebGL 之旅

Three.js 打造 3D 地球:从零开始的 WebGL 之旅

引言:Web 3D 开发的新纪元

想象一下,你只需几行代码,就能在网页上创建一个可交互的 3D 地球模型!

Three.js 是目前最流行的 WebGL 3D 库,它让创建 3D 图形变得简单而强大。今天这篇教程将带你从零开始,用 Three.js 打造一个逼真的 3D 地球模型。

第一章:Three.js 基础

1.1 什么是 Three.js?

Three.js 是一个 JavaScript 3D 库,特点:
  • 基于 WebGL 渲染
  • 跨浏览器支持
  • 简单易用的 API
  • 强大的扩展性
  • 活跃的社区

1.2 核心概念

┌─────────────────────────────────────────┐
│           Three.js 基础架构              │
├─────────────────────────────────────────┤
│  Scene(场景)                          │
│  ├─ Camera(摄像机)                    │
│  │  └─ Renderer(渲染器)              │
│  └─ Objects(对象)                     │
│     ├─ Geometry(几何体)               │
│     ├─ Material(材质)                 │
│     └─ Mesh(网格)                     │
└─────────────────────────────────────────┘

1.3 快速开始

“`html



Three.js 入门







第二章:3D 地球创建

2.1 基础地球模型

javascript
// 创建 3D 地球
function createEarth() {
// 创建球体几何体
const geometry = new THREE.SphereGeometry(5, 64, 64);

// 创建材质
const material = new THREE.MeshPhongMaterial({
color: 0x2233ff, // 蓝色
shininess: 20
});

// 创建网格
const earth = new THREE.Mesh(geometry, material);

return earth;
}

// 添加到场景
const earth = createEarth();
scene.add(earth);


2.2 添加地球材质

javascript
// 加载地球纹理
function loadEarthTextures() {
const textureLoader = new THREE.TextureLoader();

// 颜色贴图
const colorTexture = textureLoader.load(‘earth_color.jpg’);

// 法线贴图(用于光影效果)
const normalTexture = textureLoader.load(‘earth_normal.jpg’);

// 高光贴图(控制反光区域)
const specularTexture = textureLoader.load(‘earth_specular.jpg’);

// 凹凸贴图(增加细节)
const bumpTexture = textureLoader.load(‘earth_bump.jpg’);

// 星空背景
const starTexture = textureLoader.load(‘starfield.jpg’);

return {
colorTexture,
normalTexture,
specularTexture,
bumpTexture,
starTexture
};
}

// 创建带有纹理的地球
function createTexturedEarth() {
const textures = loadEarthTextures();

const geometry = new THREE.SphereGeometry(5, 64, 64);

const material = new THREE.MeshPhongMaterial({
map: textures.colorTexture,
normalMap: textures.normalTexture,
specularMap: textures.specularTexture,
bumpMap: textures.bumpTexture,
bumpScale: 0.1,
specular: new THREE.Color(0x333333),
shininess: 15
});

return new THREE.Mesh(geometry, material);
}


2.3 大气层效果

javascript
// 创建大气层
function createAtmosphere() {
// 创建稍大的球体作为大气层
const geometry = new THREE.SphereGeometry(5.1, 64, 64);

// 使用自定义着色器
const atmosphereShader = {
vertexShader: `
varying vec3 vNormal;
void main() {
vNormal = normalize(normalMatrix * normal);
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
varying vec3 vNormal;
void main() {
float intensity = pow(0.6 – dot(vNormal, vec3(0, 0, 1.0)), 4.0);
gl_FragColor = vec4(0.3, 0.6, 1.0, 1.0) * intensity;
}
`
};

const material = new THREE.ShaderMaterial({
vertexShader: atmosphereShader.vertexShader,
fragmentShader: atmosphereShader.fragmentShader,
blending: THREE.AdditiveBlending,
side: THREE.BackSide,
transparent: true
});

return new THREE.Mesh(geometry, material);
}

// 添加到场景
const earth = createTexturedEarth();
const atmosphere = createAtmosphere();
scene.add(earth);
scene.add(atmosphere);


2.4 云层效果

javascript
// 创建云层
function createClouds() {
const geometry = new THREE.SphereGeometry(5.05, 64, 64);

// 加载云层纹理
const cloudTexture = new THREE.TextureLoader().load(‘clouds.png’);

const material = new THREE.MeshPhongMaterial({
map: cloudTexture,
transparent: true,
opacity: 0.8,
blending: THREE.AdditiveBlending,
side: THREE.DoubleSide
});

const clouds = new THREE.Mesh(geometry, material);

return clouds;
}

// 地球组
const earthGroup = new THREE.Group();
earthGroup.add(earth);
earthGroup.add(createClouds());
scene.add(earthGroup);


第三章:光照和渲染

3.1 添加光源

javascript
// 添加光照
function setupLighting() {
// 环境光
const ambientLight = new THREE.AmbientLight(0x333333);
scene.add(ambientLight);

// 平行光(模拟太阳)
const sunLight = new THREE.DirectionalLight(0xffffff, 1.5);
sunLight.position.set(10, 5, 10);
scene.add(sunLight);

// 添加光晕
const sunGlow = new THREE.PointLight(0xffff00, 2, 100);
sunGlow.position.set(15, 5, 15);
scene.add(sunGlow);
}

setupLighting();


3.2 星空背景

javascript
// 创建星空
function createStarfield() {
const geometry = new THREE.BufferGeometry();
const vertices = [];

// 生成随机星星
for (let i = 0; i < 5000; i++) { const x = (Math.random() - 0.5) * 2000; const y = (Math.random() - 0.5) * 2000; const z = (Math.random() - 0.5) * 2000; vertices.push(x, y, z); } geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); const material = new THREE.PointsMaterial({ color: 0xffffff, size: 0.5, sizeAttenuation: true }); const stars = new THREE.Points(geometry, material); return stars; } scene.add(createStarfield());


3.3 渲染优化

javascript
// 优化渲染设置
const renderer = new THREE.WebGLRenderer({
antialias: true, // 抗锯齿
alpha: true, // 透明背景
powerPreference: ‘high-performance’ // 性能优先
});

renderer.setPixelRatio(window.devicePixelRatio); // 高 DPI 支持
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x000000, 0); // 透明背景
renderer.shadowMap.enabled = true; // 启用阴影
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 柔和阴影

// 后处理效果
import { EffectComposer } from ‘three/examples/jsm/postprocessing/EffectComposer’;
import { RenderPass } from ‘three/examples/jsm/postprocessing/RenderPass’;
import { UnrealBloomPass } from ‘three/examples/jsm/postprocessing/UnrealBloomPass’;

const composer = new EffectComposer(renderer);
const renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);

const bloomPass = new UnrealBloomPass(
new THREE.Vector2(window.innerWidth, window.innerHeight),
1.5, // 强度
0.4, // 半径
0.85 // 阈值
);
composer.addPass(bloomPass);

function animate() {
requestAnimationFrame(animate);

// 使用 composer 渲染
composer.render();
}


第四章:动画和交互

4.1 地球自转动画

javascript
// 地球自转
function animateEarth() {
earthGroup.rotation.y += 0.002; // 自转速度

// 云层自转(稍微快一点)
const clouds = earthGroup.children.find(child => child !== earth);
if (clouds) {
clouds.rotation.y += 0.0025;
}
}

// 主循环
function animate() {
requestAnimationFrame(animate);

animateEarth();

renderer.render(scene, camera);
}

animate();


4.2 摄像机控制

javascript
// 使用 OrbitControls
import { OrbitControls } from ‘three/examples/jsm/controls/OrbitControls’;

const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; // 惯性效果
controls.dampingFactor = 0.05;
controls.rotateSpeed = 0.5;
controls.enablePan = false; // 禁用平移
controls.minDistance = 7; // 最近距离
controls.maxDistance = 50; // 最远距离
controls.enableZoom = true; // 启用缩放


4.3 鼠标交互

javascript
// 射线检测实现点击交互
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();

window.addEventListener(‘mousemove’, (event) => {
mouse.x = (event.clientX / window.innerWidth) * 2 – 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
});

window.addEventListener(‘click’, (event) => {
raycaster.setFromCamera(mouse, camera);

const intersects = raycaster.intersectObjects([earth]);

if (intersects.length > 0) {
console.log(‘点击了地球:’, intersects[0].point);
}
});


4.4 轨道动画

javascript
// 创建卫星
function createSatellite() {
const geometry = new THREE.BoxGeometry(0.3, 0.3, 0.5);
const material = new THREE.MeshPhongMaterial({ color: 0x00ff00 });
const satellite = new THREE.Mesh(geometry, material);

// 创建轨道组
satellite.position.x = 8;

return satellite;
}

const satellite = createSatellite();
earthGroup.add(satellite);

// 卫星动画
function animateSatellite() {
const time = Date.now() * 0.001;
const radius = 8;

satellite.position.x = radius * Math.cos(time);
satellite.position.z = radius * Math.sin(time);
satellite.lookAt(earthGroup.position);
}

function animate() {
requestAnimationFrame(animate);

animateEarth();
animateSatellite();

renderer.render(scene, camera);
}


第五章:性能优化技巧

5.1 几何体优化

javascript
// 使用 LOD(多细节层次)
const lod = new THREE.LevelOfDetail(0);

// 近距离:高精度模型
const highPoly = new THREE.Mesh(
new THREE.SphereGeometry(5, 128, 128),
earthMaterial
);

// 中距离:中等精度
const mediumPoly = new THREE.Mesh(
new THREE.SphereGeometry(5, 64, 64),
earthMaterial
);

// 远距离:低精度
const lowPoly = new THREE.Mesh(
new THREE.SphereGeometry(5, 32, 32),
earthMaterial
);

lod.addLevel(highPoly, 0);
lod.addLevel(mediumPoly, 10);
lod.addLevel(lowPoly, 50);
scene.add(lod);

// 使用 InstancedMesh 批量渲染
const count = 1000;
const geometry = new THREE.BoxGeometry(0.1, 0.1, 0.1);
const material = new THREE.MeshPhongMaterial({ color: 0x00ff00 });

const mesh = new THREE.InstancedMesh(geometry, material, count);

const dummy = new THREE.Object3D();
for (let i = 0; i < count; i++) { dummy.position.set( (Math.random() - 0.5) * 100, (Math.random() - 0.5) * 100, (Math.random() - 0.5) * 100 ); dummy.updateMatrix(); mesh.setMatrixAt(i, dummy.matrix); } scene.add(mesh);


5.2 纹理优化

javascript
// 压缩纹理
const textureLoader = new THREE.TextureLoader();

// 使用压缩格式
const compressedTexture = textureLoader.load(
‘earth_texture.png’,
(texture) => {
texture.minFilter = THREE.LinearFilter;
texture.magFilter = THREE.LinearFilter;
texture.wrapS = THREE.ClampToEdgeWrapping;
texture.wrapT = THREE.ClampToEdgeWrapping;
}
);

// 使用 mipmap
compressedTexture.generateMipmaps = true;

// 减少纹理分辨率
const lowResTexture = new THREE.TextureLoader().load(
‘earth_lowres.jpg’,
null,
null,
(err) => {
console.log(‘纹理加载错误:’, err);
}
);


5.3 渲染优化

javascript
// 控制渲染频率
let lastTime = 0;
const maxFPS = 60;
const interval = 1000 / maxFPS;

function animate(currentTime) {
requestAnimationFrame(animate);

const deltaTime = currentTime – lastTime;

if (deltaTime >= interval) {
// 更新逻辑
animateEarth();
animateSatellite();

// 渲染
renderer.render(scene, camera);

lastTime = currentTime;
}
}

// 使用 renderer.autoClear
renderer.autoClear = false;

// 清除特定区域
function render() {
renderer.render(scene, camera);
}


5.4 内存优化

javascript
// 移除不需要的对象
function cleanup() {
// 移除几何体
geometry.dispose();

// 移除材质
material.dispose();

// 移除纹理
texture.dispose();

// 移除场景
scene.clear();

// 销毁 WebGL 上下文
renderer.dispose();
}

// 定期检查内存使用
function checkMemoryUsage() {
if (performance.memory) {
const usedHeap = performance.memory.usedJSHeapSize;
const totalHeap = performance.memory.totalJSHeapSize;

console.log(`
已使用内存:${(usedHeap / 1024 / 1024).toFixed(2)} MB
总内存:${(totalHeap / 1024 / 1024).toFixed(2)} MB
使用率:${(usedHeap / totalHeap * 100).toFixed(2)}%
`);
}
}


第六章:完整项目实战

6.1 完整地球项目结构

project/
├── index.html
├── js/
│ ├── main.js
│ ├── earth.js
│ ├── controls.js
│ └── utils.js
├── textures/
│ ├── earth_color.jpg
│ ├── earth_normal.jpg
│ ├── earth_specular.jpg
│ ├── clouds.png
│ └── starfield.jpg
└── styles/
└── main.css


6.2 完整代码示例

html




3D 地球


3D 地球

拖动旋转 | 滚轮缩放 | 右键平移





“`

总结:Three.js 无限可能

Three.js 让你能够轻松创建令人惊叹的 3D 体验:

核心优势:

  1. 简单易用:学习曲线平缓
  2. 功能强大:支持各种 3D 功能
  3. 性能优秀:利用 WebGL 硬件加速
  4. 社区活跃:丰富的资源和插件
  5. 性能优化要点:

    • ✅ 使用 LOD 技术
    • ✅ 优化几何体复杂度
    • ✅ 压缩纹理资源
    • ✅ 控制渲染频率
    • ✅ 合理管理内存

    学习路径:

    1. 掌握 Three.js 基础概念
    2. 学习几何体和材质
    3. 了解光照和阴影
    4. 掌握动画和交互
    5. 学习性能优化
    6. 开始你的 3D Web 开发之旅,创造令人惊叹的视觉效果!🚀

      参考资源:

      • [Three.js 官方文档](https://threejs.org/docs/)
      • [Three.js Examples](https://threejs.org/examples/)
      • [Three.js GitHub](https://github.com/mrdoob/three.js)
      • [Three.js Fundamentals](https://threejsfundamentals.org/)

标签

发表评论