three.js에서 transform 기능에 대해서 더 심도있는 코드를 정리해보려고 한다!
<script>
function draw() {
const delta = clock.getDelta();
mesh.position.set(1,1,1); 1️⃣
console.log(mesh.position.length()); 2️⃣
console.log(mesh.position.distanceTo(new THREE.Vector3(3, 2, 0))); 3️⃣
console.log(mesh.position.distanceTo(camera.position));
renderer.render(scene, camera);
renderer.setAnimationLoop(draw);
}
</script>
기존에 mesh의 position 값을 변동하기 위해서 mesh.position.y = 2;
와 같이 진행했었는데,
set
을 활용하면 더욱 간단하게 위치값을 변동할 수 있다.
코드를 보면 간단하게 알 수 있는데, (x,y,z)
의 값을 적어주면 mesh의 위치값을 셋팅이 가능하다!
length
는 길이를 뜻하는 단어와 같이 (0,0,0)
이라는 기본 위치값에서
set
으로 변경된 값까지의 거리를 나타내는 코드이다
distanceTo
해당 메서드는 어떤 벡터 3D까지의 거리를 나타냅니다!
여기서 new THREE.Vector3
라는 기능을 활용해서 새로운 벡터 사례를 만듭니다!
set()
과 동일하게 Vector3(x,x,z)
를 활용해 임의의 벡터 위치값을 생성해
distanceTo
로 mesh와의 거리를 비교해보는 것이다!
그 아래의 다른 방법으로는 우리가 미리 셋팅해둔 카메라의 위치값과의 거리도 확인이 가능하다~
<script>
function draw() {
const delta = clock.getDelta();
// mesh.scale.x = 2;
// mesh.scale.y = 0.5;
mesh.scale.set(2,0.5,1);
renderer.render(scene, camera);
renderer.setAnimationLoop(draw);
}
</script>
mesh의 크기를 변경하는건 아주 씸쁠하다!
position
과 비슷하게 scale
을 사용하면 되는데,
방법 또한 position
과 동일하게 set
을 사용하여 수정이 가능하다.
너무 간단하기에 코멘트는 여기까지~
셋팅이 (1,1,1)이었던 박스가 기준점을 기준으로 x축으로 2배 커졌고, y축으로 0.5인 반으로 줄어듬
<script>
function draw() {
const delta = clock.getDelta();
mesh.rotation.reorder('YXZ'); 2️⃣
mesh.rotation.y = THREE.MathUtils.degToRad(45); 1️⃣
mesh.rotation.x = THREE.MathUtils.degToRad(20); ✅
renderer.render(scene, camera);
renderer.setAnimationLoop(draw);
}
</script>
회전 기능인 rotation의 경우도 기존에 작업했던 회전 방식과 크게 차이는 없다!
threeJS의 기능인 MathUtils
를 사용, 라디안을 일반적으로 사용하는 각도로 변경하는 degToRad
메서드를 사용해서 각도를 넣어주는데, 여기서는 y축을 기준으로 45도를 적용시켰다!
그렇다면 y축으로 45도 회전시킨 후 추가적으로 x축으로 20도를 돌리고 싶다면
✅ 와 같이 작성하면 될까~?
아니었다!!
위의 그림처럼 회색선, 즉 y축으로 45가 돌아가서 그 돌아간 축을 기준으로 x값이 회전을 할 것 같았지만!
y축의 기준은 돌아가지 않았고 노란색 선처럼 mesh만 회전한 상태가 된것이다..!
이럴 때 사용하는 방법이 바로! 2️⃣이다~
3개의 회전을 순차적으로 적용해서 3D 객체의 회전을 표현하는 방식인 오일러 회전인 reorder()
를 사용하며
값으로는 ('XYZ')
와 같이 사용을 하게된다.
💡
reorder()
문자열(String)을 인자로 받기 때문에''
를 꼭! 사용해야 한다!
현재 위의 코드에서는 Y축으로 회전 후에 X축으로 회전하기 바라는 순서로
Y → X → Z(현재는 따로 설정은 안함)
와 같은 진행 순서다! 그렇기 때문에 .reorder('YXZ');
라고 기입을 하면 순서에 따라 축의 값이 변경된다!
🤓 reorder 순서의 중요성
Y축으로 90도 돌린 후 X축으로 90도 돌리는 것과, 그 반대 순서로 돌리는 건 전혀 다른 방향이 됩니다.
따라서 rotation.reorder('YXZ')와 rotation.reorder('XYZ')는 같은 각도 값이라도
최종적으로 mesh가 바라보는 방향이 달라집니다.
그룹을 만드는건 꽤나 중요한 부분이다!
예를 들어서 팔이란 요소가 있다고 치자~
위의 이미지와 같이 팔을 생성했을 때, 팔을 위로 올린다거나 회전을 하게 된다면
노란부분과 파란부분, 빨간부분을 일일이 하나하나 위치값을 잡아줘야 하는데, 그렇게 된다면 굉~~장히 번거롭고 코드도 복잡해지게 될 것이다! 그럴 때 그룹을 생성해서 하나로 묶어주어 transform을 준다면 간단하게 구현이 가능할 것이다!
위의 이미지와 같이 three.js에서 그룹으로 가장 좋은 예시로 태양, 우주, 달의 공전을 예시로 많이들 든다고 한다!
태양이라는 group1, group1이라는 속해있는 지구 group2, group2에 속해있는 달 group3
코드로 보도록 하자!
<script>
// Mesh
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({
color: 'orange'
});
const mesh = new THREE.Mesh(geometry, material);
//scene.add(mesh); group을 사용할 것이에 해당 부분은 삭제
⭐️
const group1 = new THREE.Group();
const box1 = new THREE.Mesh(geometry, material);
⭐️
</script>
일단은 가장 큰 그룹인 태양 group1을 생성해주는데 three.js의 기능인 Group
을 사용해서 새로운 그룹을 만들어준다!
그리고 태양이라는 box1을 mesh로 생성!
<script>
const group2 = new THREE.Group();
const box2 = box1.clone();
box2.scale.set(0.3,0.3,0.3);
group2.position.x = 2;
</script>
태양 그룹은 생성이 완료되었으니 이제 지구 그룹을 만들어준다!
지구 group2를 Group
을 사용해서 새로 생성! 태양과 동일하게 box2로 지구를 생성해주는데,
const box2 = new THREE.Mesh(geometry, material);
처럼 생성하여도 되지만!
더 간단하게 three.js의 메서드인 clone
을 사용하여 box1의 클론 박스를 생성해준다!
Three.js의 대부분의 객체( Mesh, Object3D, Material, Geometry... )는
.clone() 메서드를 제공합니다.
지구의 경우 태양보다 크기가 작기 때문에 scale
을 사용하여 set
으로 x,y,z 모두 0.3 비율로 축소시켜 줍니다!
이대로 생성을 마치게 된다면 group1과 group2는 동일한 위치에 박스를 생성하게 되기 때문에 group2의 위치를 수정해주어야 한다! 그래서 group2.position.x = 2;
로 group2의 위치를 살짝 옮겨준다!
주의
작업 도중group2.position.x = 2;
가 아니라box2.position.x = 2;
를 해주는
실수를 범했는데 태양 그룹인 group1 안으로 속하는 것은 group2여야 하기 때문에 box가 아닌
group 위치를 이동해 주어야 한다! 주의하자!!
<script>
const group3 = new THREE.Group();
const box3 = box2.clone();
box3.scale.set(0.15,0.15,0.15);
box3.position.x = 0.5;
</script>
group2와 동일하게 이제는 달 그룹인 group3을 새로 생성해주고,
box3의 경우는 box2에서 클론을 만들어준다!
box3의 사이즈도 box2의 scale.set이 0.3으로 맞춰줘 있으니 그 기준에서 절반인 0.15로 셋팅해준다!
그리고 group3의 경우는 group2에 추가될 요소이기 때문에
group3..position.x = 0.5;
않고 box3.position.x = 0.5;
로 살짝만 위치조정을 해줘도
기준이 group2로 정해져 이동되기 때문에 문제 없이 작동한다!
<script>
group3.add(box3); 1️⃣
group2.add(box2, group3); 2️⃣
group1.add(box1, group2); 3️⃣
scene.add(group1); 4️⃣
</script>
일단 1번에서 4번 순서대로 group안에 box를 add
해준다!
1️⃣ 달 그룹인 group3는 달 요소인 box3을 추가!
2️⃣ 지구 그룹인 group2는 지구 요소인 box2와 달이 포함된 group3을 추가!
3️⃣ 태양 그룹인 group1은 태양 요소인 box1과 지구와 달이 포함된 group2를 추가!
4️⃣ 전체 무대인 scene에는 태양,지구,달 그룹이 모두 포함된 group1을 추가!
위와 같이 태양,지구,달 같은 mesh가 각각의 그룹으로 짜여져 생성이 되었다!
그렇다면 여기에 rotation을 줘보자!
<script>
function draw() {
const delta = clock.getDelta();
group1.rotation.y += delta;
group2.rotation.y += delta;
group3.rotation.y += delta;
renderer.render(scene, camera);
renderer.setAnimationLoop(draw);
}
</script>
gif 변환하면서 속도가 느려지게 나오는데 실제론 빠르게 움직인다!
이와 같이 태양계를 공전하는 지구와 자전하는 지구, 지구를 공전하는 달까지 표현완료!