使用 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>
	);
};
bannerAds