使用 React Three Fiber + TypeScript:在三维空间中旋转立方体
React Three Fiber是一个库,它通过React组件来构建three.js的三维空间,并且可以操作3D对象。通过React的声明性描述,可以轻松实现基于WebGL的3D表现。以下是一个示例,展示如何在三维空间中旋转立方体。相机可以通过鼠标进行交互操作。
以下是本稿的示例作品:https://codesandbox.io/s/react-three-fiber-typescript-basic-example-dnzllu?file=/src/App.tsx:579-605
用鼠标操作相机
-
- ドラッグ: 立方体の中心から一定の距離で旋回する。
-
- 右ボタンドラッグ: カメラの方向は保ったまま、フレーミングを水平・垂直に移動させる。
- ホイール: カメラの方向に、映像を拡大・縮小させる。
创建和安装模板项目
首先,我们需要准备工作。我们将使用Create React App来创建项目的模板(我们将项目命名为react-three-fiber-typescript)。在模板(–template)的选项中,我们添加了TypeScript。虽然我们几乎不需要进行显式的类型注释,因为类型推断已经起作用了。
npx create-react-app react-three-fiber-typescript --template typescript
然后,安装three.js(简称three)和React Three Fiber(简称@react-three/fiber)。还要加上@types/three。
npm install three @react-three/fiber
npm install --save-dev @types/three
在三维空间中添加立方体
要在纯粹的JavaScript中使用three.js搭建一个3D空间的舞台,首先需要创建以下三个对象。
-
- シーン(scene)
3次元空間を表し、3Dオブジェクトやライトが加えられる。
カメラ(camera)
3次元空間を映し、レンダラーに送って画面に投影する。
レンダラー(renderer)
カメラから受け取った画像を、画面の再描画のたびに更新する。
在React Three Fiber中,只需要一个Canvas组件就可以完成。在下面的代码(src/App.tsx)中,我们通过camera属性添加了相机的设定。但是,默认设置已经被设置好了,所以即使不包括相机的设定,三维空间的准备工作也已经完成了。接下来,我们将把立方体(Cube)作为Canvas的子组件来定制。
import React from 'react';
import { Canvas } from '@react-three/fiber';
import { Cube } from './Cube';
function App() {
return (
<div className="App">
<Canvas
camera={{
fov: 45,
near: 0.1,
far: 1000,
position: [0, 0, 5]
}}
>
<Cube />
</Canvas>
</div>
);
}
export default App;
以下是camera属性中规定的项目内容。要进行此类设置,您需要参考three.js的文档。关于使用基于标准JavaScript的three.js的基础知识,请参考”three.js + TypeScript:在3D空间中旋转立方体”。
fov: 視野(field of view)またはカメラの用語で画角。写される光景の範囲を角度(度数)で示す。数字が大きいと広角で、写る範囲は広がるかわり、対象物は相対的に小さくなる。小さい角度は望遠に近づき、対象物が相対的に大きく写る。
aspect: アスペクト比で、描画する矩形領域の $ \frac{幅}{高さ} $ 。矩形領域の比と合わなければ、画像の水平・垂直比が歪む。
near: オブジェクトを描画するもっとも近い距離。これより近くのオブジェクトは画面に描かれない。
far: オブジェクトを描画するもっとも遠い距離。これより遠くのオブジェクトは画面に描かれない。
在中文中,用一种方式来重新表述以下内容:
定义3D对象的是mesh组件。其中必须包括确定形状的几何体和表面材料的材质。可以使用boxBufferGeometry来创建立方体。我选择了meshPhongMaterial作为材质。默认情况下,3D对象会被放置在三维空间的原点(0, 0, 0)处。
在下面的代码示例中,将一个数组作为args属性传递给了boxBufferGeometry。这是为了传递给three.js的BoxBufferGeometry()构造函数的参数的意思。它确定了立方体的宽度、高度和深度。值得注意的是,在three.js中,距离和长度是以1为基础的数值。在相机设置中,对象可以被放大或缩小,所以单位不是很重要,只需要将其视为比例即可。
使用的材料是three.js中的MeshPhongMaterial。表面具有镜面反射的光泽。颜色属性可以像CSS一样指定颜色,如果是十六进制,则可以使用数字。
import { FC } from 'react';
export const Cube: FC = () => {
return (
<mesh>
<boxBufferGeometry args={[1, 1, 1]} />
<meshPhongMaterial color="aqua" />
</mesh>
);
};
当您运行该应用程序时,您将看到立方体是完全黑色的。这是因为没有光源。不要被默认的白色背景所迷惑。如果没有在三维空间中添加光源,您将无法看到3D对象的正确外观。
向物体照射平行光源
首先要添加的是directionalLight(DirectionalLight)作为光源。它也被称为“平行光源”,就像太阳光一样,光线从一个方向垂直照射进来。position属性确定光线的方向。由于它是朝向原点的,因此坐标(1,1,1)在直线方向上连接原点(0,0,0)和立方体初始位置的前右上角坐标(0.5,0.5,0.5)(右上斜向前方)。intensity属性的数值表示光线的亮度,1代表100%的亮度。
function App() {
return (
<div className="App">
<Canvas
>
<directionalLight position={[1, 1, 1]} intensity={0.8} />
</Canvas>
</div>
);
}
现在,应该已经显示出指定颜色(aqua)的3D物体了。不过,由于摄像机正对着它,无法区分是立方体还是正方形平面。
给立方体添加旋转动画
让我们使用动画来旋转立方体。在此过程中,我们将使用React Three Fiber的useFrame钩子。我们将通过给立方体的mesh组件分配ref属性来准备好它。rotation属性将向x和y轴添加旋转(单位为弧度)。我们指定的回调函数将在每次重新渲染时被调用。您将能够确认立方体正在旋转。
import { useFrame } from '@react-three/fiber';
// import { FC } from 'react';
import { FC, useRef } from 'react';
import type { Mesh } from 'three';
export const Cube: FC = () => {
const cubeRef = useRef<Mesh>(null);
useFrame(() => {
const cube = cubeRef.current;
if (!cube) return;
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
});
return (
// <mesh>
<mesh ref={cubeRef}>
</mesh>
);
};
添加环境光
也许还有一点点担心的是,那是因为没有光照到的部分是漆黑的。从物理角度来考虑,这是不可避免的。但是,如果与现实相比较,会感到不协调。这里使用的光源是ambientLight组件。args属性的数组用于在AmbientLight()构造函数中确定光的颜色。亮度的intensity属性值设置得比较低调。
function App() {
return (
<div className="App">
<Canvas
>
<ambientLight args={[0xffffff]} intensity={0.2} />
</Canvas>
</div>
);
}
立方体的阴影面也增加了亮度。
使用OrbitControls以鼠标控制相机。
使用OrbitControls可以方便地通过鼠标控制相机。请将其作为单独的模块安装,与@react-three/fiber分开。
npm install @react-three/drei
将组件OrbitControls添加到Canvas中即可,使用方法很简单。鼠标控制的基本功能已经默认包含在内。
import { OrbitControls } from '@react-three/drei';
function App() {
return (
<div className="App">
<Canvas
>
<OrbitControls />
</Canvas>
</div>
);
}
這樣,我們已經完成了前面所介紹的範例。我們把兩個模組的整個描述放到了下一個代碼块001中。令人驚訝的是,除了JSX之外,幾乎沒有JavaScript代碼。
用鼠标控制相机,将三维空间中的立方体旋转。
import { Canvas } from '@react-three/fiber';
import { OrbitControls } from '@react-three/drei';
import { Cube } from './Cube';
function App() {
return (
<div className="App">
<Canvas
camera={{
fov: 45,
near: 0.1,
far: 1000,
position: [0, 0, 5]
}}
>
<Cube />
<ambientLight args={[0xffffff]} intensity={0.2} />
<directionalLight position={[1, 1, 1]} intensity={0.8} />
<OrbitControls />
</Canvas>
</div>
);
}
export default App;
import { useFrame } from '@react-three/fiber';
import { FC, useRef } from 'react';
import type { Mesh } from 'three';
export const Cube: FC = () => {
const cubeRef = useRef<Mesh>(null);
useFrame(() => {
const cube = cubeRef.current;
if (!cube) return;
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
});
return (
<mesh ref={cubeRef}>
<boxBufferGeometry args={[1, 1, 1]} />
<meshPhongMaterial color="aqua" />
</mesh>
);
};