介绍
现在让我们编码创造我们第一个Three.js场景。
如果你熟悉现在前端工具,像Node.js和Vite,那么你可以跳过本节的大部分内容。
本地服务器和构建工具
为了运行我们之后的Three.js网站,需要运行一个本地服务器。
我们可以创建一个HTML文件在电脑上,双击打开它,但是浏览器会因为安全原因限制网站的功能。做为结果,我们将不能加载Three.js,模型,纹理一系列东西。
最简单的解决方法就是使用构建工具或打包工具。
现在的构建工具
有很多的构建工具可供选择,你可能听说过一些像WePack、Vite、Gulp、Parcel。
他们有各自的的优点和缺点,但是我们接下来会使用指定的构建工具。
如今,最流行的构建工具是WebPack。它被广泛地使用,拥有一个很大的社区,你可以用它来做很多事情。但是尽管WebPack是最流行的,它不是最好用的。
事实上,现在最好用的构建工具是Vite。它安装起来很快,运行也很快,并且更少出现bug。开发者的体验会更好。
Vite
在这里,简单介绍一些关于Vite的内容。
正如之前提到的,Vite是一个构建工具。我们将会编写像 HTML/CSS/JS 的网站代码,Vite会帮我们构建最终的网站。Vite也可以用来做优化,解决缓存实效的问题,源码映射,作为一个本地服务器运行。
Vite除了这些基本功能,我们还可以增加插件来增加更多特性。我们之后会增加插件,支持GLSL文件来构建我们自己的着色器,并且让它运行在React上。
终端
我们会运行一些命令在终端上。我们将会使用Termial(MacOS)或者Command Prompt(Windows),实际上更推荐使用VSCode的内置终端。打开VSCode,键入CMD + J
(MacOS)或者CTRL+J
(Windows)去打开内置终端。
Node.js
为了运行Vite,你需要在你的计算机上安装Node.js。
Node.js可以让你在浏览器外运行JavaScript。这便于运行各种各样的工具,比如Vite。
为了安装Node.js,前往 nodejs.org/en/,下载"LTS"版本,然后以默认设置安装即可。
重新打开你的撞断,然后运行node -v
命令查看版本。
创建一个Node.js项目
现在我们拥有了Node.js,并且知道如何使用终端,让我们创建第一个Node.js项目。
创建一个将要包含你网站的文件夹。
接下来用VSCode打开文件夹。打开VSCode内置终端运行npm init -y
命令。
当我们安装Node.js的时候,我们自动安装了NPM。NPM是一个Node的包依赖管理器,可以用来获取三方库,比如Three.js,Vite。另外,NPM拥有一个命令行接口npm
可以在终端中使用。
通过运行npm init -y
命令,NPM将会创造一个package.json
文件,这个文件包含运行Node.js项目的最少信息。
添加依赖
在本节中,我们将会添加两个依赖。Vite和Three.js。
现在,我们将注意力集中在Vite。为了给Node.js项目添加依赖,在终端中运行npm install vite
。
需要注意三件事情。
node_modules/
文件夹被创建了。这个文件夹包含了项目的依赖,不要轻易修改这个文件夹里面的内容。除了包含vite
依赖在vite/
文件夹下,同样也包含其他被vite
用的依赖。package.json
文件被更新了,现在包含一个依赖数组写在"dependencies"
属性。pacakge-lock.json
文件被创建,并且包含依赖的具体信息和具体版本,这会让让你的项目准确安装依赖版本。
这两个文件使得项目可以被分享。如果你想分享Three.js项目给其他开发者,你将要移除node_modules
文件夹,然后分享剩余的项目。其他开发者会获取你项目,运行命令npm install
,根据package.json
自动创建node_modules
。
基础网站
现在,让我们创建一个基础的网站。
在项目文件夹下,创建index.html
文件。
xml
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>03 - First Three.js Project</title>
</head>
<body>
<h1>Soon to be a Three.js website</h1>
</body>
</html>
不要直接打开这个文件。这样做会违背我们使用Vite的初心。
在package.json
中,用下面的内容替换"scripts"
:
json
{
// ...
"scripts": {
"dev": "vite",
"build": "vite build"
},
// ...
}
这样我们拥有了两个"scripts",现在可以在终端运行dev
和build
命令,这些命令会分别触发vite
和vite build
,通过使用在node_modules/
文件下的vite/
依赖。
为了运行dev
脚本,在终端中,运行npm run dev
。
Vite将会展示一个类似
http://localhost:5173
的URL。复制它并在浏览器中打开,你会看到如下网站。
恭喜🎉,你已经让自己的Vite网站运行起来了。
添加JavaScript
现在我们需要执行一些JavaScript在网站上。
在项目文件夹下,创建script.js
文件:
arduino
console.log('JavaScript is working')
在index.html
,在<body>
的最后添加<Script>
:
xml
<!-- ... -->
<body>
<h1>Soon to be a Three.js website</h1>
<script type="module" src="./script.js"></script>
</body>
</html>
不要忘记了 type="module"
。
打开浏览器中的开发者工具,你将会在控制台中看见JavaScript is working
。

Three.js
就像我们之前添加Vite到项目中,我们需要添加Three.js。
添加依赖
这个依赖的名称在NPM上是three
。
我们需要运行npm install three
,但我们的终端目前正在运行Vite服务器。
我们有两个选项:
- 打开一个新的终端,运行命令。
- 关闭服务器,运行命令,随后重启服务器。
我们将会关闭服务器。
按下CTRL + C
来停止服务器。
现在运行npm install three
:

引入Three.js
回到scirpt.js
,我们按照下面的方式导入three
依赖:
javascript
import * as THREE from 'three'
这样将会从three
依赖导入所有Three.js的核心类在THREE
变量中。
这样导入依赖的方式称之为"ES modules"。
使用Three.js
在我们的script.js
文件,现在可以获取一个变量名城THREE
。
如果你console.log()
这个变量,你会看到里面的很多内容:
javascript
import * as THREE from 'three'
console.log(THREE)
THREE
变量包含大量的类和属性,这些内容会在Three.js项目中被使用。为了使用这些类,你需要实例化它。举个例子,如果你需要创建一个窗景,你需要写下const scene = new THREE.Scene()
。如果你想创建一个球形几何体,你需要写下const sphereGeometry = new THREE.SphereGeometry(1.5, 32, 32)
。
第一个场景
现在来创建我们的场景并创造一些东西在屏幕上。
我们需要四个元素:
- 一个场景用来包含物体
- 一些物体
- 一个相机
- 一个渲染器
场景
场景像一个容器。我们可以在里面放置物体,模型,粒子,光源。然后使用Three.js渲染场景。
为了创建场景,使用Scene类:
arduino
// Scene
const scene = new THREE.Scene()
物体
物体有很多类别,可以是原生的结合体,导入的模型,粒子,光源等其他东西。
我们将会从一个红色的立方体开始。
为了创建红色立方体,需要先创建一个类型为Mesh的物体。一个Mesh是几何体和材质的聚合体。
为了创建这个几何体,我们使用BoxGeometry类,带有三个参数对应着盒子的大小。
arduino
// Object
const geometry = new THREE.BoxGeometry(1, 1, 1)
为了创建材质,我们需要使用MeshBasicMaterial类,它带有一个参数:一个对象{}包含所有选项。我们需要指定它的color属性。
在Three.js中,有很多方式指定颜色。你可以传入JS十六进制格式 0xff0000 ,也可以传入字符串格式的十六进制'#ff0000',也可以使用像'red'这样的颜色名称,或者传入一个Color类的实例。
php
// Object
const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 })
为了创建最后的mesh,我们使用Mesh类,并且传入geometry和material作为参数。
php
// Object
const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 })
const mesh = new THREE.Mesh(geometry, material)
最后再往场景中添加mesh:
csharp
scene.add(mesh)
相机
相机是不可见的。它更像是理论上的一个观察点。当我们为场景做渲染的时候,会从相机的视角出发。
你可以拥有很多相机,并且在它们之间切换。但是通常情况下,我们只会用一个相机。
有几种不同的相机,我们会在之后的内容详细讨论。但现在,我们使用透视相机。
为了创建这个相机,需要使用PerspectiveCamera类。
view字段
view字段代表着视角的角度。如果你使用一个非常大的角度,你能立马看到各个方向的内容,但是有着很大的失真,因为这些内容会被绘制在很小的长方形内。如果你使用小角度,物体看起来就像被放大了。view字段(fov)表示我们在垂直方向上的可视角度。
aspect ratio
在很多情况下,aspect ratio是画布的宽度除以他的高度。
arduino
// Sizes
const sizes = {
width: 800,
height: 600
}
// Camera
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height)
scene.add(camera)
渲染器
渲染器的工作是渲染。
我们将会让渲染器将我们的场景从相机的视角渲染,并且渲染结果会绘制在画布上。你可以自己创建画图,或者让渲染器生成一个,然后把它加入到页面中。
在index.html中,替换掉<h1>
,在加载脚本之前创建一个<canvas>
元素:
ini
<canvas class="webgl"></canvas>
为了得到canvas变量,我们需要使用document.querySelector(...)。还需要用setSize(...)修改渲染器尺寸。
arduino
// Canvas
const canvas = document.querySelector('canvas.webgl')
// ...
// Renderer
const renderer = new THREE.WebGLRenderer({
canvas: canvas
})
renderer.setSize(sizes.width, sizes.height)
第一次渲染
在渲染器上调用render(...),并将scene和camera作为参数:
scss
renderer.render(scene, camera)
一个黑色的场景?我们物体在哪里?
原因在这里:我们没有指定物体的位置,也没有指定相机的位置。它们都在默认的位置上,这个场景的中心,所以我们看不到物体。
我们需要移动物体。
为了移动物体,我们需要访问每个物体的多个属性,比如position,rotation,scale。现在,我们使用postion属性向后移动我们的相机。
postion属性有三个相关的属性:x,y和z。默认情况下,Three.js默认 前/后 方向轴为z。
为了向后移动相机,我们需要提供一个相对值给position属性。你可以在任何地方设置position,只要在渲染前设置好就行。
arduino
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height)
camera.position.z = 3
scene.add(camera)
现在就可以看到我们渲染的第一个场景。