Three.js - 快速指南


Three.js - 简介

所有现代浏览器都变得更强大并且更容易直接使用 JavaScript 进行访问。他们采用了 WebGL(Web 图形库),这是一种 JavaScript API,允许您使用 GPU(图形处理单元)的功能在任何兼容的 Web 浏览器中渲染高性能交互式 3D 和 2D 图形。

但 WebGL 是一个非常低级的系统,只能绘制点、正方形和直线等基本对象。然而,直接从 JavaScript 进行 WebGL 编程是一个非常复杂且冗长的过程。您需要了解 WebGL 的内部细节并学习复杂的着色器语言才能充分利用 WebGL。Three.js来了,让您的生活变得轻松。

什么是 Three.js?

Three.js 是一个开源、轻量级、跨浏览器、通用 JavaScript 库。Three.js 在后台使用 WebGL,因此您可以使用它在浏览器中的 HTML <canvas> 元素上渲染图形。由于 Three.js 使用 JavaScript,因此您可以与其他网页元素进行交互,添加动画和交互,甚至创建具有某些逻辑的游戏。

为什么使用 Three.js?

以下功能使 Three.js 成为一个优秀的库。

  • 您只需使用 JavaScript 即可创建复杂的 3D 图形。

  • 您可以在浏览器内创建虚拟现实 (VR) 和增强现实 (AR) 场景。

  • 由于它使用WebGL,因此它具有跨浏览器支持。许多浏览器都支持它。

  • 您可以添加各种材质、纹理和动画 3D 对象。

  • 您还可以从其他 3D 建模软件加载和处理对象。

只需几行 JavaScript 和简单的逻辑,您就可以创建任何内容,从高性能交互式 3D 模型到逼真的实时场景。

这些是使用 Three.js 创建的一些优秀网站−

你可以在Three.js的官方网站上找到很多其他的例子

浏览器支持

桌面和移动设备上的所有现代浏览器目前都支持 WebGL。您唯一需要注意的浏览器是移动 Opera Mini 浏览器。对于 IE 10 及更早版本,有 IEWebGL 插件,您可以从https://github.com/iewebgl/iewebgl./您可以在此处找到有关 WebGL 浏览器支持的详细信息。

一旦您了解了 Three.js 是什么,您就可以继续阅读下一章,了解如何设置项目以开始使用 Three.js。

Three.js - 安装

有多种方法可以将 Three.js 包含在您的项目中。您可以使用以下任意方法来开始使用 Three.js。然后打开您最喜欢的代码编辑器并开始。

下载完整的 Three.js 项目

将完整的 Three.js 项目下载到您的系统中。您可以在此处或从GitHub下载。解压 Three.js-master.zip 文件并查看 build 文件夹内部。你可以找到两个三.js,三.min.js,这只是一个缩小版本。将这两个文件中的任何一个添加到您的项目文件夹中,并将它们链接到您的 HTML 文件。现在您可以在项目中使用 Three.js 了。

注意- 我们建议使用缩小版本,因为它加载速度更快。

将以下 <script> 标记插入到 HTML 的 <head> 元素中,其中包含 Threejs.min.js 文件的路径。

<script src='/path/to/threejs.min.js'></script>

使用 CDN 链接

您可以从 CDN(内容交付网络)链接文件,CDN 是一个专门用于托管文件的远程站点,以便您可以在线使用它们。您可以使用这些网站中的任何一个 -

将以下任意 <script> 标记插入 HTML 的 <head> 元素中。

<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r127/three.min.js"></script>

或者

<script src="https://cdn.jsdelivr.net/npm/three@0.127.0/build/three.min.js"></script>

安装Three.js包

Three.js 也可以作为NPM 上的包提供。如果您的计算机上安装了 Node.js,则可以使用 npm 或yarn 安装它。

npm install three

或者

yarn add three

然后,您可以将 Three.js 从 Three.module.js 文件导入到您的 JavaScript 文件中

import * as THREE from 'three'

您可以将 Three.js 与任何 JavaScript 框架(如 React、Angular、Vue)一起使用。

完成项目设置后,让我们开始创建。

Three.js - Hello Cube 应用程序

与任何其他编程语言一样,让我们​​通过创建“Hellocube!”来开始学习 Three.js。应用程序。

超文本标记语言

<!DOCTYPE html>
<html>
   <head>
      <meta name="viewport" content="width=device-width, initial-scale=1" />
      <meta charset="UTF-8" />
      <title>Three.js - Hello cube</title>
      <style>
         /* Our CSS goes here */
      </style>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r127/three.min.js"></script>
   </head>
   <body>
      <div id="threejs-container">
         <!-- Our output to be rendered here →
      </div>
      <script type="module">
         // our JavaScript code goes here
      </script>
   </body>
</html>

正如您所看到的,它只是一个带有 Three.js CDN 的简单 HTML 文件。

CSS

<style>
* {
   margin: 0;
   padding: 0;
   box-sizing: border-box;
   font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
   Oxygen,
   Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
}
html,
body {
   height: 100vh;
   width: 100vw;
}
#threejs-container{
   position: block;
   width: 100%;
   height: 100%;
}
</style>

上面的CSS只是HTML页面的基本样式。Threejs-容器占据了整个屏幕。

JavaScript

这就是我们的 Three.js 应用程序发挥作用的地方。下面的代码在屏幕中间渲染一个立方体。所有这些代码都将进入 HTML 中的空 <script> 标记。

const width = window.innerWidth
const height = window.innerHeight
// Scene
const scene = new THREE.Scene()
scene.background = new THREE.Color('#00b140')
// Camera
const fov = 45 // AKA Field of View
const aspect = window.innerWidth / window.innerHeight
const near = 0.1 // the near clipping plane
const far = 100 // the far clipping plane
const camera = new PerspectiveCamera(fov, aspect, near, far)
camera.position.set(0, 0, 10)
// Renderer
const renderer = new THREE.WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
// Creating a cube
const geometry = new THREE.BoxGeometry(2, 2, 2)
const material = new THREE.MeshBasicMaterial({ wireframe: true })
const cube = new THREE.Mesh(geometry, material)
scene.add(cube)
// Rendering the scene
const container = document.querySelector('#threejs-container')
container.append(renderer.domElement)
renderer.render(scene, camera)

让我们一次一步地讨论代码,然后您可以在接下来的章节中获得有关每个元素的更多信息。我们需要做的第一件事是创建场景、相机和渲染器。这些是构成每个 Three.js 应用程序的基本组件。

现场

const scene = new THREE.Scene()
scene.background = new THREE.Color('#262626')

场景作为我们在屏幕上看到的所有内容的容器,如果没有 THREE.Scene 对象,Three.js 就无法渲染任何内容。背景颜色是深灰色,这样我们就可以看到立方体。

相机

const camera = new PerspectiveCamera(fov, aspect, near, far)
camera.position.set(0, 0, 10)

相机对象定义了渲染场景时我们将看到的内容。相机的类型不多,但类型不同,但在本例中,您将使用 PerspectiveCamera,它与我们的眼睛观察世界的方式相匹配。

渲染器

const renderer = new THREE.WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)

渲染器对象负责根据相机计算场景在浏览器中的外观。渲染器有不同类型,但我们主要使用 WebGLRenderer,因为大多数浏览器都支持 WebGL。

除了创建渲染器实例之外,我们还需要设置渲染应用程序的大小。最好使用我们想要用我们的应用程序 The Cube 填充的区域的宽度和高度 - 在本例中是浏览器窗口的宽度和高度。

立方体

const geometry = new THREE.BoxGeometry(2, 2, 2)
const material = new THREE.MeshBasicMaterial({
   color: 0xffffff,
   wireframe: true,
})
const cube = new THREE.Mesh(geometry, material)
scene.add(cube)

上面的代码在屏幕中央创建了一个简单的立方体。我们可以使用 THREE.Mesh 制作任何物体。网格需要两个对象:几何体和材质。网格的几何形状定义了其形状,材料决定了对象的表面属性。

要创建立方体,我们需要 BoxGeometry 和颜色为 0xffffff 的主要材质 (MeshBasicMaterial)。如果将wireframe属性设置为true,它会告诉Three.js向我们显示一个线框而不是一个实体对象。

渲染场景

const container = document.querySelector('#threejs-container')
container.append(renderer.domElement)
renderer.render(scene, camera)

例子

最后但并非最不重要的一点是,我们将渲染器元素添加到 HTML 文档中。渲染器使用 <canvas> 元素向我们显示场景。在这种情况下,渲染器将 <canvas> 元素附加到 HTML 中的引用容器。

你好-cube-app.html

<!DOCTYPE html>
<html lang="en">
   <head>
      <meta charset="UTF-8" />
      <meta http-equiv="X-UA-Compatible" content="ie=edge" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0" />
      <title>Three.js – Hello cube</title>
      <style>
         * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: -applesystem, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
            Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
         }
         html,
         body {
            height: 100vh;
            overflow: hidden;
            width: 100vw;
         }
         #threejs-container {
            position: block;
            width: 100%;
            height: 100%;
         }
      </style>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
   </head>
   <body>
      <div id="threejs-container"></div>
      <script type="module">
         // Hello Cube App
         // Your first Three.js application
         // sizes
         const width = window.innerWidth
         const height = window.innerHeight
         // scene
         const scene = new THREE.Scene()
         scene.background = new THREE.Color(0x262626)
         // camera
         const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 100)
         camera.position.set(0, 0, 10)
         // cube
         const geometry = new THREE.BoxGeometry(2, 2, 2)
         const material = new THREE.MeshBasicMaterial({
            color: 0xffffff,
            wireframe: true
         })
         const cube = new THREE.Mesh(geometry, material)
         scene.add(cube)
         // renderer
         const renderer = new THREE.WebGL1Renderer()
         renderer.setSize(width, height)
         renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
         // rendering the scene
         const container = document.querySelector('#threejs-container')
         container.append(renderer.domElement)
         renderer.render(scene, camera)
      </script>
   </body>
</html>

输出

如果一切正常,输出看起来像这样。尝试一下代码以更好地理解它的工作原理。

您现在已经完成了第一个 Three.js 应用程序的创建。让我们继续为应用程序添加更多美感。

Three.js - 渲染器和响应能力

场景的基本功能

您知道场景是相机、灯光和我们要在屏幕上渲染的对象的容器。让我们看一下 Scene 对象的一些基本功能 -

添加对象

函数 add(object) 用于将对象添加到场景中。

const scene = THREE.Scene()
scene.add(cube) // adds the cube
scene.add(sphere) // adds a sphere

移除对象

函数remove(object)从场景中删除一个对象。

scene.remove(cube) // removes the last added cube
scene.remove(sphere) // removes a sphere

孩子们

在 scene.children 中返回场景中所有对象的数组,包括相机和灯光。

console.log(scene.children) // outputs all the objects in the scene
console.log(scene.children.length) // outputs number of elements on the
scene

注意- 我们可以使用名称属性为任何对象命名。名称对于调试目的很方便,但也可以直接访问场景中的对象。

查看以下示例。

场景.html

<!DOCTYPE html>
<html lang="en">
   <head>
      <meta charset="UTF-8" />
      <meta http-equiv="X-UA-Compatible" content="ie=edge" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0" />
      <title>Three.js – The scene
      <style>
         * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: -applesystem, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
               Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
         }
         html,
         body {
            height: 100vh;
            width: 100vw;
            background-color: #262626;
            overflow: hidden;
         }
         #btn-conatiner {
            position: absolute;
            top: 0;
            left: 0;
            height: 10vh;
            width: 100%;
         }
         @media screen and (max-width:600px){
            #btn-container{
               display: flex;
               flex-direction: column;
            }
         }
         .btn {
            padding: 5px 15px;
            margin: 5px 15px;
            font-weight: bold;
            text-transform: uppercase;
         }
         .add {
            color: green;
         }
         .rem {
            color: red;
         }
         #threejs-container {
            position: block;
            width: 100%;
            height: 100%;
         }
      </style>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.js"></script>
   </head>
   <body>
      <div id="btn-conatiner">
         <button class="btn add">Add Cube</button>
         <button class="btn rem">Remove Cube</button>
      </div>
      <div id="threejs-container"></div>
      <script type="module">
         // Experimenting with different methods of scene
         // add, remove, children, getElementById
         // sizes
         let width = window.innerWidth
         let height = window.innerHeight
         const gui = new dat.GUI()
         // scene
         const scene = new THREE.Scene()
         scene.background = new THREE.Color(0x262626)
         // lights
         const ambientLight = new THREE.AmbientLight(0xffffff, 0.5)
         scene.add(ambientLight)
         const light = new THREE.PointLight(0xffffff, 0.5)
         light.position.set(-10, 10, -10)
         // for shadow
         light.castShadow = true
         light.shadow.mapSize.width = 1024
         light.shadow.mapSize.height = 1024
         light.shadow.camera.near = 0.1
         light.shadow.camera.far = 1000
         scene.add(light)
         // camera
         const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000)
         camera.position.set(0, 10, 40)
         camera.lookAt(0, 0, 0)
         gui.add(camera.position, 'z', 10, 200, 1).name('camera-z')
         // plane
         const planeGeometry = new THREE.PlaneGeometry(100, 100)
         const plane = new THREE.Mesh(
            planeGeometry,
            new THREE.MeshPhongMaterial({ color: 0xffffff, side: THREE.DoubleSide })
         )
         plane.rotateX(Math.PI / 2)
         plane.position.y = -1.75
         plane.receiveShadow = true
         scene.add(plane)
         // scene.add
         function addCube() {
            const cubeSize = Math.ceil(Math.random() * 3)
            const cubeGeometry = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize)const cubeMaterial = new THREE.MeshLambertMaterial({
               color: Math.random() * 0xffffff
            })
            const cube = new THREE.Mesh(cubeGeometry, cubeMaterial)
            cube.castShadow = true
            cube.name = 'cube-' + scene.children.length
            cube.position.x = -30 + Math.round(Math.random() * 50)
            cube.position.y = Math.round(Math.random() * 5)
            cube.position.z = -20 + Math.round(Math.random() * 50)
            scene.add(cube)
         }
         const add = document.querySelector('.add')
         add.addEventListener('click', () => {
            addCube()
            console.log('cube added')
         })
         // scene.remove
         function removeCube() {
            const allChildren = scene.children
            const lastObject = allChildren[allChildren.length - 1]
            if (lastObject.name) {
               scene.remove(lastObject)
            }
         }
         const remove = document.querySelector('.rem')
         remove.addEventListener('click', () => {
            removeCube()
            console.log('cube removed')
         })
         // scene.children
         console.log(scene.children)
         // responsivenesswindow.addEventListener('resize', () => {
            width = window.innerWidth
            height = window.innerHeight
            camera.aspect = width / height
            camera.updateProjectionMatrix()
            renderer.setSize(window.innerWidth, window.innerHeight)
            renderer.render(scene, camera)
         })
         // renderer
         const renderer = new THREE.WebGL1Renderer()
         renderer.setSize(width, height)
         renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
         // animation
         function animate() {
            requestAnimationFrame(animate)
            renderer.render(scene, camera)
         }
         // rendering the scene
         const container = document.querySelector('#threejs-container')
         container.append(renderer.domElement)
         renderer.render(scene, camera)
         animate()
      </script>
   </body>
</html>

打开控制台查看场景中的元素。

添加立方体

使用名称属性

函数 scene.getObjectByName(name) 直接从场景中按特定名称返回对象。

您还可以添加另一个参数 - 递归。

scene.getObjectByName(name, recursive)

如果将递归参数设置为 true,Three.js 将搜索完整的对象树以查找具有指定名称的事物。

为场景添加雾

此属性允许您设置场景的雾。雾呈现出薄雾,遮盖了远处的物体。

scene.fog = new THREE.Fog(0xffffff, 0.015, 100)

这行代码定义了白雾(0xffffff)。您可以使用前面的两个属性来调整雾的显示方式。0.015 值设置近属性,100 值设置远属性。利用这些属性,您可以确定雾开始的位置以及雾变得更浓的速度。

对于 THREE.Fog 对象,雾会线性增加。还有一种不同的方式来设置场景的雾气;为此,请使用以下定义 -

scene.fog = new THREE.FogExp2(0xffffff, 0.01)

这次,我们不指定远近,只指定颜色(0xffffff)和雾的密度(0.01)。最好对这些属性进行一些试验以获得您想要的效果。

使用覆盖材质属性

overrideMaterial 属性强制场景中的所有对象使用相同的材​​质。

scene.overrideMaterial = new THREE.MeshLambertMaterial({ color: 0xffffff })

这里,场景中的所有物体采用相同的材​​质,即MeshLambertMaterial。

注意- THREE.Scene 是一种有时也称为Scenegraph 的结构。场景图是一种可以保存图形场景所有必要信息的结构。在 Three.js 中,这意味着 THREE.Scene 包含渲染所需的所有对象、灯光和其他对象。

场景图

渲染器

渲染器使用相机和场景中的信息在屏幕上绘制输出,即 <canvas> 元素。

在 Hello 立方体应用程序中,我们使用了 WebGLRenderer。还可以使用其他一些渲染器,但 WebGLRenderer 是迄今为止最强大的可用渲染器,并且通常是您唯一需要的渲染器。

注意- 有一个基于 canvas 的渲染器、一个基于 CSS 的渲染器和一个基于 SVG 的渲染器。尽管它们可以工作并且可以渲染简单的场景,但我不建议使用它们。它们的开发并不积极,非常消耗 CPU,并且缺乏良好的材质支持和阴影等功能。

Three.js - 响应式设计

在调整屏幕大小时,您可以观察到场景没有响应。使网页响应通常是指页面在不同尺寸的显示器(从台式机、平板电脑到手机)上显示良好。在本章中,您可以了解如何解决 Three.js 应用程序的一些基本问题。

当浏览器尺寸改变时自动调整输出大小

当您调整浏览器大小时,我们必须通知 Three.js 以了解 <canvas> 元素应该有多宽。对于相机,我们需要更新纵横比属性,该属性保存屏幕的纵横比,对于渲染器,我们需要更改其大小。

window.addEventListener('resize', () => {
   // update display width and height
   width = window.innerWidth
   height = window.innerHeight
   // update camera aspect
   camera.aspect = width / height
   camera.updateProjectionMatrix()
   // update renderer
   renderer.setSize(width, height)
   renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
   renderer.render(scene, camera)
})

例子

上面的代码为您的 Three.js 项目提供了响应能力。

调整浏览器大小.html

<!DOCTYPE html>
<html lang="en">
   <head>
      <meta charset="UTF-8" />
      <meta http-equiv="X-UA-Compatible" content="ie=edge" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0" />
      <title>Three.js – Resizing browser</title>
      <style>
         * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: -applesystem, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
            Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
         }
         html,
         body {
            height: 100vh;
            width: 100vw;
         }
         #threejs-container {
            position: block;
            width: 100%;
            height: 100%;
         }
      </style>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.js"></script>
   </head>
   <body>
      <div id="threejs-container"></div>
      <script type="module">
         // Adding responsiveness for Three.js
         // sizes
         let width = window.innerWidth
         let height = window.innerHeight
         const gui = new dat.GUI()
         // scene
         const scene = new THREE.Scene()
         scene.background = new THREE.Color(0x262626)
         // camera
         const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 100)
         camera.position.set(0, 0, 10)
         // cube
         const geometry = new THREE.BoxGeometry(2, 2, 2)
         const material = new THREE.MeshBasicMaterial({
            color: 0xffffff,
            wireframe: true
         })
         const cube = new THREE.Mesh(geometry, material)
         scene.add(cube)
         // responsiveness
         window.addEventListener('resize', () => {
            width = window.innerWidth
            height = window.innerHeight
            camera.aspect = width / height
            camera.updateProjectionMatrix()
            renderer.setSize(window.innerWidth, window.innerHeight)
            renderer.render(scene, camera)
         })
         // renderer
         const renderer = new THREE.WebGL1Renderer()
         renderer.setSize(width, height)
         renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
         // animation
         function animate() {
            requestAnimationFrame(animate)
            cube.rotation.x += 0.005
            cube.rotation.y += 0.01
            renderer.render(scene, camera)
          }
         // rendering the scene
         const container = document.querySelector('#threejs-container')
         container.append(renderer.domElement)
         renderer.render(scene, camera)
         animate()
      </script>
   </body>
</html>

输出

当您执行代码时,它将产生以下输出 -

现在,调整浏览器的大小。由于响应式设计,该对象始终会重新定位在浏览器的中心。

抗锯齿

锯齿效应是指边缘和对象(使用像素渲染)上出现锯齿状边缘或“锯齿”(也称为阶梯线)。

抗锯齿

例子

抗锯齿.html

<!DOCTYPE html>
<html lang="en">
   <head>
      <meta charset="UTF-8" />
      <meta http-equiv="X-UA-Compatible" content="ie=edge" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0" />
      <title>Three.js - Anti-aliasing</title>
      <style>
         * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: -applesystem, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
            Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
         }
         html,
         body {
            height: 100vh;
            width: 100vw;
         }
         #threejs-container {
            position: block;
            width: 100%;
            height: 100%;
         }
      </style>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.js"></script>
   </head>
   <body>
      <div id="threejs-container"></div>
      <script type="module">
         // Adding anti-aliasing to Three.js app for removing jaggies
         // sizes
         let width = window.innerWidth
         let height = window.innerHeight
         const gui = new dat.GUI()
         // scene
         const scene = new THREE.Scene()
         scene.background = new THREE.Color(0x262626)
         // camera
         const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 100)
         camera.position.set(0, 0, 10)
         // cube
         const geometry = new THREE.BoxGeometry(2, 2, 2)
         const material = new THREE.MeshBasicMaterial({
            color: 0xffffff,
            wireframe: true
         })
         const cube = new THREE.Mesh(geometry, material)
         scene.add(cube)
         // responsiveness
         window.addEventListener('resize', () => {
            width = window.innerWidth
            height = window.innerHeight
            camera.aspect = width / height
            camera.updateProjectionMatrix()
            renderer.setSize(window.innerWidth, window.innerHeight)
            renderer.render(scene, camera)
         })
         // renderer - anti-aliasing
         const renderer = new THREE.WebGLRenderer({ antialias: true })
         renderer.physicallyCorrectLights = true
         renderer.setSize(width, height)
         renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
         // animation
         function animate() {
            requestAnimationFrame(animate)
            cube.rotation.x += 0.005
            cube.rotation.y += 0.01
            renderer.render(scene, camera)
         }
         // rendering the scene
         const container = document.querySelector('#threejs-container')
         container.append(renderer.domElement)
         renderer.render(scene, camera)
         animate()
      </script>
   </body>
</html>

输出

我们的 Hello 立方体应用程序中的别名如下所示。

网页渲染器

我们可以通过将 WebGLRenderer 的 antialias 属性设置为 true 来打开抗锯齿功能。默认情况下,它是错误的。在这里,我们将抗锯齿参数设置为 true -

const renderer = new WebGLRenderer({ antialias: true })
renderer.physicallyCorrectLights = true

抗锯齿后,它看起来很平滑,没有锯齿,如下图所示。

没有锯齿

physicalCorrectLights 属性告诉 Three.js 是否使用物理上正确的光照模式。默认为 false。将其设置为 true 有助于增加对象的细节。

Three.js - 调试和统计

使用Dat.GUI

不断尝试变量的值(例如立方体的位置)是很困难的。在这种情况下,假设直到你得到你喜欢的东西。这是一个缓慢而艰巨的过程。幸运的是,已经有一个很好的解决方案可以与 Three.js、dat.GUI 完美集成。它允许您创建一个可以更改代码中变量的基本用户界面组件。

安装

要在您的项目中使用 dat.GUI,请在此处下载并将 <script> 标记添加到 HTML 文件中。

<script type='text/javascript' src='path/to/dat.gui.min.js'></script>

或者您可以使用 CDN,在 HTML 中添加以下 <script> 标记。

<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.js"></script>

如果您在节点应用程序中使用 Three.js,请安装 npm 包 - dat.GUI 并将其导入您的 JavaScript 文件中。

npm install dat.gui

或者

yarn add dat.gui
import * as dat from 'dat.gui'

用法

首先,您应该初始化对象本身。它创建一个小部件并将其显示在屏幕右上角。

const gui = new dat.GUI()

然后,您可以添加要控制的参数和变量。例如,下面的代码是控制立方体的y位置。

gui.add(cube.position, 'y')

例子

尝试添加其他位置变量。请参阅此工作代码示例。

立方体.html

<!DOCTYPE html>
<html lang="en">
   <head>
      <meta charset="UTF-8" />
      <meta http-equiv="X-UA-Compatible" content="ie=edge" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0" />
      <title>Three.js - Position GUI</title>
      <style>
         * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: -applesystem, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
            Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
         }
         html,
         body {
            height: 100vh;
            width: 100vw;
         }
         #threejs-container {
            position: block;
            width: 100%;
            height: 100%;
         }
      </style>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.js"></script>
   </head>
   <body>
      <div id="threejs-container"></div>
      <script type="module">
         // Adding UI to debug and experimenting different values
         // UI
         const gui = new dat.GUI()
         // sizes
         let width = window.innerWidth
         let height = window.innerHeight
         // scene
         const scene = new THREE.Scene()
         scene.background = new THREE.Color(0x262626)
         // camera
         const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 100)
         camera.position.set(0, 0, 10)
         // cube
         const geometry = new THREE.BoxGeometry(2, 2, 2)
         const material = new THREE.MeshBasicMaterial({
            color: 0xffffff,
            wireframe: true
         })
         gui.add(material, 'wireframe')
         const cube = new THREE.Mesh(geometry, material)
         scene.add(cube)
         gui.add(cube.position, 'x')
         gui.add(cube.position, 'y')
         gui.add(cube.position, 'z')
         // responsiveness
         window.addEventListener('resize', () => {
            width = window.innerWidth
            height = window.innerHeight
            camera.aspect = width / height
            camera.updateProjectionMatrix()
            renderer.setSize(window.innerWidth, window.innerHeight)
            renderer.render(scene, camera)
         })
         // renderer
         const renderer = new THREE.WebGL1Renderer()
         renderer.setSize(width, height)
         renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
         // animation
         function animate() {
            requestAnimationFrame(animate)
            cube.rotation.x += 0.005
            cube.rotation.y += 0.01
            renderer.render(scene, camera)
         }
         // rendering the scene
         const container = document.querySelector('#threejs-container')
         container.append(renderer.domElement)
         renderer.render(scene, camera)
         animate()
      </script>
   </body>
</html>

输出

您可以使用 name 属性自定义显示的标签。要更改变量行上的标签,请使用 .name("your label")。

gui.add(cube.position, 'y').name('cube-y')

您可以设置最小/最大限制以及获取滑块的步骤。以下行允许值从 1 到 10,每次将值增加 1。

gui.add(cube.position, 'y').min(1).max(10).step(1)
// or
gui.add(cube.position, 'y', 1, 10, 1)

如果有许多同名的变量,您可能会发现很难区分它们。在这种情况下,您可以为每个对象添加文件夹。与对象相关的所有变量都位于一个文件夹中。

// creating a folder
const cube1 = gui.addFolder('Cube 1')
cube1.add(redCube.position, 'y').min(1).max(10).step(1)
cube1.add(redCube.position, 'x').min(1).max(10).step(1)
cube1.add(redCube.position, 'z').min(1).max(10).step(1)
// another folder
const cube2 = gui.addFolder('Cube 2')
cube2.add(greenCube.position, 'y').min(1).max(10).step(1)
cube2.add(greenCube.position, 'x').min(1).max(10).step(1)
cube2.add(greenCube.position, 'z').min(1).max(10).step(1)

例子

现在,检查以下示例。

gui-folders.html

<!DOCTYPE html>
<html lang="en">
   <head>
      <meta charset="UTF-8" />
      <meta http-equiv="X-UA-Compatible" content="ie=edge" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0" />
      <title>Three.js - More variables</title>
      <style>
         * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: -applesystem, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
               Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
         }
         html,
         body {
            height: 100vh;
            width: 100vw;
         }
         #threejs-container {
            position: block;
            width: 100%;
            height: 100%;
         }
      </style>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.js"></script>
   </head>
   <body>
      <div id="threejs-container"></div>
      <script type="module">
         // Adding folders to distinguish between variables

         // controls
         const gui = new dat.GUI()
         // sizes
         let width = window.innerWidth
         let height = window.innerHeight
         // scene
         const scene = new THREE.Scene()
         scene.background = new THREE.Color(0x262626)
         // camera
         const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 100)
         camera.position.set(0, 0, 10)
         const camFolder = gui.addFolder('Camera')
         camFolder.add(camera.position, 'z').min(10).max(60).step(10)
         // cube
         const geometry = new THREE.BoxGeometry(2, 2, 2)
         const material = new THREE.MeshBasicMaterial({
            color: 0xffffff,
            wireframe: true
         })
         const cubeColor = {
            color: 0xffffff
         }
         const materialFolder = gui.addFolder('Material')
         materialFolder.add(material, 'wireframe')
         materialFolder.addColor(cubeColor, 'color').onChange(() => {
            // callback
            material.color.set(cubeColor.color)
         })
         materialFolder.open()
         const cube = new THREE.Mesh(geometry, material)
         scene.add(cube)
         const cubeFolder = gui.addFolder('Cube')
         // for position
         const posFolder = cubeFolder.addFolder('position')
         posFolder.add(cube.position, 'x', 0, 5, 0.1)
         posFolder.add(cube.position, 'y', 0, 5, 0.1)
         posFolder.add(cube.position, 'z', 0, 5, 0.1)
         posFolder.open()
         // for scale
         const scaleFolder = cubeFolder.addFolder('Scale')
         scaleFolder.add(cube.scale, 'x', 0, 5, 0.1).name('Width')
         scaleFolder.add(cube.scale, 'y', 0, 5, 0.1).name('Height')
         scaleFolder.add(cube.scale, 'z', 0, 5, 0.1).name('Depth')
         scaleFolder.open()
         cubeFolder.open()
         // responsiveness
         window.addEventListener('resize', () => {
            width = window.innerWidth
            height = window.innerHeight
            camera.aspect = width / height
            camera.updateProjectionMatrix()
            renderer.setSize(window.innerWidth, window.innerHeight)
            renderer.render(scene, camera)
         })
         // renderer
         const renderer = new THREE.WebGL1Renderer()
         renderer.setSize(width, height)
         renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
         // animation
         function animate() {
         requestAnimationFrame(animate)
            cube.rotation.x += 0.005
            cube.rotation.y += 0.01
            renderer.render(scene, camera)
         }
         // rendering the scene
         const container = document.querySelector('#threejs-container')
         container.append(renderer.domElement)
         renderer.render(scene, camera)
         animate()
      </script>
   </body>
</html>

输出

您还可以添加一些回调函数。一旦值改变就会触发onChange。

gui.add(cube.position, 'y').onChange(function () {
   // refresh based on the new value of y
   console.log(cube.position.y)
})

让我们看一下使用 dat.gui 和回调更改颜色的另一个示例。

// parameter
const cubeColor = {
   color: 0xff0000,
}
gui.addColor(cubeColor, 'color').onChange(() => {
   // callback
   cube.color.set(cubeColor.color)
})

当cubeColor的颜色改变时,上面的回调onChange通知Three.js改变立方体的颜色。

从现在开始我们将经常使用这个 dat.gui。通过尝试“Hello Cube!”确保您习惯了它。应用程序。

  • 统计- 统计在大规模应用中发挥着重要作用。

Three.js - 相机

相机类型

Three.js 中有两种类型的相机。

先生编号 相机及描述
1

透视相机

Three.js 中有不同的相机。最常见的相机也是我们一直使用的相机是 PerspectiveCamera。

2

正交相机

第二个最常见的相机是正交相机。它指定一个具有左、右、上、下、近和远设置的框。它以二维方式表示三维物体。

让相机跟随物体

在动画函数中,我们使用camera.lookAt函数将相机指向对象的位置函数。我们在渲染的每一帧中都执行此操作。看起来相机完全跟随物体的位置。

function animate() {
   const object = scene.getObjectByName('sphere')
   renderer.render(scene, camera)
   camera.lookAt(object.position)
   requestAnimationFrame(render)
}

Three.js - 控件

您可以使用摄像机控制在场景中移动摄像机。Three.js 有许多摄像机控件,您可以使用它们来控制整个场景中的摄像机。您必须从GitHub单独获取控件。Three.js 库不包含这些。

先生编号 控制和说明
1

轨道控制

轨道控制允许摄像机围绕场景中心旋转。

2

轨迹球控制

TrackballControls 与 Orbit 控件类似。然而,它不保持恒定的相机向上矢量。

3

飞行控制

这些是类似飞行模拟器的控件。使用键盘和鼠标移动和驾驶。

4

指针锁定控件

PointerLockControls 实现内置浏览器 Pointer Lock API。

在本章中,我们看到了最有用的控件。一些开发人员正在为 Three.js 创建更有用的控件。您可以在这里看到一些其他控件,它们有详细的文档记录并且易于使用。

Three.js - 光与影

灯光使对象可见,类似地,在 Three.js 中,THREE.Light 照亮场景并使某些东西可见。并非所有材质都会受到光照的影响。MeshBasicMaterial 和 MeshNormalMaterial 是自发光的,因此它们不需要照明即可在场景中可见。但是,大多数其他材质(MeshLambertMaterial、MeshPhongMaterial、MeshStandardMaterial、MeshPhysicalMaterial 和 MeshToonMaterial)都可以。我们将在后续章节中讨论更多材料。在本章中,我们将重点介绍 Three.js 中不同类型的灯光。

每种光都具有颜色和强度属性。

  • color - (可选)灯光的十六进制颜色。默认值为 0xffffff(白色)。

  • 强度- (可选)光强度/强度的数值。默认值为 1。

投射阴影

来自特定方向的光可以投射阴影。首先,我们应该让场景准备好投射阴影。

步骤 - 1

我们应该首先告诉渲染器我们要启用阴影。投射阴影是一项昂贵的操作。WebGLRenderer 仅支持此功能。它使用阴影映射,这是一种特定于 WebGL 的技术,直接在 GPU 上执行。

renderer.shadowMapEnabled = true

上面的代码行告诉渲染器在场景中投射阴影。

注意- Three.js 默认情况下使用阴影贴图。阴影贴图适用于投射阴影的光。

场景渲染所有标记为从光的角度投射阴影的对象。

如果阴影的边缘看起来有点块状,则意味着阴影贴图太小。要增加阴影贴图大小,您可以为灯光定义shadowMapHeight 和shadowMapWidht 属性。或者,您也可以尝试更改WebGLRenderer的shadowMapType属性。您可以将其设置为 THREE.BasicShadowMap、THREE.PCFShadowMap 或 THREE.PCFSoftShadowMap。

// to antialias the shadow
renderer.shadowMapType = THREE.PCFSoftShadowMap
// or
directionalLight.shadowMapWidth = 2048
directionalLight.shadowMapHeight = 2048

步骤 - 2

您应该配置对象来投射阴影。您可以告知 Three.js 哪些对象可以投射阴影以及哪些对象可以接收阴影。

object.castShadow = true
object.recieveShadow = true

步骤 - 3

上述所有步骤对于每个灯都是相同的。下一步是设置与阴影相关的属性。

light.castShadow = true
light.shadow.camera.near = 10
light.shadow.camera.far = 100
light.shadow.camera.left = -50
light.shadow.camera.right = 50
light.shadow.camera.top = 50
light.shadow.camera.bottom = -50

第一个属性castShadow告诉Three.js这个光投射阴影。由于投射阴影是一项昂贵的操作,因此我们需要定义阴影可以出现的区域。您可以使用shadow.camera.near、shadow.camera.far 和shadow.camera.left 等属性来完成此操作。利用上述属性,我们创建了一个类似盒子的区域,Three.js 在此渲染阴影。

例子

在此示例中探索更多内容。

定向.html

<!DOCTYPE html>
<html lang="en">
   <head>
      <meta charset="UTF-8" />
      <meta http-equiv="X-UA-Compatible" content="ie=edge" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0" />
      <title>Three.js - Directional Light</title>
      <style>
         * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: -applesystem, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
            Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
         }
         html,
         body {
            height: 100vh;
            width: 100vw;
         }
         #threejs-container {
            position: block;
            width: 100%;
            height: 100%;
         }
      </style>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.js"></script>
   </head>
   <body>
   <div id="container"></div>
      <script type="module">
         // Adding directional light to the scene
         // The lights falls from the light only in one direction.
         // You can see the position of light using helpers provided in Three.j
         s for debugging purposes

         // GUI
         const gui = new dat.GUI()
         // sizes
         let width = window.innerWidth
         let height = window.innerHeight
         // scene
         const scene = new THREE.Scene()
         scene.background = new THREE.Color(0x262626)
         // camera
         const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000)
         camera.position.set(0, 0, 10)
         const camFolder = gui.addFolder('Camera')
         camFolder.add(camera.position, 'z', 10, 80, 1)
         camFolder.open()
         // lights
         const ambientLight = new THREE.AmbientLight(0xffffff, 0.5)
         scene.add(ambientLight)
         const light = new THREE.DirectionalLight()
         light.position.set(2.5, 2, 2)
         light.castShadow = true
         light.shadow.mapSize.width = 512
         light.shadow.mapSize.height = 512
         light.shadow.camera.near = 0.5
         light.shadow.camera.far = 100
         scene.add(light)
         const helper = new THREE.DirectionalLightHelper(light)
         scene.add(helper)
         // light controls
         const lightColor = {
            color: light.color.getHex()
         }
         const lightFolder = gui.addFolder('Directional Light')
         lightFolder.addColor(lightColor, 'color').onChange(() => {
         light.color.set(lightColor.color)
         })
         lightFolder.add(light, 'intensity', 0, 1, 0.01)
         lightFolder.open()
         const directionalLightFolder = gui.addFolder('Position of Light')
         directionalLightFolder.add(light.position, 'x', -10, 10, 0.1)
         directionalLightFolder.add(light.position, 'y', -10, 10, 0.1)
         directionalLightFolder.add(light.position, 'z', -10, 10, 0.1)
         directionalLightFolder.open()
         // plane
         const planeGeometry = new THREE.PlaneGeometry(100, 20)
         const plane = new THREE.Mesh(planeGeometry, new THREE.MeshPhongMateria
         l({ color: 0xffffff }))
         plane.rotateX(-Math.PI / 2)
         plane.position.y = -1.75
         plane.receiveShadow = true
         scene.add(plane)
         // cube
         const geometry = new THREE.BoxGeometry(2, 2, 2)
         const material = new THREE.MeshStandardMaterial({
            color: 0x87ceeb
         })
         const materialFolder = gui.addFolder('Material')
         materialFolder.add(material, 'wireframe')
         materialFolder.open()
         const cube = new THREE.Mesh(geometry, material)
         cube.position.set(0, 0.5, 0)
         cube.castShadow = true
         cube.receiveShadow = true
         scene.add(cube)
         // responsiveness
         window.addEventListener('resize', () => {
            width = window.innerWidth
            height = window.innerHeight
            camera.aspect = width / height
            camera.updateProjectionMatrix()
            renderer.setSize(window.innerWidth, window.innerHeight)
            renderer.render(scene, camera)
         })
         // renderer
         const renderer = new THREE.WebGL1Renderer()
         renderer.setSize(window.innerWidth, window.innerHeight)
         renderer.shadowMap.enabled = true
         renderer.shadowMap.type = THREE.PCFSoftShadowMap
         renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
         // animation
         function animate() {
            requestAnimationFrame(animate)
            cube.rotation.x += 0.005
            cube.rotation.y += 0.01
            renderer.render(scene, camera)
         }
         // rendering the scene
         const container = document.querySelector('#container')
         container.append(renderer.domElement)
         renderer.render(scene, camera)
         animate()
      </script>
   </body>
</html>

输出

聚光灯
先生编号 灯光及说明
1

环境光

它是最基本的光,均匀地照亮整个场景。

2

定向光

定向光来自特定点并从远处直接发射到目标。

3

聚光灯

它是另一种来自圆锥体特定方向的光。

4

点光源

点光源是从一个点向各个方向发射光的光源。

5

半球光

它是一种用于创造自然光的特殊灯。

Three.js - 几何

几何图形用于在 Three.js 中创建和定义形状。Three.js 有许多类型的内置几何图形,包括 2D 和 3D。

在本章中,我们将讨论基本的内置几何图形。我们将首先查看 2D 几何图形,然后,我们将探索所有可用的基本 3D 几何图形。

先生编号 几何形状和描述
1

平面几何

THREE.PlaneGeometry 创建一个简单的 2D 矩形。

2

圆几何

THREE.CircleGeometry 创建一个简单的 2D 圆。

3

环几何形状

THREE.RingGeometry 创建一个中心有孔的 D 盘。

4

盒子几何形状

THREE.BoxGeometry 创建一个具有指定尺寸的简单 3D 框。

5

球体几何

THREE.SphereGeometry 创建 3D 球体几何形状。

6

圆柱体几何形状

要在 Three.js 中创建圆柱体,您可以使用 Three.CylinderGeometry。

7

圆锥体几何形状

您可以使用 THREE.ConeGeometry 创建圆锥体。它与 CylinderGeometry 非常相似,不同之处在于它只允许您设置半径而不是 radiusTop 和 radiusBottom。

8

环面几何

环面是一种管状形状,看起来像甜甜圈。您可以使用 THREE.TorusGeometry 在 Three.js 中创建圆环。

9

圆环结几何形状

环面结是一种特殊的结,看起来像一根绕自身缠绕几圈的管子。

10

多面体几何

多面体是仅具有平面和直边的几何形状。

在此处了解有关几何形状的更多信息

Three.js - 材料

材料就像物体的皮肤。它定义了几何体的外观。Three.js 提供了许多工作材料。我们应该根据自己的需要来选择材料的类型。在本章中,我们将讨论 Three.js 中最常用的材料。

先生编号 材料及描述
1

网格基础材质

它是 Three.js 中非常基本的材料。

2

网格深度材质

它使用距相机的距离来确定如何以灰度级为网格着色。

3

网格法线材质

该材质使用面部法线向量的 x/y/z 值的大小来计算和设置面部显示颜色的红色/绿色/蓝色值。

4

MeshLambert材质

您可以使用这种材料创建暗淡、无光泽的表面。

5

MeshPhong材质

此材质类似于 MeshLambertMaterial,但可以创建更有光泽的表面。

6

网格标准材料