Skip to content
Advertisement

Three.js – Model cannot receive any shadow

I’m currently new and learning about three.js. And i’m using react-three-fiber to make it happen with React, but i stumbled upon a problem. The model however, cannot receive any shadow from another model. I’ve tried to use obj.castShadow = true and obj.receiveShadow = true to one of the receiving shadow model object on the parent and the children as well but it shows no difference. Is there any way to cast a shadow to another model?

And the shadow.. it seems very rough. Is there any way to smoothen it?

1

Here’s my sandbox:
https://codesandbox.io/s/modest-newton-np1sw

Code:

import React, { Suspense, useMemo, useState } from "react";
import { Canvas } from "react-three-fiber";
import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader";
import { OrbitControls} from "drei";
import { Mesh } from "three";

import billboard from "../assets/models/billboard.obj";
import bridge from "../assets/models/bridge.obj";

const Model = ({ modelPath }) => {
  const [obj, setObj] = useState();
  useMemo(() => new OBJLoader().load(modelPath, setObj), [modelPath]);
  if (obj) {
    obj.castShadow = true;
    obj.traverse((children) => {
      if (children instanceof Mesh) {
        children.castShadow = true;
      }
    });
  }
  return obj ? <primitive object={obj} /> : null;
};

const ShadowedModel = ({ modelPath }) => {
  const [obj, setObj] = useState();
  useMemo(() => new OBJLoader().load(modelPath, setObj), [modelPath]);
  if (obj) {
    obj.castShadow = true;
    obj.receiveShadow = true;
    obj.traverse((children) => {
      if (children instanceof Mesh) {
        children.castShadow = true;
        children.receiveShadow = true;
      }
    });
  }
  return obj ? <primitive object={obj} /> : null;
};

const Lights = () => {
  return (
    <>
      <ambientLight intensity={0.1} />
      <spotLight
        castShadow
        position={[-50, 50, 20]}
        intensity={0.5}
        shadow-mapSize-shadowMapWidth={2048}
        shadow-mapSize-shadowMapHeight={2048}
        shadow-camera-left={-50}
        shadow-camera-right={50}
        shadow-camera-top={-50}
        shadow-camera-bottom={50}
      />
      <pointLight position={[10, -10, -20]} intensity={0.3} />
      <pointLight position={[0, 10, 5]} intensity={0.3} />
      <spotLight intensity={1} position={[0, 1000, 0]} />
    </>
  );
};

const Billboard = () => {
  return (
    <mesh
      castShadow
      position={[-15, 5, -35]}
      scale={[0.05, 0.05, 0.05]}
      rotation={[0, 20, 0]}
    >
      <Model modelPath={billboard} />
    </mesh>
  );
};

const Bridge = () => {
  return (
    <mesh
      castShadow
      receiveShadow
      position={[10, -40, -80]}
      // position={[-80, -40, -150]}
      scale={[0.15, 0.15, 0.15]}
      rotation={[0, 10.2, 0]}
    >
      <ShadowedModel modelPath={bridge} />
    </mesh>
  );
};

const Shadow = () => {
  return (
    <group>
      <mesh
        receiveShadow
        rotation={[-Math.PI / 2, 0, 0]}
        position={[-20, -32, -40]}
      >
        <planeBufferGeometry attach="geometry" args={[500, 500]} />
        <meshLambertMaterial attach="material" color={"lightblue"} />
      </mesh>
    </group>
  );
};

const MegatronModel = () => {
  return (
    <>
      <Canvas
        shadowMap
        colorManagement
        camera={{ position: [0, 0, 5], fov: 60 }}
      >
        <OrbitControls
          enablePan={Boolean("Pan", true)}
          enableZoom={Boolean("Zoom", true)}
          enableRotate={Boolean("Rotate", true)}
        />
        <Shadow />
        <Suspense fallback={null}>
          <Bridge />
        </Suspense>
        <Billboard />
        <Lights />
      </Canvas>
    </>
  );
};

export default MegatronModel;

Any help would be appreciated.
Thank you very much.

Advertisement

Answer

Your model is indeed receiving shadows. The problem is that you have several PointLights at full intensity that are washing everything out into white. Even shaded areas are blown out to white. See here, I’ve disabled all your PointLights and only kept the spotlight and ambient light so you can see the shadows again:

enter image description here

I recommend you use as few lights as possible to make rendering less resource-intensive. Adding so many lights could lower your framerate, overheat laptops, and drain more battery on cell-phones. For a pretty realistic lighting setup, I like using a single hemisphere light with a directional light like in this demo. Hemisphere makes it feel like there’s atmospheric light coming from the top, with reflected ground light from the bottom.

As far as pixelated shadows go, you’re using a large 2048×2048 map, but it’s spread out across too large an area. I see you’re trying to set left, right, top, bottom distances but those only apply to DirectionalLight, which uses an orthographic camera. Left, right, top, & bottom properties don’t apply to SpotLight, which uses a perspective camera. If you want to make the Spotlight shadowmap narrower, use the .angle property.

User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement