인프런 커뮤니티 질문&답변

dkwkdnwk님의 프로필 이미지
dkwkdnwk

작성한 질문수

Three.js로 시작하는 3D 인터랙티브 웹

회전

reorder('YXZ') 동작

해결된 질문

작성

·

435

·

수정됨

0

강의 너무 재밌게 잘 듣고 있습니다!

roatation에서 reorder를 했을때 어떻게 동작하는 것인지 정확히 이해가 가지 않아서 질문 남겨봅니다~

reorder('YXZ') 는 어떤 것을 위해 설정한 것인지, YXZ 순서가 무엇을 의미하는지 궁금합니다~ (rotation.x, rotation.y 로 값을 설정하는데, YXZ 순서는 어떤 것을 설정해주는 것인지요?)

아래 코드처럼 테스트했을때, 1,2번 위치에 설정했을때는 잘 동작하고, 마지막에 설정했을때는 기존처럼 동작하는데, reorder() 해주는 위치도 궁금합니다. (한번 설정하면 계속 동작하는 것인지?)

// 1. 회전 전에 설정
mesh.rotation.reorder('YXZ');

mesh.rotation.y = THREE.MathUtils.degToRad(45);

// 2. 중간에 설정
mesh.rotation.reorder('YXZ');

mesh.rotation.x = THREE.MathUtils.degToRad(20);

// 3. 마지막에 설정
mesh.rotation.reorder('YXZ');

 

답변 1

1

안녕하세요! 제가 강사님은 아니지만 수강생으로써 똑같은 고민을 해서 답변드립니다.

 

XYZ일 경우 매트릭스에 업데이트 되기 전까지 코드 상의 순서와 상관없이

rotation.x -> rotation.y -> rotation.z 순으로 적용됩니다.

예를 들어 rotaion.x, rotation.y 를 각각 90도 돌리는 상황에서

XYZ 순서일 경우 육면체에서 x방향 회전으로 윗면이 정면으로 오고,

그 상태에서 y축 방향으로 90도를 돌게 되겠죠

최종적으로는 최초 위치 기준 오른쪽면이 정면으로 오게됩니다.

 

반면 YXZ 순서일 경우 y방향 회전이 먼저 되서 오른쪽면이 정면으로 먼저 오고,

그 후 x축 방향으로 회전이 되어 최초 위치 기준 윗면이 정면으로 오게 됩니다.

 

reorder 위치는 저도 잘 모르겠네요.. 실험이 필요해보입니다 ㅠ

dkwkdnwk님의 프로필 이미지
dkwkdnwk
질문자

오오 nureongi0214님, 감사합니다!

답변해주신 부분 바탕으로 조금더 찾아보고 실험해보고 깨달음을 얻었습니다ㅎㅎ

제가 헷갈렸던 부분은 회전축이었는데요~ rotation 은 world 좌표계가 아니라, object 내부좌표계를 사용하기 때문에, 한번 회전하고 나면 축이 변경되서, 회전하는 순서를 잘 정해주는 게 필요하다는 것을 알았습니다!

https://en.wikipedia.org/wiki/Euler_angles

예로 들어주신 부분 또한 아래처럼 테스트해보았는데요~

  • 1번째줄: 육면체 색상 확인

  • 2번째줄: x 축으로 90도씩 회전

  • 3번째줄: y 축으로 90도씩 회전

  • 4번째줄: z 축으로 30도씩 회전

  • 5번째줄: x, y 축으로 90도 회전

  • 6번째줄: 수업 예제

2,3,4 번째 줄에서 회전 후에 object 내부 축이 변경되는 것을 확인할 수 있었습니다.

그리고 5번째 줄에서 말씀주신 예제로 테스트 했을 때, 아래와 같이 동작하는 것을 확인했습니다~

  • XYZ 순서: x 회전(윗면-연두색이 정면) -> y 회전 (y축이 변경되었기 때문에 그대로 윗면-연두색이 정면)

  • YXZ 순서: y 회전(왼쪽면-파랑색이 정면) -> x 회전 (x축이 변경되었기 때문에 왼쪽면-파랑색이 정면)

     

image

import * as THREE from 'three';

// ----- 주제: transform

export default function example() {
  // Renderer
  const canvas = document.querySelector('#three-canvas');
  const renderer = new THREE.WebGLRenderer({
    canvas,
    antialias: true,
  });
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setPixelRatio(window.devicePixelRatio > 1 ? 2 : 1);

  // Scene
  const scene = new THREE.Scene();

  // Camera
  const camera = new THREE.OrthographicCamera(
    -(window.innerWidth / window.innerHeight), // left
    window.innerWidth / window.innerHeight, // right
    1, // top
    -1, // bottom
    0.1, // near
    1000 // far
  );

  camera.position.y = 1;
  camera.position.z = 10;
  camera.zoom = 0.1;
  camera.updateProjectionMatrix();
  scene.add(camera);

  // Light
  const ambientLight = new THREE.AmbientLight('white', 0.5);
  scene.add(ambientLight);

  const directionalLight = new THREE.DirectionalLight('white', 1);
  directionalLight.position.x = 1;
  directionalLight.position.z = 2;
  scene.add(directionalLight);

  // Mesh
  const geometry = new THREE.BoxGeometry(1, 1, 1);
  const material = [
    new THREE.MeshStandardMaterial({ color: 'red' }), // wireframe: true
    new THREE.MeshStandardMaterial({ color: 'dodgerblue' }),
    new THREE.MeshStandardMaterial({ color: 'yellowgreen' }),
    new THREE.MeshStandardMaterial({ color: 'purple' }),
    new THREE.MeshStandardMaterial({ color: 'pink' }),
    new THREE.MeshStandardMaterial({ color: 'orange' }),
  ];

  // AxesHelper
  const axesHelper = new THREE.AxesHelper(3);
  scene.add(axesHelper);

  function createMesh() {
    const mesh = new THREE.Mesh(geometry, material);
    const axis = new THREE.AxesHelper(1);
    mesh.add(axis); // object의 axis 표시
    scene.add(mesh);
    return mesh;
  }

  const meshes = [];
  for (let i = 0; i < 6; i++) {
    const mesh_column = [];
    for (let j = 0; j < 10; j++) {
      const mesh = createMesh();
      mesh.position.set(i * -2 + 5, j * -3 + 10, 0);
      mesh_column.push(mesh);
    }
    meshes.unshift(mesh_column);
  }

  meshes[0][0].rotation.set(THREE.MathUtils.degToRad(20), THREE.MathUtils.degToRad(20), 0);
  meshes[1][0].rotation.set(THREE.MathUtils.degToRad(20), THREE.MathUtils.degToRad(110), 0);
  meshes[2][0].rotation.set(THREE.MathUtils.degToRad(20), THREE.MathUtils.degToRad(200), 0);
  meshes[3][0].rotation.set(THREE.MathUtils.degToRad(20), THREE.MathUtils.degToRad(290), 0);
  meshes[4][0].rotation.set(THREE.MathUtils.degToRad(200), THREE.MathUtils.degToRad(-20), 0);

  // x축 회전
  meshes[1][1].rotation.x = THREE.MathUtils.degToRad(90);
  meshes[2][1].rotation.x = THREE.MathUtils.degToRad(180);
  meshes[3][1].rotation.x = THREE.MathUtils.degToRad(270);
  meshes[4][1].rotation.x = THREE.MathUtils.degToRad(360);

  // y축 회전
  meshes[1][2].rotation.y = THREE.MathUtils.degToRad(90);
  meshes[2][2].rotation.y = THREE.MathUtils.degToRad(180);
  meshes[3][2].rotation.y = THREE.MathUtils.degToRad(270);
  meshes[4][2].rotation.y = THREE.MathUtils.degToRad(360);

  // z축 회전
  meshes[1][3].rotation.z = THREE.MathUtils.degToRad(30);
  meshes[2][3].rotation.z = THREE.MathUtils.degToRad(60);
  meshes[3][3].rotation.z = THREE.MathUtils.degToRad(90);
  meshes[4][3].rotation.z = THREE.MathUtils.degToRad(120);
  meshes[5][3].rotation.z = THREE.MathUtils.degToRad(150);

  // x, y 90도 회전
  meshes[0][4].rotation.x = THREE.MathUtils.degToRad(90);
  meshes[0][4].rotation.y = THREE.MathUtils.degToRad(90);
  meshes[1][4].rotation.y = THREE.MathUtils.degToRad(90);
  meshes[1][4].rotation.x = THREE.MathUtils.degToRad(90);
  meshes[2][4].rotation.reorder('YXZ');
  meshes[2][4].rotation.x = THREE.MathUtils.degToRad(90);
  meshes[2][4].rotation.y = THREE.MathUtils.degToRad(90);
  meshes[3][4].rotation.reorder('YXZ');
  meshes[3][4].rotation.y = THREE.MathUtils.degToRad(90);
  meshes[3][4].rotation.x = THREE.MathUtils.degToRad(90);

  // 수업 예제
  meshes[0][5].rotation.x = THREE.MathUtils.degToRad(20);
  meshes[1][5].rotation.y = THREE.MathUtils.degToRad(45);
  meshes[2][5].rotation.x = THREE.MathUtils.degToRad(20);
  meshes[2][5].rotation.y = THREE.MathUtils.degToRad(45);
  meshes[3][5].rotation.reorder('YXZ');
  meshes[3][5].rotation.x = THREE.MathUtils.degToRad(20);
  meshes[3][5].rotation.y = THREE.MathUtils.degToRad(45);

  function draw() {
    renderer.render(scene, camera);
  }

  function setSize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.render(scene, camera);
  }

  // 이벤트
  window.addEventListener('resize', setSize);

  draw();
}

 

참고

dkwkdnwk님의 프로필 이미지
dkwkdnwk

작성한 질문수

질문하기