如何在 react-three-fiber/drei/Three.js 中重复使用相同的 GLTF 模型?

7
在这个精简的 react-three-fiber 应用中,我正在尝试加载和包含同一个 GLTF 模型两次:
import React, { Suspense } from "react";
import { Canvas } from "@react-three/fiber";
import { useGLTF } from "@react-three/drei";

function MyContent() {
  const firstGltf = useGLTF("/eye/scene.gltf");
  const secondGltf = useGLTF("/eye/scene.gltf");

  return (
    <>
      <primitive object={firstGltf.scene} position={[-200, 0, -400]} />
      <primitive object={secondGltf.scene} position={[200, 0, -400]} />
    </>
  );
}

export default function App() {
  return (
    <Canvas>
      <ambientLight color="white" intensity={0.5} />

      <Suspense fallback={null}>
        <MyContent />
      </Suspense>
    </Canvas>
  );
}

请看这个codesandbox
然而,只有第二个<primitive>是可见的。如果我删除第二个<primitive>,那么第一个就会出现。
我很难理解为什么会这样发生,以及如何更好地处理它。(这是因为第二次调用useGLTF时记住了已经加载了"/eye/scene.gltf",并返回相同的对象吗?这是否会在使用<primitive>时出现问题,也许是因为材质/几何体没有第二次重新创建,只存在一次?)
特别是,这是我想要实现的:
  • 在我的画布上多次使用gltf模型
  • 理想情况下,只加载一次gltf
此外,也许你可以帮我澄清一下这些问题,这样我就能更好地理解到底发生了什么:
  • 既然我只需要一个3D模型,那么使用gltf.scene对象是否是正确的方法?
  • <primitive>是否是显示3D模型的正确方法?或者我应该从场景中提取几何体/纹理并呈现它们?
谢谢!
3个回答

6

我不是three.js的专家,只是根据我找到的资料尝试回答你的问题。


1.即使定义了2个原语,也只显示一个眼睛

如果使用useGLTF()导入相同的模型,则会引用相同的对象。因此,这2个原语指向同一个gltf,只应用最后/一个配置。

const firstGltf = useGLTF("/eye/scene.gltf");
const secondGltf = useGLTF("/eye/scene.gltf");
const glassesGltf = useGLTF("/glasses/scene.gltf");

// for demonstrating first eye is same as second eye
// Output: false, true
console.log(firstGltf === glassesGltf, firstGltf === secondGltf);

2. <primitive>是否是显示3D模型的正确方法?

是的,它是。但是,如果您想多次将相同的gltf显示到屏幕上,则需要创建网格并应用模型的几何和材质,以便您可以获得新的对象。

function Model(props) {
  const { nodes, materials } = useGLTF("/eye/scene.gltf");
  return (
    <group
      {...props}
      dispose={null}
      rotation={[Math.PI, 0, -Math.PI / 2]}
      scale={[1, 1, 1]}
    >
      <mesh
        geometry={nodes.Sphere001_Eye_0.geometry}
        material={materials.material}
      />
    </group>
  );
}
...
<Model position={[-1, 0, 1]} />
<Model position={[1, 0, 1]} />

这里是演示的 codesandbox


注意:

你可以使用这个库https://github.com/pmndrs/gltfjsx从模型中生成jsx。


1
这种方法比上面描述的深度克隆方法更高效吗? - Eric Johnson

3
您不能在WebGL/threejs中重复使用网格或将相同的对象放入场景中两次,否则它将被卸载和重新加载。您可以选择以下两种方法之一:
  1. 共享几何体:请参考此示例https://codesandbox.io/s/re-using-gltfs-dix1y?file=/src/Shoe.js:48-55
  2. 或深度克隆基本对象
以下是如何使用useMemo深度克隆基本对象的方法:
interface ObjectProps {
  url: string;
  position: [x: number, y: number, z: number];
}

const Object = ({ url, position, ...props }: ObjectProps) => {
  const { scene } = useLoader(GLTFLoader, url)
  const copiedScene = useMemo(() => scene.clone(), [scene])

  return (
    <group>
      <primitive object={copiedScene} position={position} />
    </group>
  );
};

参考:https://github.com/pmndrs/react-three-fiber/issues/245#issuecomment-745552102

这是关于在React Three Fiber中如何使用GLTFLoader的问题。你需要安装three和GLTFLoader依赖项,并在代码中导入它们。然后,你可以使用useLoader钩子将GLTF文件加载到场景中。最后,你可以对模型进行任何必要的调整和变换。

运作良好。我需要能够创建多个场景实例,而不知道几何图形可能是什么。 - Lucas Bazemore

0
也许你可以使用 '@react-three/drei' 中的 Clone
<>
  <Clone object={firstGltf.scene} position={[-200, 0, -400]} />
  <Clone object={secondGltf.scene} position={[200, 0, -400]} />
</>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接