Three.js 강좌 #3 Geometry part-3

zzzzsb·2021년 12월 15일
0

Three.js

목록 보기
4/5

part-1,2에서 살펴본 Geometry들

part-3에서 다룰 Geometry

  • ShapeGeometry
  • TubeGeometry
  • LatheGeometry
  • ExtrudeGeometry
  • ExtrudeGeometry를 상속받는 TextGeometry

ShapeGeometry

  • 생성시에 Shape 클래스의 객체를 인자로 받음

Shape 클래스

  • 2차원 도형을 정의하기 위한 클래스

정사각형 도형 그리기

_setupModel() {
    // Shape 클래스 생성
    const shape = new THREE.Shape();
    // x,y 좌표로 도형 정의
    // (1,1) 로 이동 -> (1, -1)까지 선 그음 -> (-1, -1)까지 선 그음 -> (-1, 1)까지 선 그음
    shape.moveTo(1, 1);
    shape.lineTo(1, -1);
    shape.lineTo(-1, -1);
    shape.lineTo(-1, 1);
    // 도형을 닫으라고 지정
    shape.closePath();

    const geometry = new THREE.BufferGeometry();
    const points = shape.getPoints();
    geometry.setFromPoints(points);

    const material = new THREE.LineBasicMaterial({ color: 0xffff00 });
    const line = new THREE.Line(geometry, material);

    this._scene.add(line);
  }

하트 도형 그리기

_setupModel() {
    // Shape 클래스 생성
    const shape = new THREE.Shape();
    const x = -2.5, y = -5;
    shape.moveTo(x + 2.5, y + 2.5);
    shape.bezierCurveTo( x + 2.5, y + 2.5, x + 2, y, x, y);
    shape.bezierCurveTo(x - 3, y, x - 3, y + 3.5, x - 3, y + 3, 5);
    shape.bezierCurveTo(x - 3, y + 5.5, x - 1.5, y + 7.7, x + 2.5, y + 9, 5);
    shape.bezierCurveTo(x + 6, y + 7.7, x + 8, y + 4.5, x + 8, y + 3.5);
    shape.bezierCurveTo(x + 8, y + 3.5, x + 8, y, x + 5, y);
    shape.bezierCurveTo(x + 3.5, y, x + 2.5, y + 2.5, x + 2.5, y + 2.5);

    const geometry = new THREE.BufferGeometry();
    const points = shape.getPoints();
    geometry.setFromPoints(points);

    const material = new THREE.LineBasicMaterial({ color: 0xffff00 });
    const line = new THREE.Line(geometry, material);

    this._scene.add(line);
  }

하트 도형 mesh 생성

_setupModel() {
    // shape 
    const shape = new THREE.Shape();

    const x = -2.5, y= -5;
    shape.moveTo(x + 2.5, y + 2.5);
    shape.bezierCurveTo(x + 2.5, y + 2.5, x + 2, y, x, y);
    shape.bezierCurveTo(x - 3, y, x - 3, y + 3.5, x - 3, y + 3.5);
    shape.bezierCurveTo(x - 3, y + 5.5, x - 1.5, y + 7.7, x + 2.5, y + 9.5);
    shape.bezierCurveTo(x + 6, y + 7.7, x + 8, y + 4.5, x + 8, y + 3.5);
    shape.bezierCurveTo(x + 8, y + 3.5, x + 8, y, x + 5, y);
    shape.bezierCurveTo(x + 3.5, y, x + 2.5, y + 2.5, x + 2.5, y + 2.5);

    // ShapeGeometry의 생성자에 인자로 shape 객체를 전달함
    const geometry = new THREE.ShapeGeometry(shape);

    // 회색 색상의 재질로 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;
  }

카메라 시점 조절

  • _setupCamera()의 camera.position.z를 15로 늘려준다. 그러면 카메라 시점이 멀어짐
  • camera.position.z=15;


TubeGeometry

  • 어떤 곡선을 따라 원통이 이어지는 형태

Curve 클래스

  • 곡선을 정의
_setupModel() {
    // Curve 클래스를 상속받은 CustomSinCurve 클래스 정의
    // curve를 t 매개변수 방정식으로 정의함
    class CustomSinCurve extends THREE.Curve {
      constructor(scale) {
        super();
        this.scale = scale;
      }
      // 0~1 사이의 t값에 대한 curve의 구성 좌표 계산
      getPoint(t) {
        const tx = t * 3 - 1.5;
        const ty = Math.sin(2 * Math.PI * t);
        const tz = 0;
        return new THREE.Vector3(tx, ty, tz).multiplyScalar(this.scale);
      }
    }

    const path = new CustomSinCurve(4);

    const geometry = new THREE.BufferGeometry();
    // 좀더 부드러운 곡선을 얻으려면 getPoints 인자에 적당한 정수값 지정(기본값 5)
    // 인자값 -> curve를 구성하는 좌표의 개수
    const points = path.getPoints(30);
    geometry.setFromPoints(points);

    const material = new THREE.LineBasicMaterial({ color: 0xffff00 });
    const line = new THREE.Line(geometry, material);

    this._scene.add(line);
  }
  • const points = path.getPoints(); 일때
  • const points = path.getPoints(30); 일때

SinCurve Line 생성

_setupModel() {
    class CustomSinCurve extends THREE.Curve {
      constructor(scale) {
        super();
        this.scale = scale;
      }
      getPoint(t) {
        const tx = t * 3 - 1.5;
        const ty = Math.sin(2 * Math.PI * t);
        const tz = 0;
        return new THREE.Vector3(tx, ty, tz).multiplyScalar(this.scale);
      }
    }

    // tube가 이어지는 형태를 결정하기 위한 curve 객체 생성
    const path = new CustomSinCurve(4);
    // path라는 curve 객체를 TubeGeometry의 생성자에 전달함
    // TubeGeometry는 5개의 인자를 받음
    // 1. path
    // 2. tube의 진행 방향에 대한 분할 수(기본값 64)
    // 3. tube의 원통에 대한 반지름 크기(기본값 1)
    // 4. 원통에 대한 분할 수(기본값 8)
    // 5. 원통의 끝 단을 닫을지에 대한 여부(기본값 false)
    const geometry = new THREE.TubeGeometry(path);

    // 회색 색상의 재질로 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;
  }

TubeGeometry는 5개의 인자를 받음

  1. tube가 이어지는 형태를 결정하기 위한 curve 객체(path 객체)
  2. tube의 진행 방향에 대한 분할 수(기본값 64)
  3. tube의 원통에 대한 반지름 크기(기본값 1)
  4. 원통에 대한 분할 수(기본값 8)
  5. 원통의 끝 단을 닫을지에 대한 여부(기본값 false)
  • const geometry = new THREE.TubeGeometry(path, 40, 0.8, 8);
  • const geometry = new THREE.TubeGeometry(path, 40, 0.8, 8, true);

    - 불필요한 부분까지 면이 생성되고 있음(원인이 뭘까..?)

LatheGeometry

  • y축으로 회전하여 얻어지는 3차원 mesh
  • LatheGeometry를 정의하기 위해서는 먼저 무엇을 회전할 것인지 지정해야함

points로 구성되는 좌표들이 이루는 선

_setupModel() {
    // points로 구성되는 좌표들이 이루는 선
    const points = [];
    for(let i = 0; i < 10; ++i) {
      points.push(new THREE.Vector2(Math.sin(i * 0.2) * 3 + 3, (i - 5) * .8));
    }

    const geometry = new THREE.BufferGeometry();
    geometry.setFromPoints(points);

    const material = new THREE.LineBasicMaterial( { color: 0xffff00 });
    const line = new THREE.Line(geometry, material);

    this._scene.add(line); 
}

  • 이 선을 y축으로 회전시켜 생성되는 3차원 mesh를 얻기위해 LatheGeometry를 사용함

line을 y축으로 회전시켜 생성되는 3차원 mesh

_setupModel() {
    // points로 구성되는 좌표들이 이루는 선
    // -> 이 선을 y축으로 회전시켜 생성되는 3차원 mesh를 얻기위해 LatheGeometry를 사용함
    const points = [];
    for(let i = 0; i < 10; ++i) {
      points.push(new THREE.Vector2(Math.sin(i * 0.2) * 3 + 3, (i - 5) * .8));
    }

    const geometry = new THREE.LatheGeometry(points);

    // 회색 색상의 재질로 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;
 }

LatheGeometry는 4개의 인자를 받음

  1. 회전시킬 대상에 대한 좌표 배열
  2. 분할 수(기본값 12)
  3. 시작 각도(기본값 0)
    • 3시 방향이 0도
  4. 연장 각도(기본값 2PI)
  • const geometry = new THREE.LatheGeometry(points, 32, 0, Math.PI);
    • 180도만 회전해서 생성된 mesh

ExtrudeGeometry

  • 평면 shape에 깊이 값을 부여해주고, mesh의 윗면과 밑면을 비스듬하게 처리해주는 geometry
  • beveling: 비스듬하게 처리해주는 것

ExtrudeGeometry로 Heart shape 형성

_setupModel() {
    // heart shape
    const x = -2.5, y= -5;
    const shape = new THREE.Shape();
    shape.moveTo(x + 2.5, y + 2.5);
    shape.bezierCurveTo(x + 2.5, y + 2.5, x + 2, y, x, y);
    shape.bezierCurveTo(x - 3, y, x - 3, y + 3.5, x - 3, y + 3.5);
    shape.bezierCurveTo(x - 3, y + 5.5, x - 1.5, y + 7.7, x + 2.5, y + 9.5);
    shape.bezierCurveTo(x + 6, y + 7.7, x + 8, y + 4.5, x + 8, y + 3.5);
    shape.bezierCurveTo(x + 8, y + 3.5, x + 8, y, x + 5, y);
    shape.bezierCurveTo(x + 3.5, y, x + 2.5, y + 2.5, x + 2.5, y + 2.5);
     
    // ExtrudeGeometry settings
    const settings = {
      steps: 1,
      depth: 4,
      bevelEnabled: false,
      bevelThickness: 0.1,
      bevelSize: 0.1,
      bevelSegments: 1,
    };
    
    const geometry = new THREE.ExtrudeGeometry(shape, settings);

    // 회색 색상의 재질로 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;
 }

ExtrudeGeometry 설정값

// ExtrudeGeometry settings
    const settings = {
      steps: 1,
      depth: 4,
      bevelEnabled: false,
      bevelThickness: 0.1,
      bevelSize: 0.1,
      bevelSegments: 1,
    };
  1. steps: 깊이 방향으로의 분할 수(기본값 1)
  2. depth: 깂이 값(기본값 100)
  3. bevelEnabeld: 베벨링 처리를 할 것인지 여부(기본값 true)
  • true로 설정되어야 아래의 bevel 속성값이 적용됨
  1. bevelThickness: 베벨링의 두께값(기본값 6)
  2. bevelSize: shape의 외곽선으로부터 얼마나 멀리 베벨링할 것인지 거리(기본값 2)
  3. bevelSegments: 베벨링 단계 수(기본값 3)
// ExtrudeGeometry settings
    const settings = {
      steps: 2,
      depth: 4,
      bevelEnabled: true,
      bevelThickness: 1.6,
      bevelSize: 2,
      bevelSegments: 6,
    };


TextGeometry

  • ExtrudeGeometry의 파생 클래스

  • 폰트 데이터 필요

  • ttf 등과 같은 폰트 파일을 three.js에서 폰트로 사용할 수 있는 포맷으로 변경해 사용함(형식 json)

  • 폰트를 로드하기 위해서는 FontLoader 클래스 필요

  • const fontLoader = new THREE.FontLoader();

  • 폰트 데이터를 비동기적으로 불러와야함

  • loadFont 비동기 함수 추가

_setupModel() {
    // FontLoader 클래스
    const fontLoader = new FontLoader();
    // 폰트 데이터를 비동기적으로 불러오기 위한 비동기 함수
    async function loadFont(that) {
      const url = "../examples/fonts/helvetiker_regular.typeface.json";
      const font = await new Promise((resolve, reject) => {
        fontLoader.load(url, resolve, undefined, reject);
      });

      // TextGeometry 생성
      const geometry = new TextGeometry("Scribubble", {
        font: font,
        size: 5,
        height: 1.5,
        curveSegments: 4,
        // setting for ExtrudeGeometry
        bevelEnabled: true,
        bevelThickness: 0.7,
        bevelSize: .7,
        bevelSegments: 2
      });

      // 함수 내부에서 geometry 생성하고 있기때문에 아래 코드도 비동기 함수 내에서 호출 
      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);

      that._scene.add(group);
      that._cube = group;
    };
    // this인자와 함께 호출
    loadFont(this);
 }  

콘솔 오류 "THREE.FontLoader has been moved to /examples/jsm/loaders/FontLoader.js"

강의 그대로 코드 입력을 했는데 아무것도 안떠서 원인이 뭘까 찾아보다가.. 유튜브 댓글에서 똑같은 에러메시지를 발견했다.
원인은 최근 three.js가 업데이트 되어 FontLoader와 TextGeometry가 더이상 THREE.~으로 참조되지 않는다고 한다.

해결방안

  • FontLoader와 TextGeometry 클래스를 아래와 같이 import한다.
import { FontLoader } from "../examples/jsm/loaders/FontLoader.js";
import { TextGeometry } from "../examples/jsm/geometries/TextGeometry.js";
  • FontLoader와 TextGeometry를 아래와 같이 생성해준다.
const fontLoader = new FontLoader();
const geometry = new TextGeometry();

TextGeometry 생성자가 받는 인자들

const geometry = new TextGeometry("Scribubble", {
        font: font,
        size: 5,
        height: 1.5,
        curveSegments: 4,
        // setting for ExtrudeGeometry
        bevelEnabled: true,
        bevelThickness: 0.7,
        bevelSize: .7,
        bevelSegments: 2
});
  1. 3차원 mesh로 설정할 문자열
  2. 설정 값
  • font: fontLoader를 통해 얻어온 폰트 객체

  • size: 텍스트 mesh의 크기(기본값 100)

  • height: 깊이 값(기본값 50)

  • curveSegments: 하나의 커브를 구성하는 정점의 개수(기본값 12)

  • bevelEnabled: 베벨링 처리를 할것인지 여부(기본값 true)

  • bevelThickness: 베벨링에 대한 두께(기본값 6)

  • bevelSize: shape의 외곽선으로부터 얼마나 멀리 베벨링할 것인지에 대한 거리값(기본값 2)

  • bevelSegment: 베벨링 단계수(기본값 3)

profile
성장하는 developer

0개의 댓글