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/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>
相关推荐
m0_748247552 小时前
Web 应用项目开发全流程解析与实战经验分享
开发语言·前端·php
m0_748255022 小时前
前端常用算法集合
前端·算法
真的很上进3 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
web130933203983 小时前
vue elementUI form组件动态添加el-form-item并且动态添加rules必填项校验方法
前端·vue.js·elementui
图表制作解说(目标1000个图表)3 小时前
ECharts散点图-气泡图,附视频讲解与代码下载
echarts·统计分析·数据可视化·散点图·大屏可视化
NiNg_1_2343 小时前
Echarts连接数据库,实时绘制图表详解
前端·数据库·echarts
如若1234 小时前
对文件内的文件名生成目录,方便查阅
java·前端·python
滚雪球~4 小时前
npm error code ETIMEDOUT
前端·npm·node.js
沙漏无语4 小时前
npm : 无法加载文件 D:\Nodejs\node_global\npm.ps1,因为在此系统上禁止运行脚本
前端·npm·node.js
supermapsupport4 小时前
iClient3D for Cesium在Vue中快速实现场景卷帘
前端·vue.js·3d·cesium·supermap