Three.js 강좌 #3 Geometry part-1

3. Geometry

three.js의 기본 구성요소

mesh 생성시 필요한것

  • mesh의 형상을 정의하는 geometry
  • 색상/투명도 등을 지정할 수 있는 material
  • 지난 #2 강의 예제 코드에서는 geometry에 육면체의 형상을 정의하는 BoxGeometry를 사용함

three.js에서 제공하는 기본 Geometry

  • 기본적으로 BufferGeometry를 상속받고 있음
  • 자주색으로 표시된 geometry 살펴볼 예정
  • ParametricGeometry: 수학적 함수식에 2개의 값을 입력하여 얻을수 있는 좌표로 구성되는 지오메트리
  • EdgesGeometry: 지오메트리를 구성하는 인접면의 각도에 따라 지오메트리를 재구성함

PolyhedrongGeometry: 다면체를 구성하는 지오메트리

이를 상속받는 지오메트리들

  • IcosahedronGeometry(20면체)
  • OctahedrongGeometry(8면체)
  • DodecahedronGeometry(12면체)
  • TetrahedronGeometry(4면체)

Geometry의 형상을 정의하기 위한 데이터

  • 지오메트리는 3차원 오브젝트의 형상을 정의한다.
  1. 정점(Vertex): 형상을 정의함. xyz축에 대한 좌표
  2. 정점 인덱스: 3차원 오브젝트의 면을 구성하는 정점에 대한 인덱스
  3. 수직 벡터: 정점에 대한 수직 벡터
  4. 정점 색상: 벡터 정점의 색상
  5. 텍스쳐 맵핑을 위한 UV 좌표
  6. 사용자 정의 데이터

-> 이러한 데이터들은 3차원으로 시각화 될 때 GPU에 한번에 전달되어 빠르게 처리된다.

WireframeGeometry, BoxGeometry

02-geometry.html

<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="02-geometry.css">
  <script type="module" src="02-geometry.js" defer></script>
</head>
<body>
  <div id="webgl-container"></div>
</body>
</html>

02-geometry.js

회색에 노란 선이 있는 정육면체 오브젝트로 변경

_setupModel() {
    // 가로, 세로 깊이 크기가 모두 1인 geometry
    /* 
    BoxGeometry: 가로, 세로, 깊이 크기와 함께 가로,세로,깊이 각각에 대한 분할(Segments)수로 정의됨(분할수는 지정하지 않으면 기본값 1)
    */
    //const geometry = new THREE.BoxGeometry(1, 1, 1);
    // 가로, 세로, 깊이 방향으로 2개씩 분할됨 
    const geometry = new THREE.BoxGeometry(1, 1, 1, 2, 2, 2);
    // 회색 색상의 재질로 mesh 타입 오브젝트 생성
    const fillMaterial = new THREE.MeshPhongMaterial({ color: 0x515151 });
    const cube = new THREE.Mesh(geometry, fillMaterial);

    // 노란색 선 재질 생성
    const lineMaterial = new THREE.LineBasicMaterial({color: 0xffff00});
    // 앞에서 만든 geometry를 이용해 line타입 오브젝트 생성
    /* 
    WireframeGeometry 클래스: 와이어프레임 형태로 지오메트리 표현
    만약 WireframeGeometry 적용하지 않고 생성하면 모델의 모든 외곽선이 표시되지 않음.
    */
    const line = new THREE.LineSegments(
      new THREE.WireframeGeometry(geometry), lineMaterial);
    
    // mesh 오브젝트와 line 오브젝트를 하나의 오브젝트로 다루기 위해 그룹으로 묶음
    const group = new THREE.Group()
    /*
    cube를 삭제하면 노란색 line만 보임,
    line을 삭제하면 회색 cube만 보임
    */
    group.add(cube);
    group.add(line);
    
    // 그룹 객체를 scene에 추가
    this._scene.add(group);
    this._cube = group;
  }

BoxGeometry 생성 시 Segments 수

  • 분할수는 지정하지 않으면 기본값 1이지만, 다음과 같이 분할수를 2로 지정해주면 실행결과가 다음과 같다.
  • const geometry = new THREE.BoxGeometry(1, 1, 1, 2, 2, 2);
  • 가로, 세로, 깊이에 대해 2개로 분할됨을 확인할 수 있다..

자동회전이 아닌 마우스로 회전할 수 있도록 변경

  • 오브젝트 자동회전 하게 했던 코드 주석처리
  • 마우스 회전에 필요한 OrbitControls 클래스 import
  • App 클래스에 컨트롤들 정의하는데 사용할 메서드인 _setupControls() 정의
  • _setupControls() 함수 생성
import * as THREE from '../build/three.module.js';

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

class App {
  constructor() {
    const divContainer = document.querySelector("#webgl-container");
    this._divContainer = divContainer;

    const renderer = new THREE.WebGL1Renderer({ antialias: true });
    renderer.setPixelRatio(window.devicePixelRatio);
    divContainer.appendChild(renderer.domElement);
    this._renderer = renderer;

    const scene = new THREE.Scene();
    this._scene = scene;

    this._setupCamera();
    this._setupLight();
    this._setupModel();
    // 컨트롤들 정의하는데 사용하는 메서드
    this._setupControls();

    window.onresize = this.resize.bind(this);
    this.resize();

    requestAnimationFrame(this.render.bind(this));
  }

  //
  _setupControls() {
    // OrbitControls 객체를 생성할 때는 카메라 객체와 마우스 이벤트를 받는 DOM 요소 필요
    // 이벤트를 받는 DOM 요소는 id가 webgl-container인 div 요소
    new OrbitControls(this._camera, this._divContainer);

  }
 
  update(time) {
    time *= 0.001;

    //오브젝트 자동 회전
    //this._cube.rotation.x = time;
    //this._cube.rotation.y = time;
  }
}

02-geometry.js 전체 코드

import * as THREE from '../build/three.module.js';
import { OrbitControls } from '../examples/jsm/controls/OrbitControls.js'

class App {
  constructor() {
    const divContainer = document.querySelector("#webgl-container");
    this._divContainer = divContainer;

    const renderer = new THREE.WebGL1Renderer({ antialias: true });
    renderer.setPixelRatio(window.devicePixelRatio);
    divContainer.appendChild(renderer.domElement);
    this._renderer = renderer;

    const scene = new THREE.Scene();
    this._scene = scene;

    this._setupCamera();
    this._setupLight();
    this._setupModel();
    this._setupControls();

    window.onresize = this.resize.bind(this);
    this.resize();

    requestAnimationFrame(this.render.bind(this));
  }

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

  }
  _setupCamera() {
    const width = this._divContainer.clientWidth;
    const height = this._divContainer.clientHeight;
    const camera = new THREE.PerspectiveCamera(
      75,
      width / height,
      0.1,
      100
    );
    camera.position.z=2;
    this._camera = camera;
  }
  
  _setupLight() {
    const color = 0xffffff;
    const intensity = 1;
    const light = new THREE.DirectionalLight(color, intensity);
    light.position.set(-1, 2, 4);
    this._scene.add(light);
  }
  
  _setupModel() {
    const geometry = new THREE.BoxGeometry(1, 1, 1);
    //const geometry = new THREE.BoxGeometry(1, 1, 1, 2, 2, 2);
    const fillMaterial = new THREE.MeshPhongMaterial({ color: 0x515151 });
    const cube = new THREE.Mesh(geometry, fillMaterial);

    const lineMaterial = new THREE.LineBasicMaterial({color: 0xffff00});
    const line = new THREE.LineSegments(
      new THREE.WireframeGeometry(geometry), lineMaterial);
    
    const group = new THREE.Group()
    group.add(cube);
    group.add(line);

    this._scene.add(group);
    this._cube = group;
  }

  resize() {
    const width = this._divContainer.clientWidth;
    const height = this._divContainer.clientHeight;

    this._camera.aspect = width / height;
    this._camera.updateProjectionMatrix();

    this._renderer.setSize(width, height);
  }

  render(time) {
    this._renderer.render(this._scene, this._camera);
    this.update(time);
    requestAnimationFrame(this.render.bind(this));
  }

  update(time) {
    time *= 0.001;

    //오브젝트 자동 회전
    //this._cube.rotation.x = time;
    //this._cube.rotation.y = time;
  }
}

window.onload = function() {
  new App();
}  

실행 결과

좋은 웹페이지 즐겨찾기