Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >用 Three.js 画一个哆啦A梦的时光机

用 Three.js 画一个哆啦A梦的时光机

作者头像
神说要有光zxg
发布于 2023-08-28 11:37:06
发布于 2023-08-28 11:37:06
57301
代码可运行
举报
运行总次数:1
代码可运行

想必大家都看过哆啦A梦,时光机是里面的常用道具。

那坐时光机是什么样的体验呢?

我用 Three.js 写了一下,应该是这种感觉:

我们一起来实现一下。

首先,我们过一下 Three.js 的基础:

在二维屏幕上渲染三维物体,得有个坐标轴。

在 three.js 里以向右的方向为 x 轴,向上的方向为 y 轴,向前的方向为 z 轴:

然后管理在三维坐标系里的物体得有个对象体系。

Three.js 的对象体系是这样的:

image.png

所有三维场景中的东西都加到 scene 里来管理。

三维世界本来是黑的,有了 light 之后才能看到东西,有点光源、环境光等不同的光源。

三维世界中的物体,可以从不同角度去观察,改变位置就可以看到不同的风景,这就是相机 camera 的事情。

三维世界中的物体叫做 mesh,任何一个物体都有一个形状,比如圆柱、立方体等,也就是 geometry,然后还得有材质 material,比如金属材质可以反光、普通材质不能。材质可以指定颜色、还可以指定图片作为纹理 texture。

场景中的所有物体,会由渲染器 WebGLRenderer 渲染出来。

场景、物体、灯光、相机、渲染器,这就是 three.js 的核心概念。

每一个物体都可以设置位置 position、缩放 scale、旋转 rotation。

每一帧渲染的时候,改变物体的位置、颜色、旋转角度等就可以实现动画效果了。

大家想一下,时空隧道用什么几何体比较合适呢?

很明显,是圆柱,也就是 CylinderGeometry

在 three.js 文档中可以看到预览大概是这样样子:

示例代码是这样的:

创建一个圆柱几何体 CylinderGeometry ,传入上圆半径、下圆半径,高度,分段数量(分的多了就是圆了)。

指定材质,用 MeshBasicMaterial,指定蓝色。

然后就可以创建一个物体 Mesh,把它加到场景 scene 里。

我们可以创建一个圆柱,内部贴上图,然后相机放在圆柱内部,是不是看到的就是一个隧道了?

圆柱体的材质我们用纹理贴图,比如这种:

这个纹理是可以设置重复 repeat 和偏移 offset 的。

用 TextureLoader 加载图片作为纹理,设置 wrapS 为 repeat,也就是水平重复、wrapT 为竖直重复。

T 是 vertical 的缩写,而 S 就是 horizontal 了。

然后 repeat.set(4, 4) 每个单位内水平方向重复 4 次、竖直方向重复 4 次。

这样就完成了纹理贴图。

然后每一帧渲染的时候,让纹理的 offset 不断增加或减少,再让圆柱不断旋转,不就实现了时空隧道效果么?

我们来写下试试:

先写个 html,引入 three.js

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>时光机</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
    </style>
    <script src="https://www.unpkg.com/three@0.154.0/build/three.js"></script>
</head>
<body>
<script>
 
</script>
</body>
</html>

我们先创建场景 scene 和 renderer:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const width = window.innerWidth;
const height = window.innerHeight;

const scene = new THREE.Scene();
const renderer = new THREE.WebGLRenderer();

renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);

调整画布大小为窗口宽高。

然后创建相机:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);

camera.position.set(0,0, 500);
camera.lookAt(scene.position);

这里创建的是透视相机,也就是近大远小那种。

它有 4 个参数:

从相机往前看,会有个角度 fov,这是第一个参数。

然后视野范围的矩形会有个宽高比 aspect,这是第二个参数。

视野范围会形成一个椎体,叫做视椎体,三四个参数是指定视椎体的范围,从哪里看到哪里。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);

所以 PerspectiveCamera 的这 4个参数分别制定了 45 度的观察角度,宽高比和窗口宽高比一样。视野范围为 0.1 到 10000。

然后就可以一帧帧渲染了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function render() {
    renderer.render(scene, camera);
    requestAnimationFrame(render);
}

render();

只不过现在还没有物体,我们添加个立方体 BoxGeometry 试试:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function create() {
    const geometry = new THREE.BoxGeometry( 100, 100, 100 ); 
    const material = new THREE.MeshBasicMaterial( {color: 0x00ff00} ); 
    const cube = new THREE.Mesh( geometry, material ); 
    cube.rotation.y = 0.5;
    cube.rotation.x = 0.5;
    scene.add( cube );
}
create();

添加一个 Mesh,用 BoxGeometry 创建立方体,长宽高为 100,用 MeshBasicMaterial 指定材质,颜色为绿色。

让这个 mesh 绕 y 和 x 旋转 0.5 的角度。

渲染出来的是这样的:

确实是个立方体,只不过没有明暗变化。

我们加个点光源就好了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const pointLight = new THREE.PointLight( 0xffffff );
pointLight.position.set(0, 0, 500);
scene.add(pointLight);

创建白色的灯光,放在和相机同一个位置,来照向场景中心的位置。

但是你刷新页面会发现没有变化,因为 MeshBasicMaterial 的材质是不反光的。

换成 MeshPhongMaterial 的材质,这种材质有金属质感,会反光。

就这样了:

然后我们来创建圆柱体:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let tunnel;
function create() {
    const geometry = new THREE.CylinderGeometry( 30, 50, 100, 32, 32, true);
    const material = new THREE.MeshPhongMaterial({ 
            color: 0x0000ff
    });
    tunnel = new THREE.Mesh(geometry, material);
    scene.add(tunnel);
}

上圆半径 30、下圆半径 50,高 100,分 32 段(差不多就是圆了)。

用 MeshPhongMaterial 指定金属材质为 蓝色。

渲染出来的是这样的:

我们让它绕 z 轴旋转下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function render() {
    renderer.render(scene, camera);

    tunnel.rotation.z = tunnel.rotation.z + 0.01;
    requestAnimationFrame(render);
}

很明显,我们观察的方向不对,应该是看到下圆的底才对。所以让它绕 x 轴逆时针旋转 90 度。

然后去掉底,这个是在创建圆柱体的时候指定:

再让圆柱绕 x 轴旋转下看看:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
tunnel.rotation.x = tunnel.rotation.x + 0.01;

确实,没有底了。

然后我们来处理下贴图。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let tunnel;
function create() {
    const textureLoader = new THREE.TextureLoader();
    textureLoader.load('./storm.jpg', function(texture) {
        texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
        texture.repeat.set( 1, 2 );

        const geometry = new THREE.CylinderGeometry( 30, 50, 100, 32, 32, true);
        const material = new THREE.MeshPhongMaterial({ 
                map: texture,
                side: THREE.DoubleSide
        });
        tunnel = new THREE.Mesh(geometry, material);

        tunnel.rotation.x = -Math.PI / 2;

        scene.add(tunnel);
    });
}

用 TextureLoader 加载纹理图片,设置水平和竖直方向重复。

然后把这个 texture 设置为纹理。

就是这样的:

把圆柱体高度改为 1000。

看到的就像是一个隧道了:

让 tunnel 绕 y 轴转起来:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function render() {
    renderer.render(scene, camera);

    if(tunnel) {
        tunnel.rotation.y += 0.01;
    }
    requestAnimationFrame(render);
}

我们不是在 z 轴的方向看向中心点么?为什么不是绕 z 轴转?

因为这个圆柱已经绕 x 轴顺时针转了 90 度,所以是绕 y 轴转,看到的是绕 z 轴转的效果。

然后我们再让纹理的 offset 也动起来:

就有穿梭隧道的感觉了:

不过如果我们想实现变色,最好不要直接把贴图作为纹理,而是用它来做透明通道,也就是这样:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const material = new THREE.MeshPhongMaterial({ 
    transparent: true,
    alphaMap: texture,
    side: THREE.BackSide
});

设置透明,然后图片作为透明通道,就会根据不同像素的颜色来设置不同的透明度。

也就是这样的效果:

然后只要设置不同的颜色,并且不断地变色就好了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let H = 0;
function render() {
    renderer.render(scene, camera);

    H += 0.001;
    if (H > 1) { H = 0; }

    if(tunnel) {
        tunnel.material.color.setHSL(H, 0.5, 0.5);

        tunnel.rotation.y += 0.01;
        stormTexture.offset.y += 0.01;
    }
    requestAnimationFrame(render);
}

我们用 HSL 颜色标识法,也就是色相、饱和度、明度。

色相是从 0 到 1 的数值,我们在每一帧改变色相的值。

效果是这样的:

隧道完成了,我们再加个时光机的底座。

这个比较简单,就是一个立方体,调整下位置:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const geometry = new THREE.BoxGeometry( 30, 2, 30 ); 

const material = new THREE.MeshPhongMaterial( {
    color: 0x0000ff
});

const cube = new THREE.Mesh( geometry, material ); 
cube.position.z = 460;
cube.position.y = -20;
scene.add( cube );

相机在 0, 0, 500,那底座就在 460 就好了,再沿着 y 轴往下一点。

这个颜色不大好,我们还是换成贴图。

找个金属的纹理图片,比如这个:

用 TextureLoader 把纹理图片加载进来,设置水平、竖直的重复。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
textureLoader.load('./metal.png', function(texture) {
    texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
    texture.repeat.set( 10, 10 );

    const geometry = new THREE.BoxGeometry( 30, 2, 30 ); 

    const material = new THREE.MeshPhongMaterial( {
        map: texture
    });

    const cube = new THREE.Mesh( geometry, material ); 
    cube.position.z = 460;
    cube.position.y = -20;
    scene.add( cube );
}); 

这样,我们的时光机效果完成了!

全部代码上传了 github:https://github.com/QuarkGluonPlasma/threejs-exercize

不到 100 行:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>时光机</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
    </style>
    <script src="https://www.unpkg.com/three@0.154.0/build/three.js"></script>
</head>
<body>
<script>
    const width = window.innerWidth;
    const height = window.innerHeight;
    const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);

    const scene = new THREE.Scene();
    const renderer = new THREE.WebGLRenderer();

    renderer.setSize(width, height);
    camera.position.set(0, 0, 500);
    camera.lookAt(scene.position);

    const pointLight = new THREE.PointLight( 0xffffff );
    pointLight.position.set(0, 0, 500);
    scene.add(pointLight);

    document.body.appendChild(renderer.domElement)

    let tunnel;
    let stormTexture;
    function create() {
        const textureLoader = new THREE.TextureLoader();
        textureLoader.load('storm.jpg', function(texture) {
            stormTexture = texture;

            texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
            texture.repeat.set( 1, 2 );

            const geometry = new THREE.CylinderGeometry( 30, 50, 1000, 32, 32, true);
            const material = new THREE.MeshPhongMaterial({ 
                transparent: true,
                alphaMap: texture,
                side: THREE.BackSide
            });
            tunnel = new THREE.Mesh(geometry, material);

            tunnel.rotation.x = -Math.PI / 2;

            scene.add(tunnel);
        });

        textureLoader.load('metal.png', function(texture) {
            texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
            texture.repeat.set( 10, 10 );

            const geometry = new THREE.BoxGeometry( 30, 2, 30 ); 

            const material = new THREE.MeshPhongMaterial( {
                map: texture
            });

            const cube = new THREE.Mesh( geometry, material ); 
            cube.position.z = 460;
            cube.position.y = -20;
            scene.add( cube );
        }); 
    }

    let H = 0;
    function render() {
        renderer.render(scene, camera);

        H += 0.002;
        if (H > 1) { H = 0; }

        if(tunnel) {
            tunnel.material.color.setHSL(H, 0.5, 0.5);

            tunnel.rotation.y += 0.01;
            stormTexture.offset.y += 0.01;
        }
        requestAnimationFrame(render);
    }

    create();
    render();

</script>
</body>
</html>

总结

今天我们用 Three.js 实现了时光机的效果。

首先,过了下 Three.js 的基础:

  • 向右为 x 轴、向上为 y 轴,向前为 z 轴
  • 场景 scene 管理灯光 light、相机 camera、物体mesh 等,然后通过渲染器 renderer 渲染出来
  • 物体 mesh 由几何体 geometry 和材质 material 构成,material 可以指定 color 或者纹理 texture
  • camera 一般用透视相机,也就是近大远小的效果

实现时空隧道的效果,就是创建了一个圆柱体,贴上纹理图片,然后把相机放到圆柱体内。

每帧渲染不断改变纹理的 offset 和圆柱体的 rotation。

此外,我们不是直接贴的图,而是把它作为透明度通道,这样可以实现变色效果,结合 HSL 改变色相的方式来变色。

最后,还加了一个立方体的几何体作为时光机底座。

用 Three.js 画一个时光机,一起穿越时空隧道吧。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-07-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 神光的编程秘籍 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Three.js 手写跳一跳小游戏(上)
玩家从一个方块跳到下一个方块,如果没跳过去就算失败,跳过去了就会再出现下一个方块。
神说要有光zxg
2023/08/28
6450
Three.js 手写跳一跳小游戏(上)
用 Three.js 画个 3D 生日蛋糕送给他(她)
作为整天和 UI 打交道的前端工程师,是否想在他(她)生日的时候用代码送上一份惊喜呢?
神说要有光zxg
2021/11/25
3.6K0
用 Three.js 画个 3D 生日蛋糕送给他(她)
用 Three.js 和 AudioContext 实现音乐频谱的 3D 可视化
最近听了一首很好听的歌《一路生花》,于是就想用 Three.js 做个音乐频谱的可视化,最终效果是这样的:
神说要有光zxg
2021/12/04
2.9K0
用 Three.js 和 AudioContext 实现音乐频谱的 3D 可视化
Three.js深入浅出:2-创建三维场景和物体
以上demo总结来说,使用了 Three.js 库创建了一个简单的绿色立方体模型,并实现了旋转动画效果。 总结一下它的步骤:
用户6297767
2023/11/21
8340
Three.js深入浅出:2-创建三维场景和物体
three.js中的重要基础概念
Three.js 是一个功能强大的 JavaScript 库,用于创建和展示基于 WebGL 的三维图形。在学习使用Three.js来构建3D世界之前,有一些基本概念是需要牢记的,否则,在你绘制3D世界时,思绪会是杂乱无章的:
fastmock
2025/04/26
1050
Three.js可视化企业实战WEBGL网-2024入门指南
Three.js 是一个功能强大的 JavaScript 库,用于在 Web 浏览器中创建和显示动画 3D 图形。它的丰富 API 和模块化设计使得开发者可以轻松构建复杂的 3D 场景和动画效果。本文将详细介绍 Three.js 中的一些重要组件和模块,包括场景、相机、几何体、材质、光源、渲染器和控制器等。
用户11130883
2024/05/31
2960
Three.js贴图技巧:优化性能与效果
在当今数字化的时代,WebGL 技术为开发者们打开了一扇通往交互式 3D 图形世界的大门,而 Three.js 作为JavaScript库中的佼佼者,凭借其简单易用的 API 和丰富的功能,在创建3D场景和交互应用方面得到了广泛应用。在Three.js构建的虚拟世界中,贴图扮演着至关重要的角色。从赋予模型逼真的材质质感,到增强场景的视觉吸引力,贴图的质量直接影响着用户的体验。然而,随着场景复杂度的增加和贴图数量的增长,如何在保证贴图效果的同时优化性能,成为了开发者们必须面对的重要课题。本文将深入探讨Three.js贴图的相关知识,分享一系列优化性能与效果的实用技巧,并通过丰富的代码示例进行详细说明,帮助读者更好地理解和应用这些技巧,从而提升Three.js项目的品质。
Front_Yue
2025/03/11
2800
Three.js贴图技巧:优化性能与效果
Three.js 基础纹理贴图
尽管 Three.js 文档已经比较详细了,但对于刚接触 Three.js 的工友来说,最麻烦的还是不懂如何组合。Three.js 的功能实在太多了,初学者很容易被大量的新概念冲晕。
德育处主任
2023/02/26
5.8K0
Three.js 基础纹理贴图
Three.js 场景创建基础
在当今数字化的时代,Web 开发已经不再局限于传统的二维界面。随着技术的不断进步,越来越多的开发者开始探索将 3D 图形引入到网页中,为用户带来更加沉浸式和交互性的体验。而 Three.js 作为一款广受欢迎的 JavaScript 3D 库,为开发者提供了强大而便捷的工具,使得在网页上创建和展示复杂的 3D 场景变得不再困难。本文将深入探讨 Three.js 场景创建的基础知识,包括其简介、引入方式,以及场景构建的关键要素。
Front_Yue
2025/03/12
2370
Three.js 场景创建基础
【愚公系列】2022年09月 微信小程序-three.js绘制球体
Three.js 是一款运行在浏览器中的 3D 引擎,你可以用它创建各种三维场景,包括了摄影机、光影、材质等各种对象。
愚公搬代码
2022/09/28
1.1K0
【愚公系列】2022年09月 微信小程序-three.js绘制球体
使用Three.Js制作3D相册
ThreeJS是一个用JavaScript写的开源3D图形库,它有个简单但是功能强大的3D渲染引擎,可以在网页浏览器里快速创建和展示3D图形。ThreeJS是一个功能强大、使用简单的3D图形库,提供了一个强大的3D渲染工具,大大降低了创建3D应用程序的难度。
查拉图斯特拉说
2024/04/26
4920
使用Three.Js制作3D相册
你的登录界面不够花里胡哨,3D 版本的来了
今天分享一篇用three.js 做的登录网站,里面还用到了粒子特效,一个字就是酷炫😎 前言: 该篇文章用到的主要技术:vue3、three.js 我们先看看成品效果: 登录gif 图 座机小图预览: login2.gif 废话不多说,直接进入正题 Three.js的基础知识 想象一下,在一个虚拟的3D世界中都需要什么?「首先,要有一个立体的空间,其次是有光源,最重要的是要有一双眼睛」。下面我们就看看在three.js中如何创建一个3D世界吧! 创建一个场景 设置光源 创建相机,设置相机位置和相
程序员小猿
2021/11/23
1.1K0
Three.js - 走进3D的奇妙世界
随着人们对用户体验越来越重视,Web开发已经不满足于2D效果的实现,而把目标放到了更加炫酷的3D效果上。Three.js是用于实现web端3D效果的JS库,它的出现让3D应用开发更简单,本文将通过Three.js的介绍及示例带我们走进3D的奇妙世界。
宜信技术学院
2019/11/27
10.6K0
我是如何用 Three.js 在三维世界建房子的(详细教程)
这两天用 Three.js 画了一个 3D 的房子,放了一个床进去,可以用鼠标和键盘控制移动,有种 3D 游戏的即视感。
神说要有光zxg
2021/12/10
5.3K0
我是如何用 Three.js 在三维世界建房子的(详细教程)
three.js 初步
我也是个初学者,大家就当这是我的笔记看,如果理解不正确请大家指正。首先推荐大家一个学习网站Tress.js中文网。 首先我们需要先引入tree.js
用户4793865
2023/01/12
5.1K0
用Three.js构建三维世界的房子
最近在学习Three.js,无奈不知道从哪里下手,查阅大部分资料都是先介绍渲染器(Renderer)、场景(Scene)、照相机(Camera),其实这些概念确实需要了解,如果不给你立体模型,你始终是无法理解的。网上看了一个大佬(神说要有光)的教程,感觉算是一只脚已经入了门,接下来我们通过这篇文章,从造物主的视角开始创建一个房子。我们先看下最终效果。
青年码农
2022/12/13
2K0
用Three.js构建三维世界的房子
Threejs 快速入门
现在什么都是3D,看电影3D,打游戏3D,估计3D打车,很快就会面世。那么作为前端开发的标准语言,JS和3D能不能也搞出点大新闻呢?刚好最近在做一个活动时,就遇到了需要播放3D全景视频的需求,顺便就研
周明礼
2017/05/17
10.3K1
Threejs 快速入门
Three.js教程(7):材质
之前说过网格(Mesh) = 几何体(Geometry) + 材质(Material),也就是一个物体是有它的形状和材质来决定。几何体(Geometry)类似于前端的HTML而材质(Material)类似于前端的CSS,今天我们看一下材质相关的内容。
kai666666
2020/10/17
2.8K0
Threejs入门之十一:创建旋转的地球
经过前面几个章节的介绍,我们对Threejs已经有了一个相对深入的了解,下面我们通过Threejs来做一个旋转的地球效果。 1.首先在电脑上创建一个earth文件夹,在earth文件夹中创建images文件夹用于存放图片文件;创建一个js文件夹用于存放JavaScript代码;创建一个css文件用于存放css样式表文件; 2.拷贝资源,将Threejs源码中的three.module.js拷贝到js文件夹,将地图的贴图文件拷贝到images文件夹 3.用vscode打开earth文件夹,在根目录下新建index.html文件,在index.html中引入three.module.js,在index.html中创建一个id为webgl的div
九仞山
2023/04/30
1.7K0
Threejs入门之十一:创建旋转的地球
Three.JS的第一个三弟(3D)案例
Three.js 是基于 WebGL 技术,用于浏览器中开发 3D 交互场景的 JS 引擎。
不惑
2024/04/23
3740
Three.JS的第一个三弟(3D)案例
相关推荐
Three.js 手写跳一跳小游戏(上)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验