ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Three.js module 활용 - (7) Custom Geometry
    Three.js 2022. 9. 27. 10:25

    [이전 글]

     

    Three.js module 활용 - (6) Texture Material

    [이전 글] Three.js module 활용 - (5) Material [이전 글] Three.js module 활용 - (4) Scene Graph [이전 글] Three.js module 활용 - (3) Geometry 마무리 [이전 글] Three.js module 활용 - (2) Geometry [이..

    chaeng03.tistory.com

     


    해당 게시글은 2022.05.11에 깃허브로 작성되었으며

    아래 유튜브 강의 영상을 수강하며 공부한 내용을 기재했습니다.


     

    지금까지 우리는 Three.js에서 제공하는 다양한 Geometry에 대해 배워왔다.

    이번에는 사용자가 원하는 형태의 Geometry를 직접 만들어보는 방법에 대해 알아보자.

     

    Geometry는 Three.js에서 BufferGeometry CLASS를 통해 정의되는데

    해당 CLASS를 통해 정의될 수 있는 속성은 다음과 같다.

    - position
     BufferAttribute로 지정 | Geometry를 구성하는 3차원 좌표에 대한 정점

    - normal
     BufferAttribute로 지정 | 각 정점에 대한 수직 벡터

    - color
     BufferAttribute로 지정 | 각 정점에 대한 색상

    - uv
     BufferAttribute로 지정 | 각 정점에 대한 Texture 맵핑좌표

    - Vertex Index
     BufferAttribute의 setIndex 메소드로 지정 | position 속성으로 지정된 정점에 대한 index 배열로 지정

     


     

    1. 세팅

    01_basic.html, 01_basic.css, 01_basic.js를 복사하여 붙여넣기 한 후 파일명을 05_custom_geometry로 변경한다.

    그에 맞춰 05_custom_geometry.html에서 css와 js를 import하는 부분을 변경된 파일명에 맞춰 수정해보자.

     

    이제 05_custom_geometry.js 파일 중 불필요한 코드를 제거해보자.

    우선 _setupModel 함수에 있는 모든 코드를 제거하고 update 함수를 다음과 같이 변경한다.

    update(time){
      time *= 0.001;
    }

     


     

    2. position

    이어서 _setupModel 함수에서 사각형을 구성하는 4개의 정점을 배열 객체로 다음과 같이 정의해보자.

    _setupModel() {
      const rawPositions = [
        -1, -1, 0,
        1, -1, 0,
        -1, 1, 0,
        1, 1, 0
      ];
    }

    그리고 다음 코드로 rawPositions 배열을 Float32Array CLASS의 객체로 랩핑하고

    BufferGeometry 객체를 생성하자.

    const positions = new Float32Array(rawPositions);
    
    const geometry = new THREE.BufferGeometry();

     

    그리고 이 geometry 객체의 position을 설정하기 위해 setAttribute의 첫번째 인자로 position을 지정하고 두번째 인자로 BufferAttribute CLASS를 통해 앞에서 랩핑한 정점 데이터를 지정하고 있다.

    geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));

     

    이제 vertex index를 지정해야 하는데 이는 삼각형 면을 정의한다.

    정점의 index를 지정할 때 주의해야 할 사항이 있는데 바로 반시계 방향인 면이 앞면이기에 삼각형을 구성하는 정점의 배치 순서가 반시계 방향이어야 한다는 점이다.

     

    이런 index 구성을 이용해서 다음처럼 Vertex Index를 지정할 수 있다.

    참고로 index는 0이 시작값이므로 첫번째 index는 0이 된다.

    geometry.setIndex([
      0, 1, 2,
      2, 1, 3
    ])

     

    그리고 Geometry를 렌더링하기 위해 Mesh로 만들어줘야 하므로 다음과 같이 코드를 추가하여 렌더링해보자.

    const material = new THREE.MeshPhongMaterial({color: 0xff0000});
    
    const box = new THREE.Mesh(geometry, material);
    this._scene.add(box);

     

    화면을 보면 아무것도 표시되어 있지 않은데 이는 정점에 대한 법선 벡터가 지정되어 있지 않기 때문이다.

    법선벡터는 광원이 Mesh의 표면에 미치는 입사각과 반사각을 계산하여 재질과 함께 표면의 색상을 결정하는 데 사용된다.

     

    다음 코드를 통해 자동으로 모든 정점에 대해 법선 벡터를 지정 할 수 있다.

    geometry.computeVertexNormals();

    그럼 다음과 같이 화면에 결과가 표시되는 것을 확인할 수 있다.

     


     

    3. normal

    하지만 우리는 Geometry의 computeVertexNormals 메소드를 통한 법선벡터를 자동 계산하지 않고 직접 지정할텐데

    이를 위해 computeVertexNormals를 삭제하고 법선 벡터에 대한 배열 데이터를 다음처럼 추가한다.

     

    position에 설정했던 과정 그대로 진행한다.

    보면 4개의 좌표에 대해 모두 (0, 0, 1)인데 이는 Mesh의 면으로 봤을 때 면에 대해 수직인 벡터가 모두 (0, 0, 1)이기 때문이다.

    Float32Array CLASS 객체로 랩핑하고 이 법선벡터를 Geometry에 지정한다.

    const rawNormals = [
      0, 0, 1,
      0, 0, 1, 
      0, 0, 1, 
      0, 0, 1
    ];
    
    const normals = new Float32Array(rawNormals);
    
    geometry.setAttribute('normal', new THREE.BufferAttribute(normals, 3));

     

     

    그리고 결과를 보면 앞에서와 동일한 결과를 볼 수 있다는 것을 알 수 있다.

     

    여기서 Mesh에 대해 법선 벡터를 시각화하기 위해 VertexNormalsHelper CLASS를 이용해보자.

    이 CLASS를 사용하기 위해 05_custom_geometry.html 파일에서 css link 부분 밑에 아래 코드를 추가하고

    <script type="importmap">
      {
          "imports": {
              "three": "../build/three.module.js"
          }
      }
    </script>

     

    05_custom_geometry.js 파일의 최상단에서 다음과 같이 import하자.

    import {VertexNormalsHelper} from '../examples/jsm/helpers/VertexNormalsHelper.js';

     

    이어서 _setupModel 함수에 아래 코드를 추가하고 결과를 보면 사각형을 구성하는 4개의 정점에 법선벡터인 노란색의 선이 표시되고 있는 걸 볼 수 있다.

    const helper = new VertexNormalsHelper(box, 0.1, 0xffff00);
    this._scene.add(helper);

     

    마우스로 화면을 회전시키기 위해 OrbitControls를 설치하자.

    마찬가지로 CLASS 사용을 위해 05_custom_geometry.js 파일 최상단에 import하고

    import {OrbitControls} from '../examples/jsm/controls/OrbitControls.js';

    함수를 호출하는 부분에서 _setupControls 함수도 호출한다.

    this._setupControls();

     

    이 함수의 내용은 다음과 같다.

    _setupControls() {
      new OrbitControls(this._camera, this._divContainer);
    }

     

    그럼 마우스로 3D Object를 돌려볼 수 있는데 보면 법선 벡터가 Mesh의 표면에 수직이라는 것을 명확히 알 수 있다.

     


     

    4. color

    color 속성을 이용하면 각 vertex마다 색상값을 지정할 수 있는데 이를 위해 각 정점에 대한 색상값 지정 배열 객체를 추가한다.

    const rawColors = [
      1, 0, 0,
      0, 1, 0,
      0, 0, 1,
      1, 1, 0
    ];

    앞서 진행한 과정과 동일하게 이 배열 객체를 Float32Array CLASS 객체로 랩핑하고 geometry의 color 속성에 지정한다.

    const colors = new Float32Array(rawColors);
    
    geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));

     

    이어서 적용을 위해 재질에 다음 속성을 추가해보자.

    const material = new THREE.MeshPhongMaterial({color: 0xff0000, vertexColors: true});

    그러고 결과를 보면 Mesh의 표면 색상이 변경되었는데 원래 재질 자체가 빨간색이므로 각 vertex에 적용된 색상이 빨간색의 영향을 받는다.

    vertex에 지정된 색상만 고유하게 표현되도록 하기 위해 색상값을 하얀색으로 변경한다.

     

    그럼 다음과 같이 각 vertex에 지정된 색상대로 Mesh가 표현되는 것을 알 수 있다.

     


     

    5. uv

    uv는 Texture 맵핑을 위한 속성인데 이 uv 좌표를 담고 있는 배열 객체를 다음처럼 추가해보자.

    const rawUVS = [
      0, 0,
      1, 0,
      0, 1,
      1, 1
    ];
    
    const uvs = new Float32Array(rawUVS);
    
    geometry.setAttribute('uv', new THREE.BufferAttribute(uvs, 2));

    uv 좌표는 2개가 하나의 좌표를 구성하므로 BufferAttribute의 두번째 인자를 2로 설정한다.

     

    다음으로 Texture 맵핑은 이미지가 필요한데 다음과 같이 이미지를 추가하고 재질에 다음 속성을 추가하자.

    const TextureLoader = new THREE.TextureLoader();
    const map = TextureLoader.load('../examples/textures/uv_grid_opengl.jpg');
    
    const material = new THREE.MeshPhongMaterial({
      color: 0xffffff,
      vertexColors: true,
      map: map
    });

    결과를 보면 vertex에 지정된 색상과 Texture 이미지의 색상이 서로 섞여서 렌더링되고 있다.

     


     

    이렇게 사용자 Geometry에 대해 알아봤다.

    다음 시간에는 광원에 대해 알아보는 시간을 갖자.

     

    [Three.js module 활용 - (7) Custom Geometry 전체 코드]

     

    GitHub - rlacodud/UID

    Contribute to rlacodud/UID development by creating an account on GitHub.

    github.com

Designed by Tistory.