概述
我们采用下载threejs源码包的方式本地离线创建一个threejs项目。
本笔记对应的threejs版本为r172
,由于threejs版本更迭较快,所以请采用指定版本。
知识储备
1.importmap类型脚本
在现代前端开发中,importmap 是一种用于管理 JavaScript 模块导入的新特性,它允许开发者在浏览器中直接映射模块的名称到具体的 URL,而无需依赖打包工具(如 Webpack 或 Rollup)。这对于使用 ES 模块(ESM)以及加载第三方库(比如 Three.js)非常有用,尤其是在原生浏览器环境中。
1.1 什么是importmap
importmap
是一种通过 <script type="importmap">
标签定义的 JSON 对象,用于告诉浏览器如何解析 ES 模块的导入路径。它可以映射模块的名称(或路径)到实际的 URL。
基本语法如下:
<script type="importmap">
{
"imports": {
"模块名": "模块URL"
}
}
</script>
模块名:你在代码中 import 时使用的名称(例如 three)。
模块URL:模块的具体地址,通常是一个 CDN 链接或本地文件路径。
1.2 实际案例
我们拿threejs官网的案例来举例
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.172.0/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.172.0/examples/jsm/"
}
}
-
“three”:映射 Three.js 的核心模块,指向 three.module.js,这是 ES 模块格式的核心文件。
-
“three/addons/”:映射 Three.js 的附加模块(如控制器、加载器等),这些模块位于 examples/jsm/ 目录下。通过这种方式,可以直接导入 OrbitControls 等模块。
在使用时我们需要配合type="module"
的脚本,例如:
<!-- 使用 type="module" 的脚本 -->
<script type="module">
// 导入 Three.js 核心模块
import * as THREE from 'three';
// 加载控制器(例如 OrbitControls)
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
</script>
<script type="module">
表明这是一个 ES 模块脚本,可以使用 import 语法。
源码下载
我们可以前往如下地址获取最新的threejs
源码。
网页:https://threejs.org/
github地址:https://github.com/mrdoob/three.js/
1.网页获取
点击download
获取源码压缩包,压缩包名为three.js-master.zip
,与github最新发行版的内容一致。
注意事项:网页版下载的源码包时最新的发行版,如果要使用特定版本的threejs需要前往github下载。
2.github下载
访问github,从如下位置访问各发行版
您可以下载最新的发行版,也可以寻找特定版本下载,找到相应版本号,下载Assets
中的压缩包,两个压缩包内容一致,只是压缩方式不同,根据自身系统选择一个即可。
源码使用
解压源码,得到如下结构:
解压得到build
目录,我们需要使用的就是其中的three.module.js
文件。
注意:
three.module.js
依赖three.core.js
,所以至少要这两个文件。当然也可以直接访问整个
build
目录。
html使用源码
我们先用importmap
引入需要用到的源码,例如
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>第一个场景</title>
<script type="importmap">
{
"imports": {
"three": "../../build/three.module.js"
}
}
</script>
</head>
<body>
</body>
</html>
引入路径参考您自己的文件布局。
然后设置js
脚本,用于构建threejs项目代码,注意:
1.项目脚本的引入要在包文件之后,因为html
中js按顺序执行。
2.项目脚本要使用module
类型,这样才可以使用import
语法。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>第一个场景</title>
<script type="importmap">
{
"imports": {
"three": "../../build/three.module.js"
}
}
</script>
</head>
<body>
<!-- 注意引用顺序 -->
<!-- 本版本基于threejs 174-->
<script type="module" src="./demo.js"></script>
</body>
</html>
简易threejs工程
概述
我们的目标是绘制一个正方体,首先来了解一下threejs工程的四要素:
-
Scene(场景)
场景是 Three.js 中的一个容器,用于存放所有 3D 对象(例如几何体、灯光、相机等)。它就像一个虚拟的“世界”,定义了所有对象的空间。
所有需要在屏幕上渲染的对象(如网格、灯光)都需要添加到场景中。
可以类比于电影拍摄的场地。
-
Objects(物体)
是我们要放入场景中的内容,例如几何体、外部导入的模型等等。
可以类比于电影的演员和道具。
-
Camera(相机)
相机定义了观察场景的视角和范围,决定了从哪个角度、以什么方式看到场景。
-
Renderer(渲染器)
渲染器负责将场景和相机中的内容绘制到屏幕上。每一次渲染(render)相当于用相机给场景拍一次照。
建议配合threejs官网的文档来学习,可以看具体的方法和API。
https://threejs.org/docs/index.html#manual/zh/introduction/Creating-a-scene
1.创建一个场景
//引入three实例
import * as THREE from "three";
//0.确定要渲染的宽高
const sizes = {
height: 400,
width: 800,
};
//1.创建一个场景
const scene = new THREE.Scene();
有了场景,我们就有了场地,下面就可以布置物体、相机等等。
2.创建一个网格
网格(Mesh)是3D建模中常用的概念,实际就是是一个具体的 3D 对象,由几何体(Geometry)和材质(Material)组成。几何体定义了形状,材质定义了外观。
几何体(Geometry)
几何体定义了一个 3D 对象的形状和结构,由顶点(vertices)和面(faces)组成。
Three.js 提供了很多内置的几何体,比如立方体(BoxGeometry)、球体(SphereGeometry)、圆柱体(CylinderGeometry)等。你也可以自定义几何体。
我们使用BoxGeometry
来绘制我们的正方体,传入宽、高、深。
//2.1 物体形状--几何体
const geometry = new THREE.BoxGeometry(1, 1, 1);
材质(Material)
材质定义了对象的外观,比如颜色、光泽、纹理等。
材质决定了一个几何体在渲染时的视觉效果。Three.js 提供了多种材质,例如:
- MeshBasicMaterial:基础材质,不受光照影响。
- MeshLambertMaterial:支持漫反射光照。
- MeshPhongMaterial:支持高光和镜面反射。
我们想创建一个红色立方体,
//2.2 物体样式 -- 材质
const material = new THREE.MeshBasicMaterial({ color: "blue" });
生成网格对象
我们需要依次将几何体和材质添加到网格对象中。
//2.3 用网格模型生成物体
const cube = new THREE.Mesh(geometry, material);
将物体加入场景
为了能让物体在场景中出现,我们需要将物体放入场景中。就好比演员和道具拍电影,你总得让他们加入电影场景才行。
//2.4 将物体放入场景
scene.add(cube);
目前的完整代码
//引入three实例
import * as THREE from "three";
//0.确定要渲染的宽高
const sizes = {
height: 400,
width: 800,
};
//1.创建一个场景
const scene = new THREE.Scene();
//2.创建物体
//2.1 物体形状--几何体
const geometry = new THREE.BoxGeometry(1, 1, 1);
//2.2 物体样式 -- 材质
const material = new THREE.MeshBasicMaterial({ color: "blue" });
//2.3 用网格模型生成物体
const cube = new THREE.Mesh(geometry, material);
//2.4 将物体放入场景
scene.add(cube);
3.架设摄像机
准备好了演员和道具,我们需要把摄像机也加入场景。
three.js 里有几种不同的相机,在这里,我们使用的是 PerspectiveCamera(透视摄像机)。
第一个参数是视野角度(FOV)。视野角度就是无论在什么时候,你所能在显示器上看到的场景的范围,它的单位是角度(与弧度区分开)。
第二个参数是长宽比(aspect ratio)。 也就是你用一个物体的宽除以它的高的值。比如说,当你在一个宽屏电视上播放老电影时,可以看到图像仿佛是被压扁的。
//3.创建相机
//3.1 依次传入垂直视野范围
//3.2 渲染区域的宽/长比,确定视角范围
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height);
注意相机位置
默认物体位置是(0,0,0)
,相机初始位置也是(0,0,0)
,所以相机实际是被物体挡住了的,甚至在物体里面。
这样相机是拍不到东西的,所以需要跳整二者位置。
当你确定一切添加无误,但是只能看到黑屏却看不到物体时,就可能是相机被遮住了,或者物体不在相机内。
摄像机就好比人眼,你是看不到你身后的事物的。
//3.3 设置相机位置,因为相机默认位置也在(0,0,0),与物体默认位置重叠,我们将看不到物体
camera.position.z = 2;
camera.position.x = 1;
camera.position.y = 0;
将相机放入场景中
//3.4 把相机添加到场景
scene.add(camera);
此时完整代码
//引入three实例
import * as THREE from "three";
//0.确定要渲染的宽高
const sizes = {
height: 400,
width: 800,
};
//1.创建一个场景
const scene = new THREE.Scene();
//2.创建物体
//2.1 物体形状--几何体
const geometry = new THREE.BoxGeometry(1, 1, 1);
//2.2 物体样式 -- 材质
const material = new THREE.MeshBasicMaterial({ color: "blue" });
//2.3 用网格模型生成物体
const cube = new THREE.Mesh(geometry, material);
//2.4 将物体放入场景
scene.add(cube);
//3.创建相机
//3.1 依次传入垂直视野范围
//3.2 渲染区域的宽/长比,确定视角范围
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height);
//3.3 设置相机位置,因为相机默认位置也在(0,0,0),与物体默认位置重叠,我们将看不到物体
camera.position.z = 2;
camera.position.x = 1;
//3.4 把相机添加到场景
scene.add(camera);
4.渲染器渲染
渲染器渲染就好比拍照和放映电影。我们需要设置渲染的位置,这里有两种处理方式:
-
1.在html中设置canvas,然后将渲染器于canvas绑定,例如:
<canvas id="first"></canvas>
渲染器绑定
const renderer = new THREE.WebGLRenderer({ //4.1 绑定要渲染的画布 canvas: document.getElementById("first"), //4.2 确定渲染画布的大小 sizes: sizes, });
-
第二种是将渲染好的场景直接添加到DOM中
const renderer = new THREE.WebGLRenderer(); renderer.setSize( window.innerWidth, window.innerHeight ); //将渲染器添加到DOM中 //具体DOM节点可以自己设置 document.body.appendChild( renderer.domElement );
执行渲染
设置好渲染器后,我们就可以传入场景和相机进行拍照渲染了。
//4.3 将场景和摄像机放入渲染程序
// 每一次渲染就相当于用该相机给场景拍张照片
renderer.render(scene, camera);
5.最终完整代码
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>第一个场景</title>
<script type="importmap">
{
"imports": {
"three": "../../build/three.module.js"
}
}
</script>
</head>
<body>
<!-- 用于呈现渲染结果,我们无需设定宽高,会被内容自动撑开 -->
<canvas id="first"></canvas>
<!-- 注意引用顺序 -->
<!-- 本版本基于threejs 174 -->
<script type="module" src="./demo.js"></script>
</body>
</html>
demo.js
//引入three实例
import * as THREE from "three";
//0.确定要渲染的宽高
const sizes = {
height: 400,
width: 800,
};
//1.创建一个场景
const scene = new THREE.Scene();
//2.创建物体
//2.1 物体形状--几何体
const geometry = new THREE.BoxGeometry(1, 1, 1);
//2.2 物体样式 -- 材质
const material = new THREE.MeshBasicMaterial({ color: "blue" });
//2.3 用网格模型生成物体
const cube = new THREE.Mesh(geometry, material);
//2.4 将物体放入场景
scene.add(cube);
//3.创建相机
//3.1 依次传入垂直视野范围
//3.2 渲染区域的宽/长比,确定视角范围
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height);
//3.3 设置相机位置,因为相机默认位置也在(0,0,0),与物体默认位置重叠,我们将看不到物体
camera.position.z = 2;
camera.position.x = 1;
camera.position.y = 0;
//3.4 把相机添加到场景
scene.add(camera);
//4.添加渲染器,
const renderer = new THREE.WebGLRenderer({
//4.1 绑定要渲染的画布
canvas: document.getElementById("first"),
//4.2 确定渲染画布的大小
sizes: sizes,
});
//4.3 将场景和摄像机放入渲染程序
// 每一次渲染就相当于用该相机给场景拍张照片
renderer.render(scene, camera);
最终成品