three.js

Three.js 예제 코드로 기본 구조 파악하기

에밀오구 2023. 9. 21. 18:33

예제 코드를 통해 Three.js의 기본 구조를 파악해 봅시다. 크게 네 가지 순서로 작성합니다.

1. Scene(장면) 생성. 

scene(3D 공간)을 생성합니다.

const scene = new THREE.Scene()

-> 더 설정해 준다면 여러 scene을 생성할 수 있고, 배경색 등 여러 조건을 설정할 수 있습니다. 

scene.background = new THREE.Color(0x000000) 
//헥스 코드로 표현한다. 빨간 배경색 0xff0000 ,초록색 0x00ff00

2. Objects(정육면체) 생성.

사실 Object는  *particles(입자), lights(조명)과 같이 다양하게 포함된 개념이지만, 지금은  3D Object(Mesh)인 정육면체로 예시를 들겠습니다.  Mesh(3D Object)는  geometry(모양)와 material(색상, 질감등의 표면속성)를 합쳐 만듭니다.  Objects (정육면체)를 만드는 순서입니다.

  1. geometry(정육면체 모양)와 material(색상,질감) 각각 생성.
  2. Mesh의 인자로 geometry와 material를 넣어 3D Object를 완성.
  3. 완성된 Mesh(3D Object)를 scene안에 추가. 
const geometry = new THREE.BoxGeometry() //Three.js 에 내장된 객체 정육면체 모양 geometry을 가져옴
const material = new THREE.MeshBasicMaterial({
    color: 0x00ff00, //색상은 초록
    wireframe: true, // 뼈대만 보이게 하기 
})

const cube = new THREE.Mesh(geometry, material) //Mesh=geometry+material
scene.add(cube) //정육면체를 3D공간(scene)에 추가한다.

particles(입자)도 objects 속한다.
wireframe: true를 추가하면 요로코롬 뼈대만 보인다.

3. Camera 생성

카메라 종류는 5가지가 있는 데 지금은 perspectiveCamera 원근 카메라로 카메라를 생성합니다. 그리고 카메라 또한 3D 공간에 (0,0,0)에 위치에 있으면 3D Object(정육면체이며, 기본위치값 0,0,0에 위치에 있다.)를 촬영할 수 없기 때문에 카메라 위치를 position으로  조정합니다. (물론 position 말고도 rotation회전 등 다양하게 이동할 수 있습니다.)

const camera = new THREE.PerspectiveCamera( //원근 카메라 PerspectiveCamera 사용
    75,// 시야각Field of view (필수)
   1,  //종횡비(필수)
   0.1, //3번째 인자 Near and far
    10
)

camera.position.z = 2

0,0,0 에서 카메라 위치를 0,0,2로 이동 (사진 속 큰 정육면체에 꼭짓점의 좌표는 three.js 에서 표현하는 좌표시스템과 x축표현이 다르다. 유의하자 적당한 사진을 못찾아 발생한 해프닝 ;ㅅ;)

perspectiveCamera(원근카메라)는 몇 가지 파라미터를 넘겨줘야 합니다.  첫 번째(시야각)와 두 번째 인자(종횡비)는 필수인자입니다. 

perspectiveCamera(원근카메라)의 인자

1번째 인자: 시야각 the field of view.

  • 숫자가 클수록 넓은 공간을 볼 수 있지만, 왜곡이 생긴다. (어안 효과)
  • 숫자가 작으면 망원렌즈처럼 사물이 확대된 거처럼 보인다.
  • 적정시야는 45~ 75 사이 

시야각에 대한 이미지
어안효과

2번째 인자: 종횡비 Aspect ratio

  • 종횡비=== 이미지의 가로 세로 비례 관계
  • 정사각형 비율 === 1
  • 보통 width/ height 로 표현

3번째과 4번째 인자: 근평면과 원평면. Near and far

카메라가  얼마나 멀리까지 볼 수 있는지, 그리고 얼마나 가까이에 있는 물체까지 볼 수 있는지를 결정해 주는 인자들입니다. 근평면값과 원평면 값사이에 3D Object 위치값이 존재해야 카메라 시야에 잡혀 렌더링되므로 주의해야 합니다.  (물체가 근평면과 원평면 안에 들어가는 지 어케아냐? 다 방법이 있지만 메서드 까먹어서 못 알려줌) 해당 인자들은 z-fighting 버그를 방지를 위한 인자들이라 하네요.

  • 3번째 인자 근평면 Near : 카메라와 수직하며 제일 가까운 곳의 시야 범위를 나타내는 평면
  • 4번째 인자 원평면 Far : 카메라와 수직하며 제일 먼 곳의 시야 범위를 나타내는 평면
  • 4번째 인자 원평면 Far 의 기본값은 2000.

카메라 근평면과 원평면 개념

-> 더 설정해 준다면 원근 카메라뿐 아니라 원근깊이가 없이 평평하게 보이는 OrthographicCamera직교 카메라도 있습니다. 카메라가 바라보는 위치도 정할 수 있습니다. 

const camera = new THREE.PerspectiveCamera( //원근 카메라PerspectiveCamera: 원근깊이가 있는 카메라입니다. Frustum 형태가 잘린 피라미드처럼 보입니다.
    75,//1번째 인자= 시야각Field of view
   1,//window.innerWidth/ window.innerHeight, //캠퍼스 크기를 바꾸려면 횡종비 업데이트와 카메라도 일치시키기위해여기도 조정이 필요함
   //2번째 인자= 종횡비
   0.1, //3번째 인자 Near:  얼마나 가까이에 있는 물체까지 볼 수 있는지를 결정해주는 인자
    10 //4번째 인자  far: 카메라가 얼마나 멀리까지 볼 수 있는지,
)
const camera2 = new THREE.OrthographicCamera( //직교 카메라 :평평하게 보입니다.
   -2,2,2,-2
)

camera.position.z = 2
camera2.position.y = 1 // 카메라가 위에서 아래로 000위치에 있는 3d 정육각형을 바라봄
camera2.lookAt(new THREE.Vector3(0, 0, 0))

직교카메라와 원근 카메라의 표현차이

4. Renderer 생성후 Scene과 Camera 연결하기

3D 공간(scene)에 정육면체(mesh 인 3D Object)를 넣고 camera 를 세팅했으니 camera의 시선을 화면에 렌더링 해야 합니다. 이를 위해 Renderer를 생성하고  renderer 내장 render 메소드에 scene과 camera를 인자로 넘겨줍니다.  그전에 three.js는 camera의 시점에서 보이는 모습을 canvas 태그 위에서 그려내죠. canvas를 html에 따로 작성하지 않아도 자동으로 만들어주기도 하지만 지금(공부하는 애송이)은 renderer가 그림을 그릴  html에 canvas를 만들고 canvas를 연결해봅시다.

1.  html 파일에 canvas를 구성합니다. 

2. Renderer를 생성하며 canvas 엘리먼트를 인자로 받습니다. 

3. setSize 메서드를 통해 canvas의 크기를 조정합니다. 

4. renderer 내장 render 메서드에 scene과 camera를 인자로 넘겨주면 이제 진짜 완성입니다!

  <body>
        <canvas id="c1" class="c"></canvas> //canvas안에서 3D가 렌더링됩니다.
        <script type="module" src="bundle.js"></script>
    </body>
const canvas1 = document.getElementById("c1") as HTMLCanvasElement //html 폴더에 있는 아이디가 c1 인 캠퍼스를 변수에 저장합니다.

const renderer1 = new THREE.WebGLRenderer({canvas:canvas1}) //인자를 안받으면 자동으로 캠퍼스를 생성합니다.
renderer1.setSize(200, 200)

renderer1.render(scene, camera)

전체 코드와 부가 기능 설명

import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
const scene = new THREE.Scene() 
//scene.background = new THREE.Color(0x000000) // 빨간 배경색 0xff0000 ,초록색 0x00ff00
const camera = new THREE.PerspectiveCamera( //원근 카메라PerspectiveCamera: 원근깊이가 있는 카메라입니다. Frustum 형태가 잘린 피라미드처럼 보입니다.
    75,//1번째 인자= 시야각Field of view
   1,//window.innerWidth/ window.innerHeight, //캠퍼스 크기를 바꾸려면 횡종비 업데이트와 카메라도 일치시키기위해여기도 조정이 필요함
   //2번째 인자= 종횡비
   0.1,
   //3번째 인자 Near and far: 카메라가 얼마나 멀리까지 볼 수 있는지, 그리고 얼마나 가까이에 있는 물체까지 볼 수 있는지를 결정해주는 인자
    10
)
const camera2 = new THREE.OrthographicCamera( //직교 카메라 :평평하게 보입니다.
   -2,2,2,-2
)

const camera3 = new THREE.OrthographicCamera(-1, 1, 1, -1, 0.1, 10)
const camera4 = new THREE.OrthographicCamera(-1, 1, 1, -1, 0.1, 10)

camera.position.z = 2
camera2.position.y = 1 // 카메라가 위에서 아래로 000위치에 있는 3d 정육각형을 바라봄
camera2.lookAt(new THREE.Vector3(0, 0, 0))
camera3.position.z = 1
camera4.position.x = 1
camera4.lookAt(new THREE.Vector3(0, 0, 0))

const canvas1 = document.getElementById("c1") as HTMLCanvasElement
const canvas2 = document.getElementById("c2") as HTMLCanvasElement
const canvas3 = document.getElementById("c3") as HTMLCanvasElement //html 파일내 아이디 c3 인 캠퍼스와 연결
const canvas4 = document.getElementById("c4") as HTMLCanvasElement

const renderer1 = new THREE.WebGLRenderer({canvas:canvas1})
renderer1.setSize(200, 200) 
const renderer2 = new THREE.WebGLRenderer({canvas:canvas2})
renderer2.setSize(200, 200) 
const renderer3 = new THREE.WebGLRenderer({canvas:canvas3})
renderer3.setSize(200, 200) 
const renderer4 = new THREE.WebGLRenderer({canvas:canvas4})
renderer4.setSize(200, 200) 

//const renderer = new THREE.WebGLRenderer()
//renderer.setSize(window.innerWidth, window.innerHeight) //캠퍼스의 크기를 창의 높이와 크기로 지정


//document.body.appendChild(renderer1.domElement) // 자동 html 파일에 <canvas> 태그 생성

new OrbitControls(camera, renderer1.domElement) //OrbitControls= 마우스로 객체와 상호작용(줌인/아웃,오른쪽마우스클릭+드래그 등)


const geometry = new THREE.BoxGeometry()
const material = new THREE.MeshBasicMaterial({
    color: 0x00ff00,
    wireframe: true,
})

const cube = new THREE.Mesh(geometry, material)
scene.add(cube)

// window.addEventListener('resize', onWindowResize, false) //브라우저창에 맞게 화면 조절
// function onWindowResize() {   //29~ 35줄을 주석처리하면  새로고침이후 브라우저창을 조절해도 브라우저창에 맞게 캠퍼스가 조절하지 않고 고정된다.
//     camera.aspect = window.innerWidth / window.innerHeight  
//     camera.updateProjectionMatrix()
//     renderer.setSize(window.innerWidth, window.innerHeight)
//     render()
// }

function animate() {
    requestAnimationFrame(animate)
 
    cube.rotation.x += 0.01  // x 축으로 회전
     cube.rotation.y += 0.01 // x 축으로 회전

    render()
}

function render() {
    renderer1.render(scene, camera)
    renderer2.render(scene, camera2)
    renderer3.render(scene, camera3) //주석처리하면  화면이 안보입니다.
    renderer4.render(scene, camera4)
}

animate()

https://github.com/joywhy/WebGL-pra

 

GitHub - joywhy/WebGL-pra: three.js 연습 레포지토리입니다.

three.js 연습 레포지토리입니다. Contribute to joywhy/WebGL-pra development by creating an account on GitHub.

github.com

요약

1. scene(3d 공간)을 만든다. 

2. 3d Object를 만들어 scene에 추가한다.

3. 카메라를 설치한다.

4. renderer 를 생성하고  3d Object가 담긴 scene과 카메라를 담는다.

 

소감

꽤 재밌네요.  카메라에 대한 개념까지 추가로 정리해야 해서 시간이 많이 걸렸습니다. 좋아요와 댓글 부탁드립니다 그럼 이만 

 

출처

https://threejs.org/docs/#api/ko/cameras/PerspectiveCamera

https://velog.io/@9rganizedchaos/Three.js-journey-%EA%B0%95%EC%9D%98%EB%85%B8%ED%8A%B8-07