Three.js — Модель не может получить никакой тени

#javascript #reactjs #three.js #react-three-fiber

#javascript #reactjs #three.js #реакция-три волокна

Вопрос:

В настоящее время я новичок и изучаю three.js . И я использую react-three-fiber , чтобы это произошло с помощью React, но я наткнулся на проблему. Однако модель не может получить какую-либо тень от другой модели. Я пытался использовать obj.castShadow = true и obj.receiveShadow = true для одного из объектов принимающей теневой модели как для родительского, так и для дочерних объектов, но это не показывает никакой разницы. Есть ли какой-либо способ отбросить тень на другую модель?

И тень .. это кажется очень грубым. Есть ли какой-нибудь способ сгладить это?

1

Вот моя песочница:
https://codesandbox.io/s/modest-newton-np1sw

Код:

 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;  

Любая помощь будет оценена.

Большое вам спасибо.

Ответ №1:

Ваша модель действительно получает тени. Проблема в том, что у вас есть несколько PointLight s с полной интенсивностью, которые вымывают все в белый цвет. Даже затененные области становятся белыми. Смотрите здесь, я отключил все ваши PointLight s и сохранил только прожектор и окружающий свет, чтобы вы снова могли видеть тени:

введите описание изображения здесь

Я рекомендую вам использовать как можно меньше источников света, чтобы сделать рендеринг менее ресурсоемким. Добавление такого количества источников света может снизить частоту кадров, перегреть ноутбуки и разрядить аккумулятор на мобильных телефонах. Для довольно реалистичной настройки освещения мне нравится использовать светильник с одной полусферой и направленным светом, как в этой демонстрации. Полушарие создает ощущение, что сверху исходит атмосферный свет, а снизу — отраженный свет земли.

Что касается пикселизированных теней, вы используете большую карту 2048×2048, но она разбросана по слишком большой области. Я вижу, что вы пытаетесь установить left, right, top, bottom расстояния, но они применимы только к DirectionalLight, в котором используется ортогональная камера. Свойства Left, right, top и bottom не применяются к SpotLight, который использует перспективную камеру. Если вы хотите сделать Spotlight shadowmap более узким, используйте .angle свойство.

Комментарии:

1. Большое вам спасибо! Я потратил часы, чтобы выяснить, что происходит, но оказывается, это просто глупая ошибка, которую я совершил. Думаю, мне нужно пересмотреть и узнать гораздо больше из документации. Еще раз, спасибо!