인프런 영문 브랜드 로고
인프런 영문 브랜드 로고

인프런 커뮤니티 질문&답변

김혜빈님의 프로필 이미지

작성한 질문수

Three.js로 시작하는 3D 인터랙티브 웹

스크롤 페이지 만들기 4 - 객체 배치하고 애니메이션 처리하기

그림자가 다르게 표현됩니다

작성

·

53

·

수정됨

0

강의 코드를 그대로 따라하면 mesh 색이 어두운 회색으로 표현되고 그림자가 생기지 않는 문제가 있습니다.

 

그래서 SpotLight의 위치와 강도를 조정했더니 그림자는 나타나는데 강의영상처럼 모든 집에대한 그림자가 동일하게 나오는 것이 아니라 맨 처음 집에 대한 그림자만 선명히 나타납니다!

image.pngimage.png

 

빛을 한방향에서만 쏘기 때문에 저 처럼 표현되는게 맞는 것 같은데, 강의영상처럼 모두 동일한 그림자를 표현하려면 어떻게 해야할까요..?

 

 

import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/Addons.js';
import { House } from './House';
import gsap from 'gsap';
// ----- 주제: 스크롤에 따라 움직이는 3D 페이지

// Renderer
const canvas = document.querySelector('#three-canvas');
const renderer = new THREE.WebGLRenderer({
  canvas,
  antialias: true,
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio > 1 ? 2 : 1);
renderer.shadowMap.enabled = true; // 그림자 설정, mesh도 함께 설정해야함
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 그림자 부드럽게

// Scene
const scene = new THREE.Scene();
scene.background = new THREE.Color('white'); // 백그라운드 하얀색

// Camera
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
camera.position.set(-5, 2, 25);
scene.add(camera);

// Light
const ambientLight = new THREE.AmbientLight('white', 2);
scene.add(ambientLight);

const spotLight = new THREE.SpotLight('white', 500);
spotLight.position.set(-5, 10, 30);
// 그림자 설정
spotLight.castShadow = true;
spotLight.shadow.mapSize.width = 1024; // 그림자 퀄리티 조정(성능에 크게 영향을 미치지 않는 정도)
spotLight.shadow.mapSize.height = 1024; // 그림자 퀄리티 조정(성능에 크게 영향을 미치지 않는 정도)
spotLight.shadow.camera.near = 1;
spotLight.shadow.camera.far = 1000;
scene.add(spotLight);

const spotLightHelper = new THREE.SpotLightHelper(spotLight, 'red');
scene.add(spotLightHelper);

// --- (2) HemisphereLight 추가 ---
// const hemiLight = new THREE.HemisphereLight('white', 'white', 2);
// scene.add(hemiLight);

const gltfLoader = new GLTFLoader();

// Mesh
const floorMesh = new THREE.Mesh(
  new THREE.PlaneGeometry(100, 100),
  new THREE.MeshStandardMaterial({
    color: 'white',
    roughness: 0.4, // 필요에 따라 조절
    metalness: 0.2, // 필요에 따라 조절
    toneMapped: false,
  }) // 강의에는 MeshStandartMaterial 사용했는데, 이거 사용시 floorMesh가 하얀색이 아닌 회색으로 보여서 변경
);
floorMesh.rotation.x = -Math.PI / 2; // 180//2 = 90도
floorMesh.receiveShadow = true; // floorMesh에 그림자가 그려지기 때문에 recieveShadow 사용
scene.add(floorMesh);

// 하우스를 통해 그림자가 만들어져야 하므로 -> castShadow
const houses = [];
houses.push(
  new House({
    gltfLoader,
    scene,
    modelSrc: '/models/house.glb',
    x: -5,
    z: 20,
    height: 2,
  })
);
houses.push(
  new House({
    gltfLoader,
    scene,
    modelSrc: '/models/house.glb',
    x: 7,
    z: 10,
    height: 2,
  })
);
houses.push(
  new House({
    gltfLoader,
    scene,
    modelSrc: '/models/house.glb',
    x: -10,
    z: 0,
    height: 2,
  })
);
houses.push(
  new House({
    gltfLoader,
    scene,
    modelSrc: '/models/house.glb',
    x: 10,
    z: -10,
    height: 2,
  })
);
houses.push(
  new House({
    gltfLoader,
    scene,
    modelSrc: '/models/house.glb',
    x: -5,
    z: -20,
    height: 2,
  })
);
// 그리기
const clock = new THREE.Clock();

function draw() {
  const delta = clock.getDelta();

  renderer.render(scene, camera);
  renderer.setAnimationLoop(draw);

  spotLightHelper.update();
}

let currentSection = 0;
function setSection() {
  //   console.log('setSection 실행!'); // setSection 스크롤할때마다 실행됨
  const newSection = Math.round(window.scrollY / window.innerHeight); // 0,1,2,3,4

  if (currentSection !== newSection) {
    console.log('animation!'); // section값이 바뀔때만 애니메이션이 동작하도록
    gsap.to(camera.position, {
      duration: 1,
      x: houses[newSection].x,
      z: houses[newSection].z + 5,
    });
    currentSection = newSection;
  }
}

function setSize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.render(scene, camera);
}

// 이벤트
window.addEventListener('scroll', setSection);
window.addEventListener('resize', setSize);

draw();

답변 1

0

인프런 AI 인턴님의 프로필 이미지

안녕하세요, 인프런 AI 인턴입니다. 김혜빈님께서 질문하신 내용에 대해 답변드리겠습니다.

Three.js에서 여러 개의 물체에 대해 그림자를 자연스럽게 표현하려면 몇 가지 사항을 점검해볼 필요가 있습니다.

  1. 조명 설정: SpotLight와 같은 특정 조명은 setup에 따라 그림자를 다르게 표현할 수 있습니다. 강의와 다르게 표현되는 이유는 조명 위치 및 각도 설정이 다를 수 있기 때문입니다. 그림자가 강의와 같이 모두 동일하게 나오도록 하려면, 각도나 위치를 조정하여 조명 광원을 더 넓게 설정하거나, 추가적인 조명을 사용할 수 있습니다.

  2. 그림자 속성 확인: 각 Mesh가 castShadowreceiveShadow 속성을 올바르게 설정했는지 확인하세요. 그림자는 일반적으로 빛과 대상 객체 모두에 castShadow 속성이 필요하고, 지표면과 같은 받는 면에는 receiveShadow 속성이 필요합니다.

  3. 장면 내 요소들의 구성: 현재 장면의 구성 요소가 어떤 방식으로 배치되어 있는지 확인해보세요. 적절한 그림자를 위해, 조명이 충분히 모든 물체에 닿을 수 있도록 배치해야 합니다.

  4. 빛의 강도: 빛의 강도를 더 높이거나, 총 사용 가능한 그림자 맵의 해상도를 올려 물체에 그림자가 고르게 드리워지는지 확인하세요.

아래 링크를 참조하여 추가적인 도움 받으실 수 있습니다:

해당 페이지에서 비슷한 질문 및 답변을 잘 정리해두었다고 하니 참고해보시면 좋겠습니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.