使用 Three.js 创建一个简单的3D地球仪

284 阅读4分钟

引言

随着Web技术的发展,创建具有互动性的3D内容变得越来越容易。Three.js是一个非常流行的JavaScript库,它简化了使用WebGL创建3D图形的过程。本文将详细介绍如何利用Three.js创建一个简单的3D地球仪,并解释每一步骤的意义和作用,即使你是前端开发的新手也能轻松上手。

什么是Three.js?

Three.js是一个用于在浏览器中创建和显示动画的3D计算机图形的跨浏览器JavaScript库。它使用HTML5 Canvas、SVG或WebGL来绘制图像。WebGL是基于OpenGL ES的一个API,允许网页通过JavaScript直接访问GPU加速功能,从而实现高性能的2D和3D图形渲染。

准备工作

首先,你需要确保你的项目中引入了Three.js库。你可以通过CDN链接的方式快速集成Three.js到你的HTML文件中,就像这样:

<script src="https://cdn.bootcss.com/three.js/r83/three.min.js"></script>

注意:虽然这里使用的是r83版本,但推荐使用最新版的Three.js以获得最佳性能和最新的特性支持。

步骤1: 设置场景(Scene)

要开始构建3D世界,首先要设置一个场景。场景就像是一个容器,里面可以放置所有你想展示的对象(如地球)。

let scene = new THREE.Scene();

这行代码创建了一个新的场景实例。

步骤2: 配置相机(Camera)

接下来需要添加一个相机,这样才能从特定的角度查看我们的3D对象。我们通常使用THREE.PerspectiveCamera,它模拟了人类眼睛看到的效果。

let camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 2000);
camera.position.z = 500;

这里的参数分别表示视野角度、长宽比、近裁剪面距离和远裁剪面距离。最后设置相机的位置以便能够看到整个地球。

步骤3: 渲染器(Renderer)

为了让浏览器能显示3D内容,我们需要一个渲染器。Three.js提供了THREE.WebGLRenderer,这是最常用的渲染器之一。

let renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

这里设置了抗锯齿选项并调整大小以适应窗口尺寸,然后将其添加到DOM中。

步骤4: 加载纹理并创建地球模型

现在让我们加载一张图片作为地球的表面纹理,并用它创建一个球体。(相关图片也可以自己去网上找,设置要对应的路径就可以)

let loader = new THREE.TextureLoader();
loader.load('https://threejsfundamentals.org/threejs/resources/images/earth-day.jpg', function(texture) {
    let geometry = new THREE.SphereGeometry(200, 20, 20);
    let material = new THREE.MeshBasicMaterial({ map: texture });
    earthMesh = new THREE.Mesh(geometry, material);
    scene.add(earthMesh);
});

这段代码首先创建了一个纹理加载器,接着定义了球体的几何形状和材质,最后将它们组合成一个网格(Mesh),并添加到了场景中。

步骤5: 实现交互与动画

为了让用户可以通过鼠标移动来改变视角,我们监听了鼠标的移动事件,并根据其位置更新相机的位置。

function onDocumentMouseMove(event) {
    mouseX = event.clientX - windowHalfX;
    mouseY = event.clientY - windowHalfY;
}

同时,为了使地球自转,我们在每一帧都稍微旋转地球模型。

if (earthMesh) { // 只有地球存在时才旋转
    earthMesh.rotation.y -= 0.005;
}

步骤6: 渲染循环

最后,为了让这一切动起来,我们需要一个持续不断的渲染循环。通过requestAnimationFrame函数,我们可以不断地重绘画面,从而形成动画效果。

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);

    if (earthMesh) { 
        earthMesh.rotation.y -= 0.005;
    }

    renderer.render(scene, camera);
}

image.png

结论

通过上述步骤,我们已经成功地创建了一个可以交互且带有动画效果的3D地球仪。Three.js的强大之处在于它极大地降低了WebGL编程的复杂度,使得开发者可以专注于创意而非底层细节。希望这篇文章能帮助你入门Three.js,并激发你探索更多3D Web应用的兴趣。无论是游戏开发还是数据可视化,Three.js都能提供强大的支持。继续学习和实践,你会发现自己能够创造出令人惊叹的作品!

完整代码:

<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>3D地球</title>
    <style>
        body { margin: 0; overflow: hidden; }
        canvas { display: block; }
    </style>
</head>
<body>
    <canvas id="webglcanvas"></canvas>
    <script src="https://cdn.bootcss.com/three.js/r83/three.min.js"></script>
    <script>
        let scene, camera, renderer;
        let mouseX = 0, mouseY = 0;
        let windowHalfX = window.innerWidth / 2;
        let windowHalfY = window.innerHeight / 2;
        let earthMesh = null; // 新增一个变量来保存地球对象

        function init() {
            // 创建场景
            scene = new THREE.Scene();
            scene.background = new THREE.Color(0xffffff);

            // 配置相机
            camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 2000);
            camera.position.z = 500;

            // 设置渲染器
            renderer = new THREE.WebGLRenderer({ antialias: true });
            renderer.setSize(window.innerWidth, window.innerHeight);
            document.body.appendChild(renderer.domElement);

            // 加载纹理并创建地球模型
            let loader = new THREE.TextureLoader();
            loader.load('1.png', function(texture) {
                let geometry = new THREE.SphereGeometry(200, 20, 20);
                let material = new THREE.MeshBasicMaterial({ map: texture });
                earthMesh = new THREE.Mesh(geometry, material); // 赋值给全局变量
                scene.add(earthMesh);
            });

            // 监听鼠标移动事件
            document.addEventListener('mousemove', onDocumentMouseMove, false);
            
            // 监听窗口大小调整事件
            window.addEventListener('resize', onWindowResize, false);
        }

        function onDocumentMouseMove(event) {
            mouseX = event.clientX - windowHalfX;
            mouseY = event.clientY - windowHalfY;
        }

        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() {
            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);

            // 地球自转
            if (earthMesh) { 
                earthMesh.rotation.y -= 0.005;
            }

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

        // 初始化并启动动画循环
        init();
        animate();
    </script>
</body>
</html>