第十四章 Three.js与React、TypeScript和Web XR一起使用

在最后一章中,我们将深入探讨另外两个主题。首先,我们来看看如何将Three.js与TypeScript和React结合起来。本章的第二部分将展示一些如何将3D场景与WebXR集成的示例。使用Web XR,您可以增强您的场景以使用VR和AR技术。

更具体地说,我们将向您展示以下示例:

  • 将Three.js与TypeScript结合使用:在第一个示例中,我们将向您展示如何创建一个简单的项目,该项目将Three.jsp与TypeScript相结合。我们将创建一个非常简单的应用程序,与我们在前几章中看到的示例非常相似,并向您展示了如何将TypeScript与Three.js一起使用来创建场景。
  • 将Three.js和React与TypeScript结合使用:React是一个非常流行的web开发框架,经常与TypeScript一起使用。对于本节,我们将创建一个Three.js项目,它将React.js与TypeScript一起使用。
  • 使用Three.js和React使用Three..js fibers:最后,我们来看看React三光纤。有了这个库,我们可以使用一组React组件以声明方式配置Three.js。这库在React和Three.js之间提供了很好的集成,并使在React应用程序中使用Three.js变得简单。
  • Three.js和VR:本节将向您展示如何在VR中查看3D场景。
  • Three.js和AR:本节将解释如何创建一个简单的3D场景,在其中可以添加Three.js网格。

让我们从本章的第一个例子开始,将Three.js与TypeScript集成

将Three.js与TypeScript结合使用

TypeScript提供了一种可转换为JavaScript的类型化语言。这意味着你可以使用它来创建你的网站,它将像浏览器中的普通JavaScript一样运行。有很多不同的方法来设置TypeScript项目,但最简单的方法是由Vite提供的(https://vitejs.dev/)。Vite提供了一个集成的构建环境,可以被视为webpack(我们用于普通章节示例)的替代品。

我们需要做的第一件事是创建一个新的Vite项目。您可以自己完成这些步骤,也可以查看three-ts文件夹,然后在那里运行yarn install以跳过此设置。要使用Vite获得一个空的TypeScript项目,我们所要做的就是在控制台中运行以下代码:

$ yarn create vite three-ts --template vanilla-ts
yarn create v1.22.17
warning package.json: No license field
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
warning Your current version of Yarn is out of date. The latest 
version is "1.22.19", while you're on "1.22.17".
info To upgrade, run the following command:
$ curl --compressed -o- -L https://yarnpkg.com/install.sh | 
bash
success Installed "create-vite@3.2.1" with binaries:
 - create-vite
 - cva
[##############################################################
########] 70/70
Scaffolding project in /Users/jos/dev/git/personal/ltjs4-all/
three-ts...

接下来更改到目录(three-ts)并运行yarn install。

$ yarn install
yarn install v1.22.17
warning ../package.json: No license field
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
 Done in 3.31s.

在这一点上,我们有一个空的Vite项目,您可以通过运行 yarn vite 来启动它。

$ three-ts git:(main) yarn vite
yarn run v1.22.17
warning ../package.json: No license field
$ /Users/jos/dev/git/personal/ltjs4-all/three-ts/node_modules/.
bin/vite
 VITE v3.2.3 ready in 193 ms
 Local: http://127.0.0.1:5173/
 Network: use --host to expose

如果您将浏览器指向http://127.0.0.1:5173/,您将看到Vite的起始页,以及 您将有一个配置好的TypeScript项目:

image-20230605151134772

图14.1-带有Vite的空TypeScript项目

接下来,我们必须添加Three.js库,然后我们可以添加一些TypeScript来初始化Three.js.要添加Three.js,我们需要添加以下两个节点模块:

$ yarn add three
$ yarn add -D @types/three

第一个添加了Three.js库,而第二个添加了Three.js库的类型描述。在IDE中使用Three.js和TypeScript(例如,在Visual Studio code中)时,这些类型在编辑器中用于获得一些不错的代码补全。现在,我们已经准备好将Three.js添加到这个项目中,并开始使用TypeScript开发Three.jss应用程序。要添加TypeScript,我们需要做的第一件事是快速查看应用程序是如何初始化的。为此,您可以查看public.html文件,该文件如下所示:

<!DOCTYPE html>
<html lang="en">
 <head>
 <meta charset="UTF-8" />
 <link rel="icon" type="image/svg+xml" href="/vite.svg" />
 <meta name="viewport" content="width=device-width,
 initial-scale=1.0" />
 <title>Vite + TS</title>
 </head>
 <body>
 <div id="app"></div>
 <script type="module" src="/src/main.ts"></script>
 </body>
</html>

在前面的代码中,正如您在最后一行脚本中看到的那样,这个HTML页面加载src/main/ts文件。打开此文件并将其内容更改为:

import './style.css'
import { initThreeJsScene } from './threeCanvas'
const mainElement = document.querySelector
 <HTMLDivElement>('#app')
if (mainElement) {
 initThreeJsScene(mainElement)
}

此处的代码将尝试查找主#app节点。如果它找到了节点,它会将该节点传递给initThreeJsScene函数,该函数在threeCanvas.ts文件中定义。此文件包含初始化Three.js场景的代码:

import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
export const width = 500
export const height = 500
export const initThreeJsScene = (node: HTMLDivElement) => {
 const scene = new THREE.Scene()
 const camera = new THREE.PerspectiveCamera(75, height /
 width, 0.1, 1000)
 const renderer = new THREE.WebGLRenderer()
 renderer.setClearColor(0xffffff)
 renderer.setSize(height, width)
 node.appendChild(renderer.domElement)
 camera.position.z = 5
 const geometry = new THREE.BoxGeometry()
 const material = new THREE.MeshNormalMaterial()
 const cube = new THREE.Mesh(geometry, material)
 const controls = new OrbitControls(camera, node)
 scene.add(cube)
 const animate = () => {
 controls.update()
 requestAnimationFrame(animate)
 cube.rotation.x += 0.01
 cube.rotation.y += 0.01
 renderer.render(scene, camera)
 }
 animate()
}

这对于我们创建初始简单场景的前几章中的代码来说将是熟悉的。主要的变化是,在这里,我们可以使用TypeScript提供的所有功能。Vite将处理到JavaScript的转换,因此您不需要做任何其他事情就可以在浏览器中看到结果:

image-20230605151628873

图14.2-使用Three.js的简单TypeScript项目

现在我们已经介绍了Three.js和TypeScript,让我们更进一步,看看如何将其与React集成。

使用Three.js并使用TypeScript进行React

从头开始创建React应用程序有不同的方法(例如,Vite也支持这一点),但最常见的方法是从命令行使用yarn create React app lts tf--template TypeScript命令。就像Vite一样,这将创建一个新项目。对于这个例子,我们在lts-tf目录中创建了这个项目。创建后,我们必须添加Three.js库,就像我们为Vite所做的那样:

$ yarn create react-app lts-tf --template TypeScript
...
$ cd lts-tf
$ yarn add three
$ yarn add -D @types/three
$ yarn install

这应该设置一个简单的react TypeScript应用程序,添加正确的Three.js库,并安装所有其他所需的模块。下一步是快速检查所有这些是否有效。运行 yarn start 启动命令:

$ yarn start
Compiled successfully!
You can now view lts-tf in the browser.
 Local: http://localhost:3000
 On Your Network: http://192.168.68.112:3000
Note that the development build is not optimized.
To create a production build, use yarn build.
webpack compiled successfully
Files successfully emitted, waiting for typecheck results...
Issues checking in progress...
No issues found.

打开浏览器以http://localhost:3000您将看到一个简单的React启动屏幕:

image-20230605151936847

图14.3-使用Three.js的简单TypeScript项目

在这个屏幕上,我们可以看到我们需要编辑app.tsx文件,所以我们将更新它,类似于我们在Using Three.js with TypeScript部分中看到的普通TypeScript示例,但这次是作为React组件:

import './App.css'
import { ThreeCanvas } from './ThreeCanvas'
function App() {
 return (
 <div className="App">
         <ThreeCanvas></ThreeCanvas>
 </div>
 )
}
export default App

正如您所看到的,在这里,我们定义了一个名为ThreeCanvas的自定义组件,该组件现在在应用程序启动时立即加载。Three.js初始化代码由ThreeCanvaselement提供,您可以在ThreeCanvas.tsx文件中找到该元素。这个文件在很大程度上类似于我们在Using Three.js with TypeScript一节中描述的initThreeJsScene函数,但为了完整起见,我们将在这里包括整个文件:

import { useCallback, useState } from 'react'
import * as THREE from 'three'
const initThreeJsScene = (node: HTMLDivElement) => {
 const scene = new THREE.Scene()
 const camera = new THREE.PerspectiveCamera(75, 500 / 500,
 0.1, 1000)
 const renderer = new THREE.WebGLRenderer()
 renderer.setClearColor(0xffffff)
 renderer.setSize(500, 500)
 node.appendChild(renderer.domElement)
 camera.position.z = 5
 const geometry = new THREE.BoxGeometry()
 const material = new THREE.MeshNormalMaterial()
 const cube = new THREE.Mesh(geometry, material)
 scene.add(cube)
 const animate = () => {
 requestAnimationFrame(animate)
 cube.rotation.x += 0.01
 cube.rotation.y += 0.01
 renderer.render(scene, camera)
 }
 animate()
}
export const ThreeCanvas = () => {
 const [initialized, setInitialized] = useState(false)
 const threeDivRef = useCallback(
 (node: HTMLDivElement | null) => {
 if (node !== null && !initialized) {
 initThreeJsScene(node)
 setInitialized(true)
 }
 },
 [initialized]
 )
 return (
 <div
 style={{
 display: 'flex',
 alignItems: 'center',
 justifyContent: 'center',
 height: '100vh'
 }}
 ref={threeDivRef}
 ></div>
 )
}

在initThreeJsScene中,可以找到使用TypeScript初始化简单Three.js场景的标准代码。要将这个Three.js场景连接到React,我们可以使用ThreeCanvasfunctional React组件中的代码。这里我们要做的是在div元素附加到其父节点的那一刻初始化Three.js场景。为此,我们可以使用useCallback函数。当此节点附加到其父节点时,此函数将被调用一次,即使其中一个父属性发生更改,也不会重新运行。在我们的例子中,我们还将添加另一个isInitialized状态,以确保即使我们让开发服务器重新加载应用程序的部分,我们也只初始化Three.js场景一次。

useRef或useCallback

您可能会想在此处使用useRef。有一个很好的解释https://reactjs.org/docs/hooks-faq.html#how-can-i-measure-a-dom-noderegarding为什么在这种情况下,应该使用useCallback而不是useRef来避免不必要的重新渲染。

有了前面的设置,我们现在可以看到结果:

image-20230605152430123

图14.4–具有TypeScript和React的Three.js

在前面的例子中,我们在React和Three.js之间创建了一个简单的集成。虽然这很有效,但以编程方式描述Three.js场景感觉有点奇怪,因为在React中,应用程序通常是使用组件声明的。我们可以像使用ThreeCanvas组件那样包装现有的Three.js组件,但这会很快变得复杂。幸运的是,Three.js fibers 项目已经完成了所有的艰苦工作:https://docs.pmnd.rs/react-three-fiber/getting-started/introduction.在下一节中,我们将看看借助这个项目可以多么容易地集成Three.js和React。

使用Three.js和React Three Fiber进行React

在前面的例子中,我们自己设置了React和Three.js之间的集成。虽然它有效,但这种方法并没有与React的工作方式紧密结合。为了在这些框架之间进行良好的集成,我们可以使用React Three Fiber。我们将通过建立一个项目重新开始。

为此,请运行以下命令:

$ yarn create react-app lts-r3f
$ cd lts-3rf
$ yarn install
$ yarn add three
$ yarn add @react-three/fiber

这将安装我们需要的所有依赖项,并建立一个新的React项目。要在lts-r3f目录中启动此项目,请运行yarn start,它将启动一个服务器。打开您在屏幕上看到的URL(http://localhost:3000);你会看到下面的屏幕,我们之前看到过,它显示了一个空的React项目:

image-20230605152830176

图14.5–启动一个简单的JavaScriptReact应用程序

当屏幕开始扩展这个例子时,我们需要编辑app.jsx文件。我们将创建一个包含Three.js场景的新组件:

import './App.css'
import { Canvas } from '@react-three/fiber'
import { Scene } from './Scene'
function App() {
 return (
 <Canvas>
 <Scene />
 </Canvas>
 )
}
export default App

在这里,我们已经可以看到 Three Fiber 组件中的第一个——Canvas元素。Canvaselement创建了一个Canvas div,并且是该库提供的所有其他Three.js组件的父容器。由于我们将Scene作为子项添加到此Canvas组件,因此我们可以在自定义组件中定义完整的Three.js场景。接下来,我们将创建此场景组件:

import React from 'react'
export const Scene = () => {
 return (
 <>
 <ambientLight intensity={0.1} />
 <directionalLight color="white" intensity={0.2}
 position={[0, 0, 5]} />
 <mesh
 rotation={[0.3, 0.6, 0.3]}>
 <boxGeometry args={[2, 5, 1]} />
 <meshStandardMaterial color={color}
 opacity={opacity} transparent={true} />
 </mesh>
 </>
 )
}

我们在这里看到的是一个非常简单的Three.js场景,它看起来与我们在本书前面看到的场景相似。此场景包含以下对象:

  • <ambientLight>:Three.ambientLight对象的实例。
  • <directionalLight>:Three.directionalLight对象的实例。
  • <mesh>:这表示Three.mesh。正如我们所知,Three.mesh包含一个几何体和一种材质,它们被定义为该元素的子元素。在本例中,我们设置了这个网格上的旋转。
  • <boxGeometry>:这类似于Three.boxGeometry,我们通过args属性传入构造函数参数。
  • <meshStandardMaterial>:这将创建THREE.meshStandardMaterial的实例,并配置该材质上的一些属性。

现在,当您将浏览器打开到localhost:3000时,您将看到Three.js场景:

image-20230605153227262

图14.6–使用React Three Fiber渲染的Three.js场景

在这个例子中,我们只展示了React Three Fiber提供的几个小元素。Three.js提供的所有对象都可以按照我们刚才展示的方式进行配置。只需将它们作为元素添加,配置它们,它们就会显示在屏幕上。除了容易显示这些元素外,所有这些元素的行为都像正常的React组件。因此,每当父元素的属性发生变化时,所有元素都会被重新绘制(或更新)。

除了React Three Fiber提供的元件外,@React Three/drei还提供了一整套附加组件。您可以在上找到这些组件及其说明https://github.com/pmndrs/drei:

image-20230605153650466

图14.7–来自@react three/drei的附加组件

对于下一个示例,我们将使用该库提供的几个组件,因此我们需要将其添加到我们的项目中:

$ yarn add @react-three/drei

现在,我们将把我们的例子扩展到:

import React, { useState } from 'react'
import './App.css'
import { OrbitControls, Sky } from '@react-three/drei'
import { useFrame } from '@react-three/fiber'
export const Scene = () => {
 // 在react的每个渲染上运行
 // const size = useThree((state) => state.size)
 const mesh = React.useRef()
 const [color, setColor] = useState('red')
 const [opacity, setOpacity] = useState(1)
 const [isRotating, setIsRotating] = useState(false)
 // 在的每个转发器上运行
 useFrame(({ clock }, delta, xrFrame) => {
 if (isRotating) mesh.current.rotation.x += 0.01
 })
 return (
 <>
 <Sky distance={450000} sunPosition={[0, 1, 0]}
 inclination={0} azimuth={0.25} />
 <ambientLight intensity={0.1} />
 <directionalLight color="white" intensity={0.2}
 position={[0, 0, 5]} />
 <OrbitControls></OrbitControls>
 <mesh
 ref={mesh}
 rotation={[0.3, 0.6, 0.3]}
 onClick={() => setColor('yellow')}
 onPointerEnter={() => {
 setOpacity(0.5)
 setIsRotating(true)
 }}
 onPointerLeave={() => {
 setOpacity(1)
 setIsRotating(false)
 }}
 >
 <boxGeometry args={[2, 5, 1]} />
 <meshStandardMaterial color={color}
 opacity={opacity} transparent={true} />
 </mesh>
  </>
 )
}

在浏览器中查看结果之前,让我们先浏览一下代码。首先,我们将查看添加到组件中的新元素:

  • <OrbitControls>:这是 drei 库提供的。这会将THREE.OrbitControls元素添加到场景中。这与我们在前几章中使用的OrbitControls相同。正如您在这里看到的,仅仅添加元素就足够了;不需要额外的配置。
  • <Sky>:这个元素为场景提供了一个不错的天空背景。我们还添加了几个标准的React Hook:

我们还添加了几个标准的React钩子:

const mesh = React.useRef()
 const [color, setColor] = useState('red')
 const [opacity, setOpacity] = useState(1)
 const [isRotating, setIsRotating] = useState(false)

在这里,我们定义了一个Ref,我们将其连接到网格(<mesh-Ref={mesh})..>)。我们使用它是为了稍后在渲染循环中访问Three.js组件。我们还使用useState三次来跟踪材质的颜色和不透明度状态值,以及查看网格属性是否正在旋转。这两个钩子中的第一个用于我们在网格上定义的事件:

<mesh
 onClick={() => setColor('yellow')}
 onPointerEnter={() => {
 setOpacity(0.5)
 setIsRotating(true)
 }}
 onPointerLeave={() => {
 setOpacity(1)
 setIsRotating(false)
 }}>

有了这些事件处理程序,我们可以很容易地将鼠标与网格集成在一起。不需要RayCaster对象,只需添加事件侦听器即可完成。在这种情况下,当鼠标指针进入网格时,我们会更改不透明度状态值和isRotation标志。当鼠标离开网格时,我们将不透明度状态值设置回,并将isRotation标志再次设置为false。最后,当我们单击网格时,我们将颜色更改为黄色。

颜色和不透明度状态值可以直接在meshStandardMaterial中使用,如下所示:

<meshStandardMaterial color={color} opacity={opacity} transparent={true} />

现在,每当我们触发相关事件时,不透明度和颜色都会自动更新。对于旋转,我们希望使用Three.js渲染循环。为此,React Three Fiber提供了一个额外的挂钩:

useFrame(({ clock }, delta, xrFrame) => {
 if (isRotating) mesh.current.rotation.x += 0.01
 })

每当我们在Three.js中有渲染循环时,就会调用useFrame。在这种情况下,我们检查isRotatingstate,如果我们应该旋转,我们会使用之前定义的useRef引用来访问底层Three.jss组件,并简单地增加其旋转。这一切都非常简单方便。浏览器中的结果如下所示:

image-20230605154839828

图14.8–使用React和React Three Fiber 效果的场景

React Three Fiber和drei库提供了普通Three.js库中几乎所有的功能(以及一些不可用的功能)。如果您正在使用React,并且需要集成Three.js,这是使用Three.js的好方法。即使您不一定要构建React应用程序,React Three Fiber提供的定义场景、组件和交互的声明方式也非常直观。React Three Fiber为您想要创建的任何Three.js可视化提供了一个很好的替代方案。

在接下来的两节中,我们将介绍如何使用AR和VR功能扩展3D场景。我们将从如何在您的场景中启用虚拟现实开始。

Three.js 和VR

在我们查看所需的代码更改之前,我们将为浏览器添加一个扩展,用它我们可以模拟VR耳机和VR控件。这样,您就可以在不需要物理耳机和物理控制器的情况下测试场景。为此,我们将安装WebXR API模拟器此插件可用于Firefox和Chrome:

  • Firefox plugin:从这里下载并安装它: https://addons.mozilla.org/en-US/firefox/addon/webxr-api-emulator/
  • Chrome plugin:从这里下载并安装它:https://chrome.google.com/webstore/detail/webxr-api-emulator/mjddjgeghkdijejnciaefnkjmkafnnje

按照特定浏览器的说明进行操作。安装后,我们可以使用以下示例对其进行测试:https://immersive-web.github.io/webxr-samples/immersive-vr-session.html。

打开此示例,打开开发人员控制台,然后单击WebXR选项卡。现在,您将看到如下内容:

image-20230605155753089

图14.9–带有WebXR API扩展的 Chrome 浏览器

在扩展中,您将看到一个虚拟耳机和一些虚拟VR控件。通过点击耳机,您可以模拟真实VR耳机的运动;这同样适用于控件。如果你点击Enter VR 按钮,你现在可以简单地测试你的VR场景,而不需要实际的VR耳机:

image-20230605155928814

图14.10–模拟VR耳机

现在我们有了一个(虚拟)耳机可以玩,让我们将之前的一个场景转换为VR场景,在那里我们可以跟踪头部运动,并为一些虚拟VR控件添加功能。为此,我们创建了第8章“创建和加载高级网格和几何体”中“第一人称控制”示例的副本。您可以通过打开第14章源代码中的vr.html示例来打开此示例:

image-20230605160137778

图14.11 基于第9章示例的虚拟现实场景

要准备好您的场景VR,我们需要采取几个步骤。我们需要做的第一件事是告诉Three.js我们将启用Web XR功能。可以这样做:

renderer.xr.enabled = true

下一步是添加一个简单的按钮,我们可以点击该按钮进入VR模式。Three.js提供了一个开箱即用的组件,我们可以这样使用:

import { VRButton } from 'three/examples/jsm/webxr/VRButton'
document.body.appendChild(VRButton.createButton(renderer))

这将创建一个按钮,您可以在图14.11中的屏幕底部看到该按钮。

最后,我们需要更新渲染循环。您可能还记得,在第1章“用Three.js创建第一个3D场景”中,我们使用requestAnimationFrame来控制渲染循环。在使用VR时,我们需要稍微改变一下,如下所示:

animate()
function animate() {
 renderer.setAnimationLoop(animate)
 renderer.render(scene, camera)
 if (onRender) onRender(clock, controls, camera, scene)
}

在这里,我们使用了render.setAnimationLoop而不是requestAnimationFrame。此时,我们的场景已经转换为VR,一旦我们点击按钮,我们就进入VR模式,可以环视我们的场景:

image-20230605160559573

图14.12-进入VR模式并使用浏览器扩展旋转相机

当您进入VR时,会显示上一张屏幕截图。现在,您可以通过点击Web XR扩展中的VR设备来轻松地移动相机。这些步骤几乎就是将任何Three.js场景转换为VR场景所需的全部步骤。

如果你仔细观察图14.12,你可能会注意到我们还展示了一些手持VR设备。我们还没有展示如何添加这些。为此,Three.js还附带了一些不错的辅助组件:

import { XRControllerModelFactory } from 'three/examples/jsm/webxr/XRControllerModelFactory'
const controllerModelFactory = new XRControllerModelFactory()
const controllerGrip1 = renderer.xr.getControllerGrip(0)
controllerGrip1.add(controllerModelFactory.
createControllerModel(controllerGrip1))
scene.add(controllerGrip1)
const controllerGrip2 = renderer.xr.getControllerGrip(1)
controllerGrip2.add(controllerModelFactory.
createControllerModel(controllerGrip2))
scene.add(controllerGrip2)

在前面的代码中,我们要求Three.js获取有关附加控制器的信息,创建一个模型,并将它们添加到场景中。如果您使用WebXR API模拟器,您可以移动控件,它们也会在场景中移动。

Three.js提供了许多示例,说明如何使用这些控件来拖动对象、选择场景中的对象,以及以其他方式添加与控件的交互。对于这个简单的例子,我们添加了一个选项,每当您单击选择按钮时,都可以在第一个控件(右边的控件)的位置添加一个立方体:

image-20230605161902971

图14.13-在虚拟现实中向场景添加立方体

我们可以通过简单地将事件侦听器添加到控制器来实现这一点,如下所示:

const controller = renderer.xr.getController(0)
controller.addEventListener('selectstart', () => {
 console.log('start', controller)
 const mesh = new THREE.Mesh(new THREE.BoxGeometry(0.1,
 0.1, 0.1), new THREE.MeshNormalMaterial())
 mesh.position.copy(controller.position)
 scene.add(mesh)
})
controller.addEventListener('selectend', () => {
 console.log('end', controller)
})

在这段简单的代码中,您可以看到我们向控制器添加了两个事件侦听器。当selectstart事件被触发时,我们将向该控制器的位置添加一个新的多维数据集。当selected事件被触发时,我们只需将一些信息记录到控制台。其他一些事件可以通过JavaScript访问。有关VR会话中可用的API的更多信息,您可以查看以下文档:https://developer.mozilla.org/en-US/docs/Web/API/XRSession

在最后一节中,我们将快速了解如何将Three.js与AR结合起来。

Three.js and AR

虽然使用Three.js的VR在各种设备和浏览器上都得到了很好的支持,但Web-AR的情况并非如此。在安卓系统上,它的支持非常好,但在iOS设备上,它运行得不太好。苹果目前正在将其添加到Safari中,因此一旦加入,原生AR也应该在iOS上运行。检查哪些浏览器支持此功能的一个好方法是检查https://caniuse.com/webxr,提供了所有主要浏览器支持的最新概述。

因此,要测试原生AR示例,您要么需要在Android设备上查看它,要么使用我们在Three.js和VR部分中使用的相同模拟器。

让我们创建一个标准场景,作为AR实验的起点。我们需要做的第一件事是告诉Three.js我们想使用XR:

const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true })
renderer.xr.enabled = true

请注意,我们需要将alpha属性设置为true;否则,我们将看不到任何通过相机。接下来,就像我们对VR所做的那样,我们需要通过调用rendered.xr.enabled在渲染器上启用AR/VR。

要进入AR模式,Three.js还提供了一个按钮,我们可以使用:

Import { ARButton } from 'three/examples/jsm/webxr/ARButton'
document.body.appendChild(ARButton.createButton(renderer))

最后,我们只需要将requestAnimationFrame更改为setAnimationLoop:

animate()
function animate() {
 renderer.setAnimationLoop(animate)
 renderer.render(scene, camera)
}

这就是它的全部内容。如果你打开ar.html示例并通过WebXR插件(你需要在其中选择三星Galaxy S8+(ar)设备)查看此示例,你会看到如下内容:

image-20230605163339530

图14.14-使用设备的原生AR功能在Three.js中查看AR场景

在这个屏幕截图中,您可以看到一个模拟的AR环境,在那里我们可以看到我们渲染的两个对象。如果你移动模拟手机,你会注意到渲染的对象相对于手机摄像头的位置是固定的。

这里的例子非常简单,但它展示了如何设置最小AR场景的基本知识。WebXR提供了许多与AR相关的其他功能,例如检测飞机和命中测试。然而,涵盖这一点有点超出了本书的范围。有关Web-XR和此API公开的本机AR功能的更多信息,您可以查看以下规范:https://developer.mozilla.org/en-US/docs/Web/API/WebXR_Device_API/Fundamentals.

总结

在本章中,我们介绍了与Three.js相关的一些技术。我们向您展示了将Three.jss与TypeScript和React集成的不同方法,还向您演示了如何创建一些基本的AR和VR场景。

通过使用Three.js TypeScript绑定,您可以轻松地从TypeScript项目访问Three.js的所有功能。通过React Three Fiber库可以轻松地将Three.js与React集成。

在Three.js中使用VR和AR也非常简单。只需在主渲染器中添加几个属性,就可以将任何场景快速转换为VR或AR场景。请记住,使用浏览器插件可以轻松测试您的场景,而无需实际的VR和AR设备。

至此,我们就到了这本书的结尾。我希望你喜欢读这本书,喜欢玩这些例子。试验愉快!