「three.js」웹에서 3D 기본 라이트를 구현해보자
3D CG에서 라이팅은 필수다.
three.js는 r116대 이후 버전부터 hdr을 광원으로 사용할 수 있다.이는 CG제작자들이 보기에 혁신이다. 반면, 의외로 많은 it분야 개발자들은 별 것 아닌 것으로 여긴다. 데이터가 풍부한 HDR(High Dynamic Range)사진을 라이팅으로 사용한다는 의미는 '실사'에 근접한 간접광을 만들 수 있는 것이다. CG분야는 이 같은 (실사 간접광) 라이팅&랜더링을 위해 거의 30년간 연구했다. three.js에서 코드 몇줄로 구현할 수 있지만, 사실 HDR광원 간접광은 그렇게 단순하게 볼 사항은 아니다.
| 기본 구조
우선, 아래와 같이 라이팅이 없는 기본 씬을 만든다.
라이트 종류
three.js r148버전(2023.01) 의 라이트는 'Ambient', 'Directional', 'Hemishphere', 'Point', 'RectArea', 'Spot' 6가지다.(최근 간접광 추가) 이 외에도 RGBELoader()를 통해 HDR을 광원으로 사용할 수 있다. (Probe 라이트) 라이트 특징은 다음과 같다.
* Point Light: 360도 전방향으로 퍼져나감(ex.백열등)
* Spot Light: 무대 연극 씬에서 배우에게 집중되는 '스폿(한점)' 라이트
* Directional Light: '야외 라이트'이며, 태양광
* Ambient Light: 전체 씬 광원 조절, 비현실적이며 테스트용으로 추천
* Hemisphere Light: 360º 구 형태이며, 상하 2개의 색상을 넣을 수 있음
* Area Light: 방송 제작에서 사용하는 반사판 기능
| Ambient라이트
Ambient라이트는 전체 씬의 광원을 동시에 조절한다. 라이트의 방향을 설정할 수 없고, 특정 색상을 씬에 추가하는 용도로 사용할 수 있다. 당연히 라이트의 위치도 설정할 수 없다. 설정도 간단하다.
let ambientLight = new THREE.AmbientLight( color, intensity )
라이트가 없는 기본 씬에 강도0.6 수준의 Ambient를 넣어보자.
Ambient라이트는 씬의 모든 오브젝트의 색상을 동일하게 만들었다. 그림자도 따로 설정할 게 없으므로 사실 '실사 라이팅'과 가장 거리가 먼 라이트이다. 인위적으로 특정 색상을 씬에 추가해야 할 때, 0.1~3 수준의 강도로 넣는 용도가 적당하다. 즉, 단독으로 사용할 수 없는 라이트다.
| Directional라이트
Directional라이트는 '태양광'으로 불린다. '실외광'으로 사용하며, 이름 그대로 방향을 지정할 수 있다. Ambient라이트와 비교한다면, 사실상 Directional라이트부터 제대로 된 라이트이며, 주로 사용하는 속성은 다음과 같다.
const directionalLight = new THREE.DirectionalLight( color, intensity );
scene.add( directionalLight );
* .castShadow = true or false;
* .position.x = 값
* .target = Object3D;
「메서드」
* .position.set( x, y, z );
* .copy()
현재 씬에 Directional라이트를 추가해보자.
| Hemisphere라이트
Hemisphere라이트는 일명 '돔 라이트'로 불린다. Max같은 3D프로그램에서 사용되었으며 (Maya, Blender, C4D도 지원), 반원을 기준으로 지면과 하늘을 동시에 표현하기에 좋다. Top에서 Bottom까지 그라디언트 효과를 통해 간접광 효과를 만들 수 있다. Hemisphere라이트의 속성과 메서드는 다음과 같다.
const light = new THREE.HemisphereLight( skyColor, groundColor, intensity );
scene.add( light );
* .color = sky 색상값
* .groundColor = 지면 색상값
* .position.x = 위치값(x, y도 같음)
「메서드」
* position.set( x, y, z)
* copy()
다소 인위적이지만 Hemisphere라이트라면, 빠르게 입체감을 만들수 있다.
| point라이트
point라이트는 전방향으로 광원을 형성하며, 백열등과 같다.
let light = new THREE.PointLight( color, intensity, distance, decay )
* .castShadow = true or false;
* .decay = Float값
* .distance = Float값
* .intensity = Float값
* .power = Float값( intensity보다 정밀하게 조절할 수 있음)
「메서드」
* position.set( x, y, z );
* copy()
특이한 점은 파라미터 값으로 존재하는 "거리"관련 속성이다. 즉, 라이트와 오브젝트의 거리(distance)와 거리에 따라 광원이 줄어드는 값, Decay 설정에 따라 실사 라이팅과 같은 효과를 낼 수 있다.
현재 distance값은 '10', Decay는 '1'이다. 만일 Decay값을 10 정도로 올린다면, 거리에 따라 광원이 줄어드는 강도가 증가하면서 거의 암흑처럼 보일 것이다. 개인적으로 Distance와 Decay비율은 '10: 1'정도가 실사에 가깝지 않을까 한다.
| Spot라이트
Spot라이트는 HDR이미지를 광원으로 사용하기 전까지 point, Area라이트와 함께 실사 라이팅의 핵심이었다. 그 이유는 파라미터 값만 보더라도 알 수 있다.
let light = new THREE.SpotLight( color, intensity, distance, angle, penumbra, decay );
light.position.set( 11, 7, 0 );
light.angle = Math.PI / 10;
light.penumbra = .8;
light.decay = .1;
light.distance = 20;
light.castShadow = true;
Spot라이트는 각도, 거리, (거리 대비)광원 증감값부터 그림자 엣지값을 조절하는(penumbra) 기능까지 가능하다. 따라서 G.I기반의 간접광이 등장하기 전까지 거의 절대적인 비중을 차지했다.
여전히 Area, Spot라이트는 실사 라이팅에서 중요한 역할을 차지하며, web 3D분야 역시 속도가 느린 컴퓨터를 위해 자주 사용할 수 밖에 없는 라이트다.
Spot라이트의 속성과 메서드는 다음과 같다.
* .angle = Float값
* .castShadow = true or false;
* .decay = Float값
* .distance = Float값
* .intensity = Float값
* .penumbra = Float값
* .target = Object3D 객체
「메서드」
* .position.set( x, y, z );
* .copy()
Spot라이트는 위와 같이 「거리, 각도, 경계값」에 따라 실사광을 연출할 수 있다.
| Area라이트
three.js에서는 r89버전에서 RectAreaLight를 업데이트 했다. 3D 프로그램에서 Area라이트라 불리며, 주로 반사광으로 사용한다. 반사판은 spot, point 라이트와 함께 (인위적인)실사광 연출에 필수적이다. 그런데 three.js는 어찌된 일인지 여전히 그림자를 지원하지 않고 있다. 그림자가 없기에 RectAreaLight는 사실상 반쪽짜리다.
const light = new THREE.RectAreaLight( 0xffffff, 10.0 );
light.rotation.set( new THREE.Vector3( 0, 0, Math.PI / 2))
light07.position.set( 2, 5, 0 );
light07.lookAt( 0, 0, 0)
light07.width = 3;
light07.height = 3;
단, 위와 같이 꽤 많은 속성을 지원한다.
* .height = Float값
* .width = Float값
* .intensity = Float값
* .position.x(y,z) = Float값
「메서드」
* .lookAt( Vec3 벡터값 )
* .copy()
RectAreaLight에서 주의해야 할 점은 오브젝트의 material이다. three.js에서 Area라이트는 PBR material만 지원하는데, 'MeshStandardMaterial' 혹은 'MeshPhysicalMaterial' 2가지를 사용할 수 있다.(그림자 지원 안 됨) 이에 따라, castShadow, shadow.map과 같은 설정은 불가능하다.
Area라이트는 크기와 광원이 비례한다. 향후, 그림자만 지원된다면 형광등, LED와 같은 실사광에 사용할 수 있을 것이다.