Zach
Zach

Reputation: 468

Three.js and React-three-fiber model is completely black?

This model seems to load completely black - but others do not. https://d1iczm3wxxz9zd.cloudfront.net/e4a5c9ee-9fa0-46b2-9ff5-8e52e6286a3b/5ee3baf2-2524-4617-99a7-2a91b4bd20c1/11/ITEM_PREVIEW1.glb

Anyone happen to know why?

Other GLB files have no issues with textures. This one seems to have an issue. But when loaded in other online GLB viewers it loads fine.

Using reactjs, react-three-fibre and three.js.

Here is the three.js code:

import * as THREE from "three";
import React, { useRef, Suspense } from "react";
import { Canvas, useThree, useFrame, extend } from "react-three-fiber";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { Box } from "./Box";
import { Model } from "./Model";

extend({ OrbitControls });
const Controls = (props) => {
  const { gl, camera } = useThree();
  const ref = useRef();

  //@ts-ignore
  useFrame(() => ref.current.update());

  //@ts-ignore
  return <orbitControls ref={ref} args={[camera, gl.domElement]} {...props} />;
};

const ThreeJSComponent = (props) => {
  const {
    width,
    height,
    fallback,
    modelSuccess,
    setBackground,
    background,
  } = props;

  return (
    <div
      style={{
        width: width,
        height: height,
        position: "relative",
      }}
    >
      <div
        onClick={() => setBackground("black")}
        style={{
          position: "absolute",
          top: "0",
          right: "0",
          width: "25px",
          borderRadius: "5px",
          height: "25px",
          background: "black",
          zIndex: 50,
          border: "white 1px solid",
        }}
      ></div>
      <div
        onClick={() => setBackground("white")}
        style={{
          position: "absolute",
          top: "0",
          right: "50px",
          width: "25px",
          height: "25px",
          borderRadius: "5px",
          background: "white",
          border: "black 1px solid",
          zIndex: 50,
        }}
      ></div>
      <Canvas
        style={{
          width: "100%",
          height: "100%",
          background: background,
        }}
        onCreated={({ gl }) => {
          gl.shadowMap.enabled = true;
          gl.shadowMap.type = THREE.PCFShadowMap;
          //@ts-ignore
          gl.outputEncoding = false;
          gl.toneMappingExposure = 0;
        }}
        camera={{
          position: [0, 0, 10],
          isPerspectiveCamera: true,
        }}
        //shadowMap
      >
        {" "}
        <ambientLight intensity={0x222222} />
        <Suspense fallback={fallback}>
          <Model url={props.modelURL} modelSuccess={modelSuccess} />
        </Suspense>
        <Controls
          autoRotate
          enablePan={true}
          enableZoom={true}
          enableDamping
          dampingFactor={0.5}
          rotateSpeed={1}
        />
      </Canvas>
    </div>
  );
};

export default ThreeJSComponent;

and Model:

import React, { useState, useEffect } from "react";
import { useLoader } from "react-three-fiber";

import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";

export const Model = (props) => {
  const { url, modelSuccess } = props;

  const [model, setModel] = useState();

  const loadedModel = useLoader(GLTFLoader, url, (loader) => {
    const dracoLoader = new DRACOLoader();
    dracoLoader.setDecoderPath("/draco-gltf/");
    loader.setDRACOLoader(dracoLoader);
  });

  useEffect(() => {
    if (!model) {
      setModel(loadedModel);
    }
  }, []);

  if (model) {
    modelSuccess();
    return (
      <primitive
        position={[0, 0, 0]}
        scale={[1, 1, 1]}
        object={model.scene}
        dispose={null}
      ></primitive>
    );
  } else return null;
};

Upvotes: 0

Views: 6848

Answers (1)

hpalu
hpalu

Reputation: 1435

you dont need setState, useLoader uses suspense, loadedModel will contain the model guaranteed, or the component will throw.

const { scene } = useLoader(GLTFLoader, url, ...)
return <primitive object={scene} dispose={null} />

ambientlight intensity is also wrong, 0 is no light, 1 is the default, then it goes higher. you're giving it an impossibly high value.

other than that it could be that the model is outside the cameras frustrum (too big), it could also be too small. or the path is wrong (it should either be in /public, or you need to it import modelUrl as "./model.glb".

here's a working example: https://codesandbox.io/s/r3f-ibl-envmap-simple-k7q9h?file=/src/App.js

import React, { Suspense } from 'react'
import { Canvas, useLoader } from 'react-three-fiber'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { OrbitControls, Html, draco } from 'drei'

function Model({ url }) {
  const { scene } = useLoader(GLTFLoader, url, draco())
  return <primitive object={scene} dispose={null} />
}

export default function App() {
  return (
    <Canvas camera={{ position: [0, 5, 1] }}>
      <directionalLight position={[10, 10, 5]} intensity={2} />
      <directionalLight position={[-10, -10, -5]} intensity={1} />
      <OrbitControls />
      <Suspense fallback={<Html>loading..</Html>}>
        <Model url="/suzanne-draco.glb" />
      </Suspense>
    </Canvas>
  )
}

Upvotes: 1

Related Questions