본문 바로가기

three.js

[three.js][typescript] - Material(1)

728x90

 

 

Three.js 의 Object3D의 파생클래스는 위와 같다.

 

Point는 Geometry를 구성하는 좌표 하나 하나를 렌더링하며

 

Line은 좌표를 통해 선을 만들어 렌더링하며

 

Mesh는 좌표를 바탕으로 만들어진 모델을 만들어 렌더링한다

 

 

 

그리고 이 Point, Line, Mesh에 맞게 사용할 수 있는 재질의 종류는 위와 같다.

 

위를 포함한 다양한 종류의 Material 클래스들은 Material 클래스를 상속받는다.

 

코드를 통해 알아보자.

 

import './style.css'
import * as THREE from 'three'

class App {
  private renderer: THREE.WebGLRenderer //Renderer Field 추가
  private domApp: Element 
  private scene: THREE.Scene
  private camera?: THREE.PerspectiveCamera //?를 붙이면 PerspectiveCamera Type이나 Undefined Type을 가질 수 있음.(Optional Properties)

  constructor(){
    console.log("YooHwanIhn");
    this.renderer = new THREE.WebGLRenderer({antialias:true}) // 안티-알리아스 : 높은 렌더링 결과를 얻기 위해 픽셀 사이에 계단 현상을 방지하는 효과 추가
    this.renderer.setPixelRatio(Math.min(2, window.devicePixelRatio)) // 고해상도에서 깨짐 방지를 위해 픽셀 비율 지정

    this.domApp = document.querySelector('#app')! 
    this.domApp.appendChild(this.renderer.domElement) // renderer.domeElement : canvas Type의 DOM 객체

    this.scene = new THREE.Scene()

    this.setupCamera()
    this.setupLight()
    this.setupModels()
    this.setupEvents()
  }

  private setupCamera(){
    const width = this.domApp.clientWidth
    const height = this.domApp.clientHeight
    
    this.camera = new THREE.PerspectiveCamera(75, width/height, 0.1, 100)
    this.camera.position.z = 2  // (0, 0, 2)
  }

  private setupLight(){
    //빛의 색상 값과 빛의 강도
    const color = 0xffffff  // white
    const intensity = 1
    const light = new THREE.DirectionalLight(color, intensity)  //광원
    light.position.set(-1, 2, 4)

    this.scene.add(light)
  }

  private setupModels(){

  }

  //실제 이벤트와 렌더링 처리를 다룰 메서드
  private setupEvents(){
    window.onresize = this.resize.bind(this); // html의 size가 변경될 때마다 호출되는 함수(addEventListener 느낌??)
    this.resize();

    this.renderer.setAnimationLoop(this.render.bind(this)) // 연속적으로 render 메서드 호출(모니터 Frame에 맞게)
  }

  // html창 resize시 호출할 함수
  private resize(){
    const width = this.domApp.clientWidth
    const height = this.domApp.clientHeight

    //앞선 setUpCamera에서 설정한 camera 정보를 다시 수정해줌.
    const camera = this.camera
    if(camera){
      camera.aspect = width / height
      camera.updateProjectionMatrix() // 카메라의 값이 변경되었다면 수정하도록 함.
    }

    //renderer도 마찬가지로 사이즈 수정함
    this.renderer.setSize(width, height)
  }

  private update(time: number){
    time *= 0.001 // ms -> s 단위로 변경
  }

  private render(time: number){
    // time : setAnimationLoop의 값에 의해서 결정되는데 단위는 ms
    this.update(time)

    this.renderer.render(this.scene, this.camera!)
  }
}

new App()

 

Cube Rotation에서 사용하던 코드를 그대로 재활용하여 Point Material을 알아볼 것이다.

 

private setupModels(){
    const geometry = new THREE.SphereGeometry()
    const material = new THREE.PointsMaterial({
      color: 0xff0000,
      size: 5,
      sizeAttenuation: false  // point의 크기가 camera의 거리에 따라 조절되도록 하는 option
    })
    const points = new THREE.Points(geometry, material)
    this.scene.add(points)
  }
  
  private setupCamera(){
    const width = this.domApp.clientWidth
    const height = this.domApp.clientHeight
    
    this.camera = new THREE.PerspectiveCamera(75, width/height, 0.1, 100)
    this.camera.position.z = 2  // (0, 0, 2)

    new OrbitControls(this.camera, this.domApp as HTMLElement)
  }

 

SphereGeometry는 많이 다뤄봐서 이해할 것이다.

 

구형의 Geometry를 추가한 뒤, 이 재질을 PointsMaterial로 설정하여 scene객체에 렌더링하는 방식이다.

 

그리고 이를 카메라로 확인하기 편하게 OrbitControls를 Camera에 추가해준다.

(import를 해주어야 한다.)

 

 

 

기존의 Mesh로 면을 확인했을때와 다르게 Point를 사용하면 점으로 구성된 구 형태를 확인할 수 있다.

 

코드를 조금 수정해보자

 

 

 

Color의 값을 Green으로 변경시키고, sizeAttenuation 옵션을 true로 변경시켰다.


그리고 GUI를 사용하기 위해 import 한다. (lil gui 사용)

 

 gui를 사용해 point의 size를 조절 가능하도록 변경하였다.

 

결과는 다음과 같다.

 

 

 

sizeAttenuation 옵션을 켰기때문에 카메라에 가까운 점이 더 크게 보이도록 변경되었다.

 

그리고 Color값은 Green을 지정하였는데 기존의 0xff0000 컬러가 나타난다.

 

 

Three.js는 Color 객체를 통해 Model을 생성 후 Material의 color를 변경 가능하다는 것을 확인할 수 있다.

 

이번엔 Points를 내가 원하는 이미지로 변경해보자

 

 

 

three.js/data at main · yoohwanihn/three.js

[three.js][typescript]. Contribute to yoohwanihn/three.js development by creating an account on GitHub.

github.com

 

위 깃허브에서 circle.png를 다운 받은 뒤 아래의 경로에 넣어준다.

 

 

 

 

 

 

circle.png의 이미지는 위와 같다.

 

원 주변을 자세히 보면 배경이 격자판 모양으로 투명하게 처리된 것을 볼 수 있다.

 

이 circle 이미지를 사용하기 위해서는 Texture 객체를 만들어야한다.

 

 

 

 

먼저 해당 경로의 이미지를 로딩하여 Texture 객체를 만들어준다.

 

 

Texture 객체를 PointMaterial의 Map속성에 지정해줘야 하며

 

추가적으로 alphaTest 값을 지정해주어야 한다.

 

(alphaTest는 이미지의 픽셀값 중 alpha 값이 0.5보다 클 경우에만 픽셀이 표시되도록 하는 옵션)

 

결과를 확인해보자.

 

 

 

 

 

우리가 의도한 circle.png로 Points가 변화하지도 않았으며, SphereGeometry도 깨진 모습을 확인할 수 있다.

 

이유는 이 SphereGeometry에 할당되어 있는 uvAttribute 값 때문이다.

 

uv는 Texture 이미지를 어떻게 사용할지에 대한 2차원 좌표인데, PointMaterial의 경우 uvAttribute를 제거해야한다.

 

 

 

uv Attribute를 제거하고 저장하여 실행해보자

 

 

 

 

 

기존의 사각형 Point에서 circle.png Point로 변경되었다.

 

 

전체 코드(main.ts)

import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/Addons.js'
import GUI from 'three/examples/jsm/libs/lil-gui.module.min.js'
import './style.css'

class App {
  private renderer: THREE.WebGLRenderer //Renderer Field 추가
  private domApp: Element 
  private scene: THREE.Scene
  private camera?: THREE.PerspectiveCamera //?를 붙이면 PerspectiveCamera Type이나 Undefined Type을 가질 수 있음.(Optional Properties)

  constructor(){
    console.log("YooHwanIhn");
    this.renderer = new THREE.WebGLRenderer({antialias:true}) // 안티-알리아스 : 높은 렌더링 결과를 얻기 위해 픽셀 사이에 계단 현상을 방지하는 효과 추가
    this.renderer.setPixelRatio(Math.min(2, window.devicePixelRatio)) // 고해상도에서 깨짐 방지를 위해 픽셀 비율 지정

    this.domApp = document.querySelector('#app')! 
    this.domApp.appendChild(this.renderer.domElement) // renderer.domeElement : canvas Type의 DOM 객체

    this.scene = new THREE.Scene()

    this.setupCamera()
    this.setupLight()
    this.setupModels()
    this.setupEvents()
  }

  private setupCamera(){
    const width = this.domApp.clientWidth
    const height = this.domApp.clientHeight
    
    this.camera = new THREE.PerspectiveCamera(75, width/height, 0.1, 100)
    this.camera.position.z = 2  // (0, 0, 2)

    new OrbitControls(this.camera, this.domApp as HTMLElement)
  }

  private setupLight(){
    //빛의 색상 값과 빛의 강도
    const color = 0xffffff  // white
    const intensity = 1
    const light = new THREE.DirectionalLight(color, intensity)  //광원
    light.position.set(-1, 2, 4)

    this.scene.add(light)
  }

  private setupModels(){
    const geometry = new THREE.SphereGeometry()
    geometry.deleteAttribute("uv")

    const circle = new THREE.TextureLoader().load("./circle.png")

    const material = new THREE.PointsMaterial({
      color: "Green", //0xff0000
      size: 5,
      sizeAttenuation: true,  // point의 크기가 camera의 거리에 따라 조절되도록 하는 option
      map: circle,
      alphaTest: 0.5  // 이미지의 픽셀값 중 alpha 값이 0.5보다 클 경우에만 픽셀이 표시되도록 하는 옵션
    })

    material.color = new THREE.Color(0xff0000); // Color 객체를 통해 Model을 생성 후 Point의 color를 변경 가능
    const points = new THREE.Points(geometry, material)
    this.scene.add(points)

    const gui = new GUI()
    gui.add(material, "size", 0.1, 10, 0.01)
  }

  //실제 이벤트와 렌더링 처리를 다룰 메서드
  private setupEvents(){
    window.onresize = this.resize.bind(this); // html의 size가 변경될 때마다 호출되는 함수(addEventListener 느낌??)
    this.resize();

    this.renderer.setAnimationLoop(this.render.bind(this)) // 연속적으로 render 메서드 호출(모니터 Frame에 맞게)
  }

  // html창 resize시 호출할 함수
  private resize(){
    const width = this.domApp.clientWidth
    const height = this.domApp.clientHeight

    //앞선 setUpCamera에서 설정한 camera 정보를 다시 수정해줌.
    const camera = this.camera
    if(camera){
      camera.aspect = width / height
      camera.updateProjectionMatrix() // 카메라의 값이 변경되었다면 수정하도록 함.
    }

    //renderer도 마찬가지로 사이즈 수정함
    this.renderer.setSize(width, height)
  }

  private update(time: number){
    time *= 0.001 // ms -> s 단위로 변경
  }

  private render(time: number){
    // time : setAnimationLoop의 값에 의해서 결정되는데 단위는 ms
    this.update(time)

    this.renderer.render(this.scene, this.camera!)
  }
}

new App()