이번 주차에서는 간단한 태양계를 구현해 보며 씬 그래프를 이해해 보는 게 목표이다.
씬 그래프에서 부모가 자식에게 미치는 영향
우선 씬 그래프는 '요소(node)의 계층 구조를 그림으로 나타낸 것으로, 여기서 각 요소는 각각의 "지역 공간(local space)"을 가리'킨다.
지역 공간이 낯설긴 한데, 부모 자식관계를 설정해 부모의 위치, 크기, 회전을 조정하다 보면 어떤 개념인지 와닿는다. 부모 객체의 스케일이 확대되면, 자식 객체의 스케일도 따라 커지고, 자식 객체의 좌표평면에서 x, y, z 단위도 그만큼 확대된다. 부모객체가 x 축으로 90도 회전한다면, 자식도 마찬가지로 x 축으로 90도 회전된다. 즉 자식 객체의 1) 위치, 2) 크기, 3) 회전 등의 부모 객체를 기준으로 한다. 왜 그럴까? geometry 만 영향을 받는건가? three.js 공식문서에선 부모-자식 관계에 대해 이렇게 말한다.
An object's matrix stores the object's transformation relative to the object's parent; to get the object's transformation in world coordinates, you must access the object's matrixWorld.
1) 위치, 크기, 회전 등의 3D 객체의 변환을 위해 행렬을 사용하고, 그 행렬은 객체마다 저장한다.
2) 그 행렬은 부모 객체에 상대적인 값이다.
3) 따라서 자식객체는 부모 객체의 모든 변환에 영향받는다.
부모-자식 관계에 matrix 외에 영향을 주는 요소는 또 뭐가 있을까 지피티에게 물어보니 Object3D 의 visible (가시성), matrixAutoUpdate, frustum Culled, material 의 opacity, transparent 등이 있다고 하는 데, 공통적으로 얘기하자면 부모 객체가 안 보이게 되면 자식 객체도 안 보이게 된다 정도 같다.
태양계 코드 구현
공식 문서의 태양계 구현 코드를 읽으며 다음 두 가지가 의아했다.
1) 태양을 왜 굳이 회전시키지?
태양을 중심으로 공전하는 지구와 지구를 중심으로 공전하는 태양계를 구현하는 게 목표이다. 회전하는 solarSystem 객체 하위의 객체들은 solarSystem의 원점을 중심으로 회전하게 된다. 그래서 지구를 solarSystem 객체의 자식으로 만들고 나면 바로 공전하게 되는 데, 중심에 있는 태양(sunMesh)은 굳이 objects 배열에 추가해 회전시킬 필요 없지 않나? 태양을 objects 배열에서 제외시켜도 태양이 여전히 돌고 있는 걸 확인할 수 있다. 다만, 이전에는 회전하는 solarSystem 에서 태양(sunMesh)이 회전해야 하기 때문에 훨씬 속도면에서 빠르게 회전한다. 태양계 보다 더 넓은 범위를 구현할 땐 의미가 있을 것 같지만, 현재 목표에선 의미적으로나 성능적으로나 굳이 그럴 필요가 없어서 내가 구현할 때는 objects 배열에 추가하는 부분을 제거했다.
부모가 회전할 때 자식도 회전하면, 부모가 회전할 때 자식은 회전하지 않은 경우보다 얼마나 빠른 건지 갑자기 궁금해져서 지피티에게 물어보니 2배 정도라고 한다. 고마워 지피티야~!
2) earthOrbit과 moonOrbit을 굳이 추가하는 이유?
solarSystem 에 earthMesh를 자식으로 추가하고, moonMesh를 earthMesh 에 자식으로 추가해도 공식 문서의 예제와 동일하게 동작한다. 그럼에도 불구하고 굳이 분리한 이유는, 위에서 말한 것처럼 지역 공간의 변환이 하위 공간에 영향을 미치기 때문이다. 예를 들어, earthMesh 하위에 바로 moonMesh 를 속하게 한 후 earthMesh의 스케일을 키운다면 달의 크기와 위치가 모두 변하게 된다. 코드의 확장성과 그리고 코드를 볼 누군가를 위한 가독성을 위해 earthOrbit과 moonOrbit을 추가하는 편이 더 좋아 보인다. (그렇지만 내 코드에선 제외했다 :) 실험한 상태 그대로... ㅎ )
탱크!
공식 문서에선 귀여운 태양계 이후에 굉장히 복잡해진 탱크 코드를 던져준다. 뭐랄까. 예제가 심심해서 보면서 공부하는 사람이 의욕을 잃을까 걱정한 것 같다. 어쨌든 태양계 코드를 읽어보다 탱크코드를 보니 재밌긴 했다. 직접 구현하려면 한참 연습이 필요한 느낌. 다음은 코드를 뜯어볼 때 눈여겨본 부분들이다.
1) 객체 material의 castShadow와 조명에서 shadow 속성을 줘서 그림자를 표현한 점
2) Primitives 들을 차곡차곡 쌓아 올려 탱크를 만들 때, 각 geometry의 위치 설정. 특히 turretMesh.position.z로 얼마나 turret 이 뽑혀 나오는지 조정한 부분, 왼쪽 3개 오른쪽 3개 바퀴를 하나하나 달아준 부분들이 감명 깊었다. ㅎ ㅏ. 지역 공간을 이래서 잘 설정해야 하구나 싶기도 하고, 유니티를 써 본 입장에선 언제 코드로 한 줄 한 줄 다 쌓지 싶기도 하다. 결국 실제로 작업할 땐 3D 모델링을 어떻게 구하고 로드하냐가 중요할 것 같긴 하다.
3) 복잡한 곡선을 그리는 탱크의 움직임을 구현한 부분. 1) Vector2 객체 배열을 받아 생성된 SplineCurve 객체를 만들고 2)이 객체를 활용해 포인트를 뽑아 BufferGeometry를 생성해 탱크 길(?)을 시각화하고 3) SplineCurve 객체의 getPointAt 메서드로 탱크가 어디로 가야 할지 구한 뒤, 탱크의 위치를 설정해 준다.
// Create a sine-like wave
// 탱크가 가는 길
const curve = new THREE.SplineCurve([
new THREE.Vector2(- 10, 0),
new THREE.Vector2(- 5, 5),
new THREE.Vector2(0, 0),
new THREE.Vector2(5, - 5),
new THREE.Vector2(10, 0),
new THREE.Vector2(5, 10),
new THREE.Vector2(- 5, 10),
new THREE.Vector2(- 10, - 10),
new THREE.Vector2(- 15, - 8),
new THREE.Vector2(- 10, 0),
]);
// 시각화
const points = curve.getPoints(50);
const geometry = new THREE.BufferGeometry().setFromPoints(points);
const material = new THREE.LineBasicMaterial({ color: 0xff0000 });
const splineObject = new THREE.Line(geometry, material);
splineObject.rotation.x = Math.PI * .5; // 땅에 평행해야하기 때문에 x 축기준으로 90도
splineObject.position.y = 0.05; // 살짝 위로 나옴
scene.add(splineObject);
4) 계층 내에서 어떤 객체의 위치를 다른 객체가 위치를 참조할 때 getWorldPosition을 사용한 부분. 필요하지 않나 싶었는 데, 예시에 바로 나와서 좋았다.
const targetPosition = new THREE.Vector3();
// target 자체 회전
targetMesh.rotation.x = time * 7;
targetMesh.rotation.y = time * 13;
// face turret at target
// NOTE targetMesh 의 월드 좌표계에서의 위치를 targetPosition이라는 Vector3 객체에 저장함
targetMesh.getWorldPosition(targetPosition);
turretPivot.lookAt(targetPosition);
타깃의 움직임은 아직 모르겠다...! 몇 단계 중첩해서 부모를 두고, 부모의 y 좌표를 다양하게 조작해 움직이는 부분이 정확히 어떤 역할을 하는지 잘 모르겠다 :( 카메라도,,,,너무 많은데,,,, 어딜 바라보고,,, 어디에 종속되고,,,, 카메라와 타깃을 생각하다 멀미가 와서 더 익숙해졌을 때 이 부분은 재도전하기로 마음먹었다.
참고한 공식 문서와 연습한 코드
three.js manual
threejs.org
threejsstudies/src/week3.js at main · munjimon822/threejsstudies
Contribute to munjimon822/threejsstudies development by creating an account on GitHub.
github.com
threejsstudies/src/week3-1.js at main · munjimon822/threejsstudies
Contribute to munjimon822/threejsstudies development by creating an account on GitHub.
github.com
'개발 > js' 카테고리의 다른 글
네이버 앱에서 css가 깨질 땐..! (1) | 2024.11.20 |
---|---|
three.js 스터디 4주차) 카메라 컨트롤, 여러가지 재질과 텍스처를 곁들인 (2) | 2024.11.14 |
three.js 스터디 2주차) Primitives (2) | 2024.11.01 |
three.js 스터디 1주차) Hello Cube! (0) | 2024.10.29 |
three.js 스터디를 시작하며 (3) | 2024.10.29 |