Three Fiber渲染器在React 中渲染3D模型

Three.js是一个3D渲染/动画库,为了在画布上显示模型它结合了WebGL。如果你有兴趣深入了解Three.js为JavaScript环境提供的功能,可以参考我之前写的有关文章。不过,在本文中,我们将介绍一个能进一步简化Three.js在环境中所使用的库,即React Native。

今天我会提供一个速成演示,用于展示其通过React Native Three Fiber组件来渲染3D模型的简化功能。那现在就开始吧。

设置场景

在开始之前,我们首先需要安装以下核心依赖项。

npm i react react-dom react-three-fiber three drei

在网络环境中渲染3D模型的前提是必须要有一个专门为模型渲染而设置的场景。让我们从创建一个基本的反应组件开始,它将随着我们的操作进展来存储我们的工作。

rt React, { useRef } from "react";
import { Canvas, useLoader, useFrame } from "react-three-fiber";

export default function App() {
  return (
    <div style={{
      height: innerHeight
    }}>
    </div>
  );
};

export default App;

但如何创建场景呢?网站上的3D模型实际上是从画布上投影出来的,所以我们应该从这里入手,即通过调用由React NativeThree Fiber库提供的组件来实现。 现在,如果想看一下我们的app,我们会看到一个白色的窗口。


由于白色背景的原因,这导致我们网页上的场景看起来就好像没有被渲染过一样。

渲染一个3D形状

现在我们已经在网页上成功渲染了场景,接下来,我们需要再添加一个基本形状。为了渲染这个形状,我们首先要创建一个新的函数,用来保存形状的属性。

function Mesh() {
  return (
    <mesh rotation={[0, 0, 0]}>
      <sphereGeometry attach="geometry" args={[1, 16, 16]} />
      <meshStandardMaterial
        attach="material"
        color="blue"
      />
    </mesh>
  );
}

在上面的代码中,我们正在返回创建基本形状所需的HTML。ThreeJS中的形状被称为网格,它在Geometries的计算数据中持有所有3D对象的属性。所以,我们现在就可以通过在canvas场景内声明我们刚刚制作的< Mesh />函数来渲染我们的3D模型。

export default function App() {
  return (
    <div style={{
      height: innerHeight
    }}>
    <Canvas>
      <OrbitControls/>
      <directionalLight intensity={0.5} />
      <ambientLight intensity={0.5} />
      <spotLight position={[10, 15, 10]} angle={0.9} />
      <Mesh />
    </Canvas>
    </div>
  );
};

现在我们的网页看起来如下图:

这不是我们真正想要的结果,但这并不意味着我们的形状没有被正常渲染,我们只是没有添加任何照明,所以形状被完全包裹在黑暗中了。我们可以们通过为HTML添加照明和聚光灯来改变这种情况:

让我们来分析一下以上代码片断:

在以上代码中,我们正在声明 ambientLight ,以在周围的场景中创建一个光源,并根据此方向性光源,使环境光照在一个特定的方向。最后一个组件是 spotLight ,它将在环境中渲染一个聚光灯,它将在特定的位置/角度照耀物体。最后,我们添加了OrbitControls,允许光标在3D环境中移动/旋转。

现在来看看我们渲染的对象:

看起来好多了,渲染对象现在已经被我们制作的光照亮了。现在,如果我们想,我们可以将网格的形状改为Three.js文档中提供的众多网格之一,以及物体的材料。

导入预制网格

现在,我们完成了场景实例的创建,并在该场景中用适当的灯光渲染了一个3D模型。然而,如果我们想要进一步操作,即从网上导入一个模型到我们的本地环境中,会怎么样呢?为了达到这个目的,我们需要向组件导入新的依赖项,不用担心,这不需要安装任何新的包。

接下来看看如果我们想把这个表导入场景中:

为了将这个表导入环境中,我们必须先创建一个新的函数。在这个新函数的范围内,应该包含叠加的已导入的3D对象的纹理,作为变量,同时还有一个组来持久化3D对象。此外,你还会注意到对GLTFLoader的调用,这是一个与Three.js链接的导入包,它允许从GLTF文件中检索数据。

function Table() {
  // textures from the imported image
  const texture = useLoader(THREE.TextureLoader, img)
  const texture1 = useLoader(THREE.TextureLoader, img1)
  const texture2 = useLoader(THREE.TextureLoader, img2)
  const group = useRef();
  // loading the table.gtlf file being imported into the component.
  const { nodes } = useLoader(GLTFLoader, table);
  // useFrame will run outside of react in animation frames to optimize updates.
  useFrame(() => {
    group.current.rotation.x = 5.09;
  });
  return (
    // Add a ref to the group. This gives us a hook to manipulate the properties of this geometry in the useFrame callback.
    <group ref={group} position={[-12, -20, -10]} >
      <mesh visible geometry={nodes.mesh_1.geometry}>
      <meshPhongMaterial attach="material" color="gold" map={texture} map={texture1} map={texture2}/>
      </mesh>
      <mesh visible geometry={nodes.mesh_0.geometry}>
      <meshPhongMaterial attach="material" color="#795C34" map={texture} map={texture1} map={texture2}/>
      </mesh>
    </group>
  );
}

在以上代码片断中,Mesh组件还接收了一个新的选项,geometry。这个选项将渲染导入范围内的任何模型的几何图形。此外,Mesh材质标签还包含一个纹理选项,它将把任何纹理图像应用到特定的网格表面。

function Loading() {
  return (
    <mesh rotation={[0, 0, 0]}>
      <sphereGeometry attach="geometry" args={[1, 16, 16]} />
      <meshStandardMaterial
        attach="material"
        color="white"
        transparent
        opacity={0.6}
        roughness={1}
        metalness={0}
      />
    </mesh>
  );
}

export default function App() {
  return (
    <div style={{
      height: innerHeight
    }}>
    <Canvas>
      <OrbitControls/>
      <directionalLight intensity={0.5} />
      <ambientLight intensity={0.5} />
      <spotLight position={[10, 15, 10]} angle={0.9} />
      <Suspense fallback={<Loading />}>
        <Table />
      </Suspense>
    </Canvas>
    </div>
  );
}

最后,如果要进行网格异步渲染的话,我们希望有一个备份模型来代替我们试图导入的3D对象。这可以通过创建一个新的网格函数作为占位符来实现,并在导入的网格周围加一个悬念法。如果组件范围内的Mesh在调用时没有执行,suspense方法将调用备份的Mesh(即fallback选项中的Mesh)。

结论

通过本文,我们介绍了什么是Three.js库,以及Three Fiber如何在React Native环境中渲染3D模型,同时还介绍了如何从外部来源导入3D模型到自己的场景中。希望这篇博客能对你有所帮助,并可供未来的、对将3D模型/动画纳入他们的网站感到兴趣的开发者使用。

原文作者 Ben Yoss
原文链接 https://levelup.gitconnected.com/rendering-3d-objects-and-animation-in-react-using-three-fiber-bf0255e642be