three.js指南

threejs 相关资料

  • threejs 官网
  • threejs 案例

安装(Installation)

使用 NPM 和构建工具进行安装

对于大多数用户而已,从 npm 包注册表中心 安装并使用 构建工具 会是一个更推荐的方案。因为项目需要的依赖越多,就越有可能遇到静态托管无法轻易解决的问题。而使用构建工具,导入本地 JavaScript 文件和 npm 软件包将会是开箱即用的,无需导入映射(import maps)。

  1. 安装 Node.js。我们需要它来管理依赖项和运行构建工具。

在项目文件夹中通过 终端 安装 three.js 和构建工具 Vite。Vite 将在开发过程中使用,但不会被打包成为最终网页的一部分。当然,除了 Vite 你也可以使用其他支持导入 ES Modules 的现代构建工具。

  1. # three.js
    npm install --save three

    # vite
    npm install --save-dev vite安装时我的项目中新增了 node_modules/package.json。它们是什么
  2. 在终端运行:npx vitenpx 是什么

three.js与WebGL

Three.js经常会和WebGL混淆, 但也并不总是,three.js其实是使用WebGL来绘制三维效果的。 WebGL是一个只能画点、线和三角形的非常底层的系统. 想要用WebGL来做一些实用的东西通常需要大量的代码, 这就是Three.js的用武之地。它封装了诸如场景、灯光、阴影、材质、贴图、空间运算等一系列功能,让你不必要再从底层WebGL开始写起。

基础语法

这套教程假设你已经了解了JavaScript,且大部分内容会使用 ES6的语法。点击这里查看你需要提前掌握的东西。 大部分支持Three.js的浏览器都会自动更新,所以绝大多数用户应该都能运行本套教程的代码。 如果你想在非常老的浏览器上运行此代码, 你需要一个像Babel一样的语法编译器 。 当然使用非常老的浏览器的用户可能根本不能运行Three.js。

three.js应用结构

人们在学习大多数编程语言的时候第一件事就是让电脑打印个"Hello World!"。 对于三维来说第一件事往往是创建一个三维的立方体。 所以我们从"Hello Cube!"开始。

在我们开始前,让我们试着让你了解一下一个three.js应用的整体结构。一个three.js应用需要创建很多对象,并且将他们关联在一起。下图是一个基础的three.js应用结构。

上图需要注意的事项:

  • 首先有一个渲染器(Renderer)。这可以说是three.js的主要对象。你传入一个场景(Scene)和一个摄像机(Camera)到渲染器(Renderer)中,然后它会将摄像机视椎体中的三维场景渲染成一个二维图片显示在画布上。
  • 其次有一个场景图 它是一个树状结构,由很多对象组成,比如图中包含了一个场景(Scene)对象 ,多个网格(Mesh)对象,光源(Light)对象,群组(Group),三维物体(Object3D),和摄像机(Camera)对象。一个场景(Scene)对象定义了场景图最基本的要素,并包了含背景色和雾等属性。这些对象通过一个层级关系明确的树状结构来展示出各自的位置和方向。子对象的位置和方向总是相对于父对象而言的。比如说汽车的轮子是汽车的子对象,这样移动和定位汽车时就会自动移动轮子。你可以在场景图的这篇文章中了解更多内容。

注意图中摄像机(Camera)是一半在场景图中,一半在场景图外的。这表示在three.js中,摄像机(Camera)和其他对象不同的是,它不一定要在场景图中才能起作用。相同的是,摄像机(Camera)作为其他对象的子对象,同样会继承它父对象的位置和朝向。在场景图这篇文章的结尾部分有放置多个摄像机(Camera)在一个场景中的例子。

  • 网格(Mesh)对象可以理解为用一种特定的材质(Material)来绘制的一个特定的几何体(Geometry)。材质(Material)和几何体(Geometry)可以被多个网格(Mesh)对象使用。比如在不同的位置画两个蓝色立方体,我们会需要两个网格(Mesh)对象来代表每一个立方体的位置和方向。但只需一个几何体(Geometry)来存放立方体的顶点数据,和一种材质(Material)来定义立方体的颜色为蓝色就可以了。两个网格(Mesh)对象都引用了相同的几何体(Geometry)和材质(Material)。
  • 几何体(Geometry)对象顾名思义代表一些几何体,如球体、立方体、平面、狗、猫、人、树、建筑等物体的顶点信息。Three.js内置了许多基本几何体 。你也可以创建自定义几何体或从文件中加载几何体。
  • 材质(Material)对象代表绘制几何体的表面属性,包括使用的颜色,和光亮程度。一个材质(Material)可以引用一个或多个纹理(Texture),这些纹理可以用来,打个比方,将图像包裹到几何体的表面。
  • 纹理(Texture)对象通常表示一幅要么从文件中加载,要么在画布上生成,要么由另一个场景渲染出的图像。
  • 光源(Light)对象代表不同种类的光。

有了以上基本概念,我们接下来就来画个下图所示的"Hello Cube"吧。

立方体案例

引入three.js

首先是加载three.js

<script type="module">
import * as THREE from 'three';
</script>

把type="module"放到script标签中很重要。这可以让我们使用import关键字加载three.js。还有其他的方法可以加载three.js,但是自r106开始,使用模块是最推荐的方式。模块的优点是可以很方便地导入需要的其他模块。这样我们就不用再手动引入它们所依赖的其他文件了。

下一步我们需要一个<canvas>标签。

<body>
  <canvas id="c"></canvas>
</body>

Three.js需要使用这个canvas标签来绘制,所以我们要先获取它然后传给three.js。

<script type="module">
import * as THREE from 'three';
 
function main() {
  const canvas = document.querySelector('#c');
  const renderer = new THREE.WebGLRenderer({antialias: true, canvas});
  ...
</script>

拿到canvas后我们需要创建一个WebGL渲染器(WebGLRenderer)。渲染器负责将你提供的所有数据渲染绘制到canvas上。

注意这里有一些细节。如果你没有给three.js传canvas,three.js会自己创建一个 ,但是你必须手动把它添加到文档中。在哪里添加可能会不一样这取决你怎么使用, 我发现给three.js传一个canvas会更灵活一些。我可以将canvas放到任何地方, 代码都会找到它,假如我有一段代码是将canvas插入到文档中,那么当需求变化时, 我很可能必须去修改这段代码。

创建摄像机

接下来我们需要一个透视摄像机(PerspectiveCamera)。

const fov = 75;
const aspect = 2;  // 相机默认值
const near = 0.1;
const far = 5;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);

fov是视野范围(field of view)的缩写。上述代码中是指垂直方向为75度。 注意three.js中大多数的角用弧度表示,但是因为某些原因透视摄像机使用角度表示。

aspect指画布的宽高比,在默认情况下 画布是300x150像素,所以宽高比为300/150或者说2。

near和far代表近平面和远平面,它们限制了摄像机面朝方向的可绘区域。 任何距离小于或超过这个范围的物体都将被裁剪掉(不绘制)。

这四个参数定义了一个 "视椎(frustum)"视椎(frustum)是指一个像被削去顶部的金字塔形状。换句话说,可以把"视椎(frustum)"想象成其他三维形状如球体、立方体、棱柱体、截椎体。

近平面和远平面的高度由视野范围决定,宽度由视野范围和宽高比决定。

视椎体内部的物体将被绘制,视椎体外的东西将不会被绘制。

摄像机默认指向Z轴负方向,上方向朝向Y轴正方向。我们将会把立方体放置在坐标原点,所以我们需要往后移一下摄像机才能显示出物体。

camera.position.z = 2;

下图是我们想要达到的效果。

我们能看到摄像机的位置在z = 2。它朝向Z轴负方向。我们的视椎体范围从摄像机前方0.1到5。因为这张图是俯视图,视野范围会受到宽高比的影响。画布的宽度是高度的两倍,所以水平视角会比我们设置的垂直视角75度要大。

创建场景

然后我们创建一个场景(Scene)。场景(Scene)是three.js的基本的组成部分。需要three.js绘制的东西都需要加入到scene中。 我们将会在场景是如何工作的一文中详细讨论。

const scene = new THREE.Scene();

创建立方体

然后创建一个包含盒子信息的立方几何体(BoxGeometry)。几乎所有希望在three.js中显示的物体都需要一个包含了组成三维物体的顶点信息的几何体。

const boxWidth = 1;
const boxHeight = 1;
const boxDepth = 1;
const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);

然后创建一个基本的材质并设置它的颜色. 颜色的值可以用css方式和十六进制来表示。

const material = new THREE.MeshBasicMaterial({color: 0x44aa88});

再创建一个网格(Mesh)对象,它包含了:

  1. 几何体(Geometry)(物体的形状)
  2. 材质(Material)(如何绘制物体,光滑还是平整,什么颜色,什么贴图等等)
  3. 对象在场景中相对于他父对象的位置、朝向、和缩放。下面的代码中父对象即为场景对象。
const cube = new THREE.Mesh(geometry, material);

最后我们将网格添加到场景中。

scene.add(cube);

之后将场景和摄像机传递给渲染器来渲染出整个场景。

renderer.render(scene, camera);

这里有一个实例。

点击此处在新标签页中打开

很难看出来这是一个三维的立方体,因为我们直视Z轴的负方向并且立方体和坐标轴是对齐的,所以我们只能看到一个面。

让立方体旋转起来

我们来让立方体旋转起来,以便更好的在三维环境中显示。为了让它动起来我们需要用到一个渲染循环函数 requestAnimationFrame.

代码如下:

function render(time) {
  time *= 0.001;  // 将时间单位变为秒
 
  cube.rotation.x = time;
  cube.rotation.y = time;
 
  renderer.render(scene, camera);
 
  requestAnimationFrame(render);
}
requestAnimationFrame(render);

requestAnimationFrame函数会告诉浏览器你需要显示动画。传入一个函数作为回调函数。本例中的函数是render函数。如果你更新了跟页面显示有关的任何东西,浏览器会调用你传入的函数来重新渲染页面。我们这里是调用three.js的renderer.render函数来绘制我们的场景。

requestAnimationFrame会将页面开始加载到函数运行所经历的时间当作入参传给回调函数,单位是毫秒数。但我觉得用秒会更简单所以我将它转换成了秒。

然后我们把立方体的X轴和Y轴方向的旋转角度设置成这个时间。这些旋转角度是弧度制。一圈的弧度为2Π所以我们的立方体在每个方向旋转一周的时间为6.28秒。

最后渲染我们的场景并调用另一个帧动画函数来继续我们的循环。

回调函数之外在主进程中我们调用一次requestAnimationFrame来开始整个渲染循环。

点击此处在新标签页中打开

添加光照

效果好了一些但还是很难看出是三维的。我们来添加些光照效果,应该会有点帮助。three.js中有很多种类型的灯光,我们将在后期文章中详细讨论。现在我们先创建一盏平行光。

{
  const color = 0xFFFFFF;
  const intensity = 3;
  const light = new THREE.DirectionalLight(color, intensity);
  light.position.set(-1, 2, 4);
  scene.add(light);
}

平行光有一个位置和目标点。默认值都为(0, 0, 0)。我们这里 将灯光的位置设为(-1, 2, 4),让它位于摄像机前面稍微左上方一点的地方。目标点还是(0, 0, 0),让它朝向坐标原点方向。

我们还需要改变下立方体的材质。MeshBasicMaterial材质不会受到灯光的影响。我们将他改成会受灯光影响的MeshPhongMaterial材质。

const material = new THREE.MeshBasicMaterial({color: 0x44aa88});  // 绿蓝色
const material = new THREE.MeshPhongMaterial({color: 0x44aa88});  // 绿蓝色

这是我们新的项目结构

下面开始生效了。

点击此处在新标签页中打开

现在应该可以很清楚的看出是三维立方体了。

多个立方体

我们再添加两个立方体来增添点趣味性。

每个立方体会引用同一个几何体和不同的材质,这样每个立方体将会是不同的颜色。

首先我们创建一个根据指定的颜色生成新材质的函数。它会根据指定的几何体生成对应网格,然后将网格添加进场景并设置其X轴的位置。

function makeInstance(geometry, color, x) {
  const material = new THREE.MeshPhongMaterial({color});

  const cube = new THREE.Mesh(geometry, material);
  scene.add(cube);

  cube.position.x = x;

  return cube;
}

然后我们将用三种不同的颜色和X轴位置调用三次函数,将生成的网格实例存在一个数组中。

const cubes = [
  makeInstance(geometry, 0x44aa88,  0),
  makeInstance(geometry, 0x8844aa, -2),
  makeInstance(geometry, 0xaa8844,  2),
];

最后我们将在渲染函数中旋转三个立方体。我们给每个立方体设置了稍微不同的旋转角度。

function render(time) {
  time *= 0.001;  // 将时间单位变为秒

  cubes.forEach((cube, ndx) => {
    const speed = 1 + ndx * .1;
    const rot = time * speed;
    cube.rotation.x = rot;
    cube.rotation.y = rot;
  });

  ...

这里是结果。

点击此处在新标签页中打开

如果你对比上面的示意图可以看到此效果符合我们的预想。位置为X = -2 和 X = +2的立方体有一部分在我们的视椎体外面。他们大部分是被包裹的,因为水平方向的视角非常大。

我们的项目现在有了这样的结构

正如你看见的那样,我们有三个网格(Mesh)引用了相同的立方几何体(BoxGeometry)。每个网格(Mesh)引用了一个单独的MeshPhongMaterial材质来显示不同的颜色。

希望这个简短的介绍能帮助你起步。接下来我们将介绍如何使我们的代码具有响应性,从而使其能够适应多种情况.

es6模块,three.js,和文件夹结构

从r106版本开始,使用three.js的首选方式是通过es6模块。

在一个脚本中,es6模块可以通过import关键字加载或者通过<script type="module">行内标签。这有一个两种方法都用的例子。

<script type="module">
import * as THREE from 'three';

...

</script>

路径必须是绝对或相对的。相对路径通常由./或者../开头,和其他标签不同如<img>和<a>.

只要它们的绝对路径完全相同,对同一脚本的引用将只被加载一次。对于three.js这意味着它需要你把所有的实例的库放在正确的文件夹结构中。

someFolder
 |
 ├-build
 | |
 | +-three.module.js
 |
 +-examples
   |
   +-jsm
     |
     +-controls
     | |
     | +-OrbitControls.js
     | +-TrackballControls.js
     | +-...
     |
     +-loaders
     | |
     | +-GLTFLoader.js
     | +-...
     |
     ...

之所以需要这种文件夹结构,是因为像OrbitControls.js这样的示例中的脚本有一个复杂的相对路径,像下面这样

import * as THREE from '../../../build/three.module.js';

使用相同的结构保证了当你导入three和任一示例库时,它们都会引用同一个three.module.js文件。

import * as THREE from './someFolder/build/three.module.js';
import {OrbitControls} from './someFolder/addons/controls/OrbitControls.js';

在使用CDN时,是同样的道理。确保three.modules.js的路径以 /build/three.modules.js结尾,比如

import * as THREE from 'https://cdn.jsdelivr.net/npm/three@<version>/build/three.module.js';
import {OrbitControls} from 'https://cdn.jsdelivr.net/npm/three@<version>/addons/controls/OrbitControls.js';

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/689315.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

1.vue2.x-初识及环境搭建

目录 1.下载nodejs v16.x 2.设置淘宝镜像源 3.安装脚手架 4.创建一个项目 5.项目修改 代码地址&#xff1a;source-code: 源码笔记 1.下载nodejs v16.x 下载地址&#xff1a;Node.js — Download Node.js 2.设置淘宝镜像源 npm config set registry https://registry.…

【PyTorch】PyTorch深度学习框架实战(二):torchrun

一、引言 PyTorch由facebook人工智能研究院研发&#xff0c;2017年1月被提出&#xff0c;是一个开源的Python机器学习库&#xff0c;基于Torch&#xff0c;用于自然语言处理等应用程序。PyTorch既可以看作加入了GPU支持的numpy&#xff0c;同时也可以看成一个拥有自动求导功能的…

【iOS】MRC下的单例模式批量创建单例

单例模式的介绍和ARC下的单例请见这篇&#xff1a;【iOS】单例模式 目录 关闭ARC环境MRC下的单例ARC下的单例批量创建单例Demo 关闭ARC环境 首先关闭ARC环境&#xff0c;即打开MRC&#xff1a; 或是指定某特定目标文件为非ARC环境&#xff1a; 双击某个类文件&#xff0c;指定…

python的最小二乘法(OLS)函数

1、作用 pandas提供了一些很方便的功能&#xff0c;比如最小二乘法(OLS)&#xff0c;可以用来计算回归方程式的各个参数。 2、Python导出的OLS模型的结果 下面是如何解读Python导出的OLS模型的结果。 1. 回归系数&#xff1a; 代表每个自变量对因变量的影响程度&#xff0c…

软件质量保障与测试 Lab2

Lab2 12修改代码执行结果问题解决 3修改代码执行结果问题解决 1 klee 对 symbolic.c 生成文件的执行结果&#xff1a; 2 修改代码 头文件引用添加&#xff1a; #include <klee/klee.h>执行部分&#xff1a; 将原先的读入&#xff1a; int main() {maze[y][x] X;re…

Wakeup Source框架设计与实现

Wakeup Source 为系统组件提供了投票机制&#xff0c;以便低功耗子系统判断当前是否可以进入休眠。 Wakeup Source(后简称&#xff1a;WS) 模块可与内核中的其他模块或者上层服务交互&#xff0c;并最终体现在对睡眠锁的控制上。 1. 模块功能说明 WS的处理逻辑基本上是围绕 com…

Python初步使用教程

1.基本输出print函数 a10 b20 print(a)#输出结束后会自动换行 print(b) print(a,b,猪猪侠)#print中sep决定三者之间会存在空格#连接方法一 print(猪猪,end) print(侠) #连接方法二&#xff08;只能是字符串和字符串连&#xff09; print(超级无敌)print(chr(67)) print(ord(猪…

内存经验分享

目录 内存统计工具 /proc/meminfo Buddy ​​​​​​​​​​​​​​Slub ​​​​​​​Procrank /proc/pid/smaps ​​​​​​​Dumpsys meminfo 内存评估 内存泄漏 Lmk 水位调整 内存统计工具 /proc/meminfo 可以提供整体内存信息&#xff0c;各字段表示的意思如…

Ant Design Pro

一&#xff1a;Ant Design pro是什么&#xff1a; Ant Design Pro 是基于 Ant Design 和 umi 的封装的一整套企业级中后台前端/设计解决方案&#xff0c;致力于在设计规范和基础组件的基础上&#xff0c;继续向上构建&#xff0c;提炼出典型模板/业务组件/配套设计资源&#x…

[linux] 上手新ubuntu机器的初始化工作(自用侵删)

文章目录 环境类Vimzshother 应用类Typora激活环境准备解包替换文件app.asar激活Typora VsCodeextension.vscode乱码 WattToolkitQQWPS输入法:FcitxDeepin-wine : Wechat 环境类 Vim 直接贴配置 vim-Plug: let mapleader "," let g:mapleader "," le…

攻防世界---misc---小小的PDF

1、题目描述&#xff0c;下载附件是一个PDF&#xff0c;打开之后是这样&#xff0c;有两页PDF 2、用winhex分析&#xff0c;没有发现奇怪的地方 3、在kali中binwalk发现有多张照片 4、接着使用foremost将图片分离出来&#xff0c; 5、得到3张图片&#xff0c;打开第3张图片&am…

【TB作品】MSP430F5529 单片机,智能温控系统,DS18B20

作品功能 本项目设计并实现了一个基于MSP430单片机的智能温控系统。系统可以实时显示当前温度&#xff0c;并且可以根据设置的临界值对环境进行加热或降温。主要功能如下&#xff1a; 实时显示当前温度。显示并调整温度临界值&#xff0c;临界值可在20~35摄氏度之间调节。当前…

STM32-呼吸灯仿真

目录 前言: 一.呼吸灯 二.跑马灯 三. 总结 前言: 本篇的主要内容是关于STM32-呼吸灯的仿真,包括呼吸灯,跑马灯的实现与完整代码,欢迎大家的点赞,评论和关注. 接上http://t.csdnimg.cn/mvWR4 既然已经点亮了一盏灯,接下来就可以做更多实验了, 一.呼吸灯 在上一个的基础上…

力扣560. 和为 K 的子数组

Problem: 560. 和为 K 的子数组 文章目录 题目描述思路复杂度Code 题目描述 思路 1.初始化一个哈希表preSum&#xff0c;用于记录前缀和及其出现次数,ans记录和为k的子数组数量、sum_i记录当前前缀和&#xff1b; 2.将前缀和为 0 的情况存入哈希表&#xff0c;表示前缀和为 0 出…

C# 绘图及古诗填字

绘图 绘图的结果如下&#xff1a; 绘图部分主要使用了 Bitmap、Graphics 具体的函数是 MakeMap 入参说明 string bg : 背景图 Rectangle rect &#xff1a;绘图区域 int row_count &#xff1a;行数 int col_count &#xff1a;列数 string fn &#xff1a;保存到的文件 …

前端三大件速成 05 javascript(1)js组成、引入、基本语法

文章目录 一、js组成二、js的引入三、基本语法1、变量2、基本规范3、关键字4、数据类型&#xff08;1&#xff09;基本数据类型&#xff08;2&#xff09;引用数据类型&#xff08;3&#xff09;数据类型转换&#xff08;4&#xff09;typeof运算符 5、运算符6、流程控制&#…

数据结构与算法笔记:基础篇 - 散列表(下):为什么散列表和链表经常会一起使用?

概述 已经学习了这么多章节了&#xff0c;你有没有发现&#xff0c;两种数据结构&#xff0c;散列表和链表&#xff0c;经常会被放在一起使用。你还记得&#xff0c;前面的章节中都有哪些地方讲到散列表和链表的组合使用吗&#xff1f; 在链表那一节&#xff0c;我讲到如何用…

MAVEN:自定义模板Archetype的创建

目录 一、简介 二、具体步骤 三、 vscode通过模板创建项目 四、通过IDEA创建 一、简介 有时候MAVEN自带的模板库并不能满足我们创建项目的需求&#xff0c;为了能够快速创建项目&#xff0c;免去每次复杂的配置&#xff0c;所以我们需要自定义模板库&#xff0c;本次操作基于…

nss刷题(4)

1、[SWPUCTF 2021 新生赛]easyrce <?php error_reporting(0); highlight_file(__FILE__); if(isset($_GET[url])) { eval($_GET[url]); } ?> if(isset($_GET[url])) isset函数用来检测url变量是否存在&#xff1b;$_GET函数获取变量数据 eval($_GET[url]); eval函数用…

数据挖掘--数据预处理

数据清理 缺失值 如果数据集含有分类属性&#xff0c;一种简单的填补缺失值的方法为&#xff0c;将属于同一类的对象的该属性值的均值赋此缺失值&#xff1b;对于离散属性或定性属性&#xff0c;用众数代替均值。更复杂的方法&#xff0c;可以将其转换为分类问题或数值预测问…