
import { Canvas } from "@react-three/fiber";
import "./App.css";
import MYElement3DLight from "./MyElement3D_Light";
function App() {
return (
<>
{/* fov - 카메라 렌즈 화곽을 75도로 지정(Fovy값 0 ~ 180) / position - 카메라 위치 x, y 각 7로 지정*/}
<Canvas camera={{ fov: 75, position: [7, 7, 0] }}>
<MYElement3DLight />
</Canvas>
</>
);
}
export default App;
<ambientLight color="#ffffff" intensity={5} />
AmbientLight과 비슷하게 광원의 색상과 밝기 쌔기값을 속성으로 줄 수 있다.
하지만 색상 2개값을 가질수 있고, 하늘에서 비추는 광원, 지상에서 비추는 광원으로 나뉜다.
<hemisphereLight args={["#00f", "#f00", 20]} />
//args={["하늘", "지상", 밝기]}

태양과 같은 광원
광원의 위치와 물체와의 거리와 상관없이 특정(일정)한 방향으로 향하는 빛
directionalLight의 position 위치에서 원점까지의 방향으로 결정
<directionalLight color={0xffffff} intensity={5} position={[0, 5, 0]} />
//position={[0, 5, 0]} 으로 광원의 위치를 설정

광원 위치 눈으로 확인하는 방법
const light = useRef();
useHelper(light, THREE.DirectionalLightHelper);
원점이 아닌 다른 Target으로 할 경우
<directionalLight
ref={light}
color={0xffffff}
intensity={5}
position={[0, 5, 0]}
target-position={[1, 0, 0]}
/>
장면에 대한 객체가 scene에 저장
const { scene } = useThree();
useEffect를 통해 광원의 타겟을 장면에 추가하는 코드 작성
useEffect(() => {
scene.add(light.current.target);
//개발자가 직접 장면에 추가했으므로, 클린업 코드도 넣어야됨
return () => {
scene.remove(light.current.target);
};
}, [light]);
광원이 바라보는 코드를 회전하고 있는 빨간 원으로 추적
// smallSpherePivot를 움직이게하기 위해 useFrame사용
useFrame((state) => {
// 프레임이 만들어진 이후 경과된 시간을 변수에 저장
const time = state.clock.elapsedTime;
// time은 경과된 시간이므로 계속 증가하게 될 것임
const smallSpherePivot = state.scene.getObjectByName("smallSpherePivot");
// smallSpherePivot를 rotation.y로 회전 시키고 각도는 time * 50로 설정
smallSpherePivot.rotation.y = THREE.MathUtils.degToRad(time * 50);
//mesh에 getWorldPosition로 월드좌표계에 위치를 구해서 그 결과를 광원 타겟 포지션에 지정
⭐️⭐️smallSpherePivot.children[0].getWorldPosition(
light.current.target.position
);
});
import { OrbitControls, useHelper } from "@react-three/drei";
import { useFrame, useThree } from "@react-three/fiber";
import { useEffect, useRef } from "react";
import * as THREE from "three";
const torusGetometry = new THREE.TorusGeometry(0.4, 0.1, 32, 32);
const torusMaterial = new THREE.MeshStandardMaterial({
color: "#9b59b6",
roughness: 0.5,
metalness: 0.9,
});
function MYElement3DLight() {
// smallSpherePivot를 움직이게하기 위해 useFrame사용
useFrame((state) => {
// 프레임이 만들어진 이후 경과된 시간을 변수에 저장
const time = state.clock.elapsedTime;
// time은 경과된 시간이므로 계속 증가하게 될 것임
const smallSpherePivot = state.scene.getObjectByName("smallSpherePivot");
// smallSpherePivot를 rotation.y로 회전 시키고 각도는 time * 50로 설정
smallSpherePivot.rotation.y = THREE.MathUtils.degToRad(time * 50);
//mesh에 getWorldPosition로 월드좌표계에 위치를 구해서 그 결과를 광원 타겟 포지션에 지정
smallSpherePivot.children[0].getWorldPosition(
light.current.target.position
);
});
const light = useRef();
useHelper(light, THREE.DirectionalLightHelper);
const { scene } = useThree();
useEffect(() => {
scene.add(light.current.target);
return () => {
scene.remove(light.current.target);
};
}, [light]);
return (
<>
<OrbitControls />
<directionalLight
ref={light}
color={0xffffff}
intensity={5}
position={[0, 5, 0]}
target-position={[1, 0, 0]}
/>
<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="#fff" roughness={0.1} metalness={0.2} />
</mesh>
{new Array(8).fill().map((item, idx) => {
return (
<group key={idx} rotation-y={THREE.MathUtils.degToRad(45 * idx)}>
<mesh
geometry={torusGetometry}
material={torusMaterial}
position={[3, 0.5, 0]}
></mesh>
</group>
);
})}
{/* group에 name을 정하면 이를 통해 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 MYElement3DLight;

<pointLight
ref={light}
color="#ffffff"
intensity={20}
position={[0, 5, 0]}
/>
useHelper(light, THREE.PointLightHelper, 0.5);

//mesh에 getWorldPosition로 월드좌표계에 위치를 구해서 그 결과를 광원 타겟 포지션에 지정
smallSpherePivot.children[0].getWorldPosition(light.current.position);


<pointLight
ref={light}
color="#ffffff"
intensity={20}
position={[0, 5, 0]}
distance={1}
/>
광원에 위치에서 무대의 조명처럼 깔때기 모양으로 퍼짐
SpotLightHelper사용해서 어떻게 퍼지는지 확인할 수 있음
useHelper(light, THREE.SpotLightHelper);
return (
<>
<OrbitControls />
<spotLight
ref={light}
color={0xffffff}
intensity={10}
position={[0, 5, 0]}
target-position={[0, 0, 0]}
distance={20} //영향을 주는 거리값
angle={THREE.MathUtils.degToRad(30)} //깔때기 각도
penumbra={0.2} //빛의 감쇠율 (빛의 번짐정도)
/>

target-position={[0, 0, 0]} 원점이 아닌 광원의 타겟을 자식으로 추가해야됨const { scene } = useThree();
useEffect(() => {
scene.add(light.current.target);
return () => {
scene.remove(light.current.target);
};
}, [light]);
//mesh에 getWorldPosition로 월드좌표계에 위치를 구해서 그 결과를 광원 타겟 포지션에 지정
smallSpherePivot.children[0].getWorldPosition(light.current.target.position);
import {
RectAreaLightUniformsLib,
RectAreaLightHelper,
} from "three/examples/jsm/Addons.js";
<rectAreaLight
ref={light}
color="#fff"
intensity={20}
width={1}
height={3} //intensity과 크기로 밝기 조절 가능
position={[0, 5, 0]}
rotation-x={THREE.MathUtils.degToRad(-90)} //광원에서 비추는 각도
/>
useHelper(light, RectAreaLightHelper);
RectAreaLightUniformsLib.init();

import { OrbitControls, useHelper } from "@react-three/drei";
import { useFrame, useThree } from "@react-three/fiber";
import { useEffect, useRef } from "react";
import * as THREE from "three";
import {
RectAreaLightUniformsLib,
RectAreaLightHelper,
} from "three/examples/jsm/Addons.js";
const torusGetometry = new THREE.TorusGeometry(0.4, 0.1, 32, 32);
const torusMaterial = new THREE.MeshStandardMaterial({
color: "#9b59b6",
roughness: 0.5,
metalness: 0.9,
});
RectAreaLightUniformsLib.init();
function MYElement3DLight() {
// smallSpherePivot를 움직이게하기 위해 useFrame사용
useFrame((state) => {
// 프레임이 만들어진 이후 경과된 시간을 변수에 저장
const time = state.clock.elapsedTime;
// time은 경과된 시간이므로 계속 증가하게 될 것임
const smallSpherePivot = state.scene.getObjectByName("smallSpherePivot");
// smallSpherePivot를 rotation.y로 회전 시키고 각도는 time * 50로 설정
smallSpherePivot.rotation.y = THREE.MathUtils.degToRad(time * 50);
});
const light = useRef();
useHelper(light, RectAreaLightHelper);
return (
<>
<OrbitControls />
<rectAreaLight
ref={light}
color="#fff"
intensity={20}
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="#fff" roughness={0.1} metalness={0.2} />
</mesh>
{new Array(8).fill().map((item, idx) => {
return (
<group key={idx} rotation-y={THREE.MathUtils.degToRad(45 * idx)}>
<mesh
geometry={torusGetometry}
material={torusMaterial}
position={[3, 0.5, 0]}
></mesh>
</group>
);
})}
{/* group에 name을 정하면 이를 통해 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 MYElement3DLight;
실제 환경을 360도로 촬영한 이미지를 활용해서 광원효과를 효과적으로 나타냄
이미지 다운로드 사이트 -> 다운받아서 public폴더에 넣음
https://polyhaven.com/

Environment import후 광원 코드 추가
<Environment files={"./images/metro_noord_4k.hdr"} />

<Environment background files={"./images/metro_noord_4k.hdr"} />

<Environment
blur={0.1}
background
files={"./images/metro_noord_4k.hdr"}
/>
<Environment preset="sunset" />
sunset - 일몰을 연출합니다.
dawn - 새벽의 풍경을 연출합니다.
night - 밤하늘을 연출합니다.
warehouse - 창고 내부의 환경을 연출합니다.
forest - 숲속의 환경을 연출합니다.
apartment - 아파트 내부의 환경을 연출합니다.
studio - 스튜디오 환경을 연출합니다.
city - 도시 풍경을 연출합니다.
park - 공원의 환경을 연출합니다.
lobby - 로비 공간을 연출합니다.

이 글은 아래 유투브 강의를 듣고 작성한 글 입니다.
https://www.youtube.com/watch?v=0jnGlLb_z7w&list=PLe6NQuuFBu7HUeJkowKRkLWwkdOlhwrje&index=4