three.js에서 광원은 각각의 클래스에 따라 다른 효과를 제공함.
// 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
color='#ff0000' // 조명 색상
intensity={7} // 조명 세기
/>
주로 다른 광원과 함께 사용되어 더 현실적인 조명 효과를 만들어냄.
// 첫: 장면의 위 조명 색상
// 두: 장면의 아래 조명 색상
// 세: 조명 세기
<hemisphereLight args={['#0000ff', '#ff0000', 8]} />
태양광과 유사한 효과를 제공함. 전체적인 조명과 그림자를 만들어냄.
...
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]}
/>
특정 지점에서 방향을 모든 방향으로 뿌려지는 광원으로, 위치에 따라 조명의 강도가 줄어듬.
...
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, 무한한 거리)
/>
특정 방향으로 집중된 빛을 발산하는 광원으로, 지정된 각도에 따라 빛이 퍼짐. 주로 특정 지점을 강조하기 위해 사용함. 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)
/>
.. 임포트 에러
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;
실제 환경을 360도로 촬영한 이미지를 활용하여 광원 효과를 매우 효과적으로 나타낼 수 있음.
...
import { Environment } from '@react-three/drei';
...
// blur: 환경맵의 블러 정도 0 ~ 1
// background: 장면의 배경에 환경맵 적용
// files: 환경맵 이미지 파일 경로
<Environment
blur={0.01}
background
files='./images/hdr/symmetrical_garden_02_1k.hdr'
/>