GLTF Loader
는 Scene 에 glTF 모델을 로드하기 위해 존재하는 로더이다.
에셋은 JSON(.gltf) 또는 바이너리(.glb) 형식으로 제공될 수 있다.
그럼 glTF 와 glb 의 차이점은 무엇일까?
glb
는 all in 1 인데 반해 glTF
는 때때로 외부 파일을 참조하므로 모든 리소스를 기본 에셋과 같은 폴더에 넣지 않으면 많은 문제가 발생할 수 있다.
Group 객체는 Object3D와 거의 동일하다. 다만, 오브젝트 그룹 작업을 구문적으로 더 명확하게 하기 위해 분리하였다.
속성 값도 isGroup 밖에 없다.
즉, glTF 파일은 Group 객체인 것이다.
우선 blender 를 설치해준다.
설치하면 기본적인 프롭들이 존재하는데 a
를 눌러서 모두 선택한 후 삭제해주고 이제 마우스 wheel 버튼으로 시야를 욺직일 수 있다.
shfit + a
: 추가 메뉴 단축키
numpad 1~9
: 카메라 프리 셋 (e.g. 7번 머리에서 수직으로 내려보는 카메라)
Mesh 선택 후 + s
: 사이즈 조절
n
: x, y, z 위치 조절
g+z
: 마우스로 높이 조절 가능
그래서 이것저것 만져본 후 export 를 한 다음 프로젝트에서 불러오면
// ... renderer, camera, controller 추가 코드
const light = new THREE.SpotLight(undefined, Math.PI * 1000)
light.position.set(7, 7, 7)
light.angle = Math.PI / 3
light.castShadow = true
scene.add(light)
new GLTFLoader().load('models/suzanne_the_monkey.gltf', (gltf) => {
console.log(gltf)
scene.add(gltf.scene)
})
여기서 중요한 점은 하나의 glTF 로 내보낸 Group 객체는 동일한 Material 을 공유하여 하나의 Material 을 바꾸면 모두 적용된다는 것을 알아야한다.
new GLTFLoader().load('models/suzanne_the_monkey.gltf', (gltf) => {
const rootScene = gltf.scene;
const shadowSettings = {
cast: ['수잔', '구체', '구체001', '구체002', '구체003'],
receive: ['평면'],
};
rootScene.traverse((object) => {
if (object instanceof THREE.Mesh) {
if (shadowSettings.cast.includes(object.name)) {
object.castShadow = true;
}
if (shadowSettings.receive.includes(object.name)) {
object.receiveShadow = true;
}
}
});
scene.add(rootScene);
})
light(1000) + environment map
light(1000) + environment map(ACESFilmicToneMapping + toneMappingExposure 10%) (더 자연스러운 모습)
우리는 앞서 blender 에서 생성한 glTF 파일을 three.js 에 import 해봤다.
하지만 역시 blender 는 전문적인 툴로 훨씬 더 다양한 옵션을 제공한다. 하지만 그 옵션 중 몇개는 three.js 에서 인식할 수 있는 옵션이 존재한다. 그것들을 살펴보자.
오브젝트를 클릭 후 내려보면 Material 을 볼 수 있다. 여기서 기본 표면은 Principled BSDF 이다. 이 surface 를 three.js 로 import 해올 시 밑에있는 color, Matalic, Roughness 까지 three.js 에서 적용이 가능하다. 이제 이를 설정하면 아무것도 변경되는 것이 없는데 이는 지금 블렌더가 덩어리만 보여주고 있기 때문이다. 따라서 오른쪽 상단 viewport shader 를 눌러주면 수정한 값이 반영되어 표출된다.
transmission 속성은 MeshPhysicalMaterial 이었다.
자, 그럼 일단 이 투명도 속성없이 한번 가보자.
보면 MeshStandardMaterial 인 것을 알 수 있다. 하지만 여기에 블렌더에 transmission 속성을 넣는다면?
투명도 속성 때문에 타입이 MeshPhysicalMaterial 로 바뀐 것을 알 수 있다.
하지만 중요한 것은 blender 와 three.js 는 동작 엔진도 다르고 옵션, 사용 목적, 대상도 모두 다르기 때문에 blender 에 나오는 모습이 three.js 에서 그래도 나올 것이라는 기대를 하면 안 된다. 따라서 blender 에서 대충 모양을 잡고 three.js 로 가져와서 gui 를 통하여 본인이 원하는 모습을 만들어야 할 것이다.
블렌더에는 총 4가지의 광원이 준비되어있다.
1. point: point light 와 같다. 광원에서 모든 방향으로 빛이 나가는
2. sun: directional Light 와 같다. 모든 방향이 같은 방향으로 parellel 하게 들어옴
3. spot: spot light 와 같다. 손전등 같은거
4. area: RectAreaLight 와 같다. (아직 살펴본 적 없음.)
대충 spot light 를 만들어주고 export 할 때 이제 Include 에서 Punctual lights
를 체크해주면 된다.
여기서 light 를 켜주고 power 를 5000 으로 설정하면
블랜더와 다르게 intensity 가 너무너무 강렬하게 설정되어 색이 다 날아간 것을 확인할 수 있다. 따라서 우리는
new GLTFLoader().load('models/suzanne_the_monkey.gltf', (gltf) => {
const spotLight = gltf.scene.getObjectByName("스폿") as THREE.SpotLight
spotLight.castShadow = true
spotLight.intensity /= 5000
scene.add(gltf.scene)
})
위 코드를 통하여 아래와 같은 결과를 얻을 수 있다.
블렌더에는 굉장히 다양한 텍스쳐를 확인할 수 있다. 하지만 이 모든 텍스쳐가 three.js 에서 동작하진 않는다.
우선 이미지 텍스쳐를 넣어보자. 저장하고 three.js 와 블렌더를 비교해보면 상당히 다른 모습인 것을 확인할 수 있는데 이는 역시 블렌더와 three.js 의 이미지 렌러딩 형식이 달라서 그렇다. 이는 코드를 통해 맞춰줄 수 있다.
new GLTFLoader().load('models/suzanne_the_monkey.gltf', (gltf) => {
console.log(gltf)
const suzanne = gltf.scene.getObjectByName('수잔') as THREE.Mesh
suzanne.castShadow = true;
((suzanne.material as THREE.MeshStandardMaterial).map as THREE.Texture).colorSpace = THREE.LinearSRGBColorSpace;
...
아직 정식적으로 나온 것은 아니다.
앞서 작성한 Light 객체에 .add()
메서드로 Lensflare 객체를 추가하면 광원 효과를 얻을 수 있다.
const light = new THREE.SpotLight(undefined, Math.PI * 1000)
...
scene.add(light)
const textureLoader = new THREE.TextureLoader()
const textureFlare0 = textureLoader.load('https://cdn.jsdelivr.net/gh/Sean-Bradley/First-Car-Shooter@main/dist/client/img/lensflare0.png')
const lensflare = new Lensflare()
lensflare.addElement(new LensflareElement(textureFlare0, 1000, 0))
light.add(lensflare)