three.js建立3D模型展示地球+高亮

你还在为平面而感到没有视觉效果吗?

下面将手把手建立一个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/[email protected]/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/[email protected]/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>
相关推荐
树上有只程序猿19 分钟前
后端思维之高并发处理方案
前端
庸俗今天不摸鱼1 小时前
【万字总结】前端全方位性能优化指南(十)——自适应优化系统、遗传算法调参、Service Worker智能降级方案
前端·性能优化·webassembly
黄毛火烧雪下1 小时前
React Context API 用于在组件树中共享全局状态
前端·javascript·react.js
Apifox1 小时前
如何在 Apifox 中通过 CLI 运行包含云端数据库连接配置的测试场景
前端·后端·程序员
一张假钞1 小时前
Firefox默认在新标签页打开收藏栏链接
前端·firefox
高达可以过山车不行1 小时前
Firefox账号同步书签不一致(火狐浏览器书签同步不一致)
前端·firefox
m0_593758101 小时前
firefox 136.0.4版本离线安装MarkDown插件
前端·firefox
掘金一周1 小时前
金石焕新程 >> 瓜分万元现金大奖征文活动即将回归 | 掘金一周 4.3
前端·人工智能·后端
三翼鸟数字化技术团队2 小时前
Vue自定义指令最佳实践教程
前端·vue.js
Jasmin Tin Wei2 小时前
蓝桥杯 web 学海无涯(axios、ecahrts)版本二
前端·蓝桥杯