R3F(React Three Fiber) - Light

H.GOO·2024년 4월 5일
0

WEB-3D-Project

목록 보기
5/9
post-thumbnail

🪴 광원(Light)

three.js에서 광원은 각각의 클래스에 따라 다른 효과를 제공함.




🪴 Three.js 클래스별 광원(Light)

비교를 위한 기본 3D 렌더링 화면

// Canvas가 있는 부분

import { Canvas } from '@react-three/fiber';
import styled from 'styled-components';
import Box from '../../components/Geometry/Box';

const Main = () => {
  return (
    <MainBox>
      <Canvas
        camera={{
          fov: 75, // 렌지 화각
          position: [7, 7, 0], // 카메라 좌표
        }}
      >
        <Box />
      </Canvas>
    </MainBox>
  );
};

const MainBox = styled.div`
  display: flex;
  align-items: center;
  width: 100%;
  height: 100vh;

  background: black;
`;

export default Main;
// mesh가 있는 부분

import * as THREE from 'three';
import { useFrame } from '@react-three/fiber';
import { OrbitControls } from '@react-three/drei';

const torusGeometry = new THREE.TorusGeometry(0.4, 0.1, 32, 32);
const torusMaterial = new THREE.MeshStandardMaterial({
  color: '#b86ed5',
  roughness: 0.5,
  metalness: 0.9,
});

const Box = () => {
  useFrame((state) => {
    // state: 현재 프레임을 렌더링하는 동안 변할 수 있는 상태값
    // delta: 프레임 간의 시간 간격

    const time = state.clock.elapsedTime;
    const smallSpherePivot = state.scene.getObjectByName('smallSpherePivot');
    smallSpherePivot.rotation.y = THREE.MathUtils.degToRad(time * 50);
  });
  return (
    <>
      <OrbitControls />

      <ambientLight color='#ffffff' intensity={7} />

      <mesh rotation-x={THREE.MathUtils.degToRad(-90)}>
        <planeGeometry args={[10, 10]} />
        <meshStandardMaterial color='#2c3e50' roughness={0.5} metalness={0.5} side={THREE.DoubleSide} />
      </mesh>

      <mesh rotation-x={THREE.MathUtils.degToRad(-90)}>
        <sphereGeometry args={[1.5, 64, 64, 0, Math.PI]} />
        <meshStandardMaterial color='#ffffff' roughness={0.1} metalness={0.2} />
      </mesh>

      {new Array(8).fill().map((item, index) => (
        <group key={index} rotation-y={THREE.MathUtils.degToRad(45 * index)}>
          <mesh geometry={torusGeometry} material={torusMaterial} position={[3, 0.5, 0]} />
        </group>
      ))}

      {/* group 이름을 지정해두면 이후에 그룹 이름을 통해 해당 그룹을 참조할 수 있음 */}
      <group name='smallSpherePivot'>
        <mesh position={[3, 0.5, 0]}>
          <sphereGeometry args={[0.3, 32, 32]} />
          <meshStandardMaterial color='#e74c3c' roughness={0.2} metalness={0.5} />
        </mesh>
      </group>
    </>
  );
};

export default Box;

AmbientLight (주변광/환경광)

모든 방향에서 동일한 밝기를 가지며, 자체로는 그림자를 생성하지 않음.

<ambientLight
  color='#ff0000' // 조명 색상
  intensity={7}   // 조명 세기
/>

HemisphereLight (주변광, 색상 2가지)

주로 다른 광원과 함께 사용되어 더 현실적인 조명 효과를 만들어냄.

// 첫: 장면의 위 조명 색상
// 두: 장면의 아래 조명 색상
// 세: 조명 세기
<hemisphereLight args={['#0000ff', '#ff0000', 8]} />

DirectionalLight (특정 방향으로 향하는 빛)

태양광과 유사한 효과를 제공함. 전체적인 조명과 그림자를 만들어냄.

...

useFrame((state) => {
  const time = state.clock.elapsedTime;
  const smallSpherePivot = state.scene.getObjectByName('smallSpherePivot');
  // 작은 공 회전
  smallSpherePivot.rotation.y = THREE.MathUtils.degToRad(time * 50);
  // 작은 공 위치와 조명 타겟 포지션 동기화
  smallSpherePivot.children[0].getWorldPosition(light.current.target.position);
});

const light = useRef();

useHelper(light, THREE.DirectionalLightHelper); // directionalLight 헬퍼 추가

const { scene } = useThree(); // 장면 객체

useEffect(() => {
  scene.add(light.current.target); // 장면에 타겟 객체 추가
  return () => {
    scene.remove(light.current.target); // 언마운트시 장면에서 타겟 제거
  };
}, []);

...

// color: 조명 색상
// intensity: 조명 세기
// position: 조명 위치(x, y, z)
// target-position: 조명의 타겟 위치(x, y, z)
<directionalLight
  ref={light}
  color={0xffffff}
  intensity={5}
  position={[0, 5, 0]}
  target-position={[0, 0, 1]}
/>

PointLight (모든 방향으로 비추는 빛)

특정 지점에서 방향을 모든 방향으로 뿌려지는 광원으로, 위치에 따라 조명의 강도가 줄어듬.

...

useFrame((state) => {
  const time = state.clock.elapsedTime;
  const smallSpherePivot = state.scene.getObjectByName('smallSpherePivot');
  smallSpherePivot.rotation.y = THREE.MathUtils.degToRad(time * 50);
  // 작은 공 위치와 조명 포지션 동기화
  smallSpherePivot.children[0].getWorldPosition(light.current.position);
});

const light = useRef();
useHelper(light, THREE.PointLightHelper, 0.5); // 첫: 광원 객체, 두: 객체 종류, 세: 헬퍼 메시 크기

...

<pointLight
  ref={light}
  color='#ffffff' // 조명 색상
  intensity={10} // 조명 세기
  position={[0, 5, 0]} // 조명 위치
  distance={0} // 조명이 영향을 미치는 거리 (기본값: 0, 무한한 거리)
/>

SpotLight (조명광)

특정 방향으로 집중된 빛을 발산하는 광원으로, 지정된 각도에 따라 빛이 퍼짐. 주로 특정 지점을 강조하기 위해 사용함. PointLight 와 비슷하지만, 빛이 방향을 가지고 있고, 빛이 발산하는 각도를 조절할 수 있음.

...

useFrame((state) => {
  const time = state.clock.elapsedTime;
  const smallSpherePivot = state.scene.getObjectByName('smallSpherePivot');
  // 작은 공 회전
  smallSpherePivot.rotation.y = THREE.MathUtils.degToRad(time * 50);
  // 작은 공 위치와 조명 타겟 포지션 동기화
  smallSpherePivot.children[0].getWorldPosition(light.current.target.position);
});

const light = useRef();

useHelper(light, THREE.DirectionalLightHelper); // directionalLight 헬퍼 추가

const { scene } = useThree(); // 장면 객체

useEffect(() => {
  scene.add(light.current.target); // 장면에 타겟 객체 추가
  return () => {
    scene.remove(light.current.target); // 언마운트시 장면에서 타겟 제거
  };
}, []);

...

<spotLight
  ref={light}
  color='#ffffff' // 조명 색상
  intensity={80} // 조명 세기
  position={[0, 5, 0]} // 조명 위치
  target-position={[0, 0, 0]} // 조명의 타겟 위치
  distance={0} // 조명이 영향을 미치는 거리 (기본값: 0, 무한한 거리)
  angle={THREE.MathUtils.degToRad(30)}
  penumbra={0.1} // 빛 감쇠율 (기본값: 0)
/>

ReactAreaLight (형광등처럼 비추는 빛)

.. 임포트 에러

import { useRef } from 'react';
import * as THREE from 'three';
import { useFrame } from '@react-three/fiber';
import { OrbitControls, useHelper } from '@react-three/drei';
import { ReactAreaLightUniformsLib } from '../../../node_modules/three/examples/jsm/lights/ReactAreaLightUniformsLib';
import { ReactAreaLightHelper } from 'three/examples/jsm/helpers/ReactAreaLightHelper';

const torusGeometry = new THREE.TorusGeometry(0.4, 0.1, 32, 32);
const torusMaterial = new THREE.MeshStandardMaterial({
  color: '#b86ed5',
  roughness: 0.5,
  metalness: 0.9,
});

ReactAreaLightUniformsLib.init();

const ReactArea = () => {
  useFrame((state) => {
    const time = state.clock.elapsedTime;
    const smallSpherePivot = state.scene.getObjectByName('smallSpherePivot');
    smallSpherePivot.rotation.y = THREE.MathUtils.degToRad(time * 50);
  });

  const light = useRef();
  useHelper(light, ReactAreaLightHelper);

  return (
    <>
      <OrbitControls />

      <reactAreaLight
        ref={light}
        color='#ffffff' // 조명 색상
        intensity={80} // 조명 세기
        width={1} // 조명 너비
        height={3} // 조명 폭
        position={[0, 5, 0]} // 조명 위치
        rotation-x={THREE.MathUtils.degToRad(-90)} // 조명 각도
      />

      <mesh rotation-x={THREE.MathUtils.degToRad(-90)}>
        <planeGeometry args={[10, 10]} />
        <meshStandardMaterial color='#2c3e50' roughness={0.5} metalness={0.5} side={THREE.DoubleSide} />
      </mesh>

      <mesh rotation-x={THREE.MathUtils.degToRad(-90)}>
        <sphereGeometry args={[1.5, 64, 64, 0, Math.PI]} />
        <meshStandardMaterial color='#ffffff' roughness={0.1} metalness={0.2} />
      </mesh>

      {new Array(8).fill().map((item, index) => (
        <group key={index} rotation-y={THREE.MathUtils.degToRad(45 * index)}>
          <mesh geometry={torusGeometry} material={torusMaterial} position={[3, 0.5, 0]} />
        </group>
      ))}

      <group name='smallSpherePivot'>
        <mesh position={[3, 0.5, 0]}>
          <sphereGeometry args={[0.3, 32, 32]} />
          <meshStandardMaterial color='#e74c3c' roughness={0.2} metalness={0.5} />
        </mesh>
      </group>
    </>
  );
};

export default ReactArea;



🪴 Drei 클래스별 광원(Light)

Environment (주위 환경을 촬영한 이미지를 이용한 빛)

실제 환경을 360도로 촬영한 이미지를 활용하여 광원 효과를 매우 효과적으로 나타낼 수 있음.

Hdris 이미지 다운로드 링크

...

import { Environment } from '@react-three/drei';

...

// blur: 환경맵의 블러 정도 0 ~ 1
// background: 장면의 배경에 환경맵 적용
// files: 환경맵 이미지 파일 경로

<Environment
  blur={0.01}
  background 
  files='./images/hdr/symmetrical_garden_02_1k.hdr'
/>


0개의 댓글