✅ 기본 개발 환경
- Visual Studio Code
- Three.js 라이브러리 다운 후 폴더 open
* Three.js 라이브러리 구성*
three.js -> 기본 라이브러리
three.min.js -> 용량 최소화한 라이브러리
three.module.js -> 모듈 방식의 라이브러리 (우리가 사용할 것)
- 웹페이지 확인용 폴더 하나 만들고 html 파일 생성
✅ web 페이지 확인하기
<!DOCTYPE html>
<html>
<head>
<meta value="viewport" content="width=device-width, inital-scale=1">
<title>GIS Developer's Three.js Tutorials</title>
</head>
<body>
<h1>Hello. Three.JS !</h1>
</body>
</html>
- meta 태그의 viewport 값 : 웹과 모바일 모두에서 화면 띄울 수 있도록 함
- 위의 예시는 Hello. Three.JS ! 라는 텍스트를 띄운다.
✅Three.js 기본 구성
✅ js 코드 예
import * as THREE from '../build/three.module.js';
class App {
constructor() { //App 클래스 생성자
const divContainer = document.querySelector("#webgl-container"); //id 저거인 태그 선택해서 divContainer 상수에 저장
this._divContainer = divContainer; //본 클래스의 필드로 재정의 (이유: divContainer를 다른 메서드에서 참조하기 위함)
const renderer = new THREE.WebGLRenderer({ antialias: true}); //antialias(계단현상 노) 옵션으로 렌더러 객체 생성
renderer.setPixelRatio(window.devicePixelRatio); //픽셀 비율 설정
divContainer.appendChild(renderer.domElement); //domElement를 자식으로 추가
this._renderer = renderer; //필드화
const scene = new THREE.Scene(); //씬 객체 생성
this._scene = scene; //필드화
this._setupCamera(); //카메라객체
this._setupLight(); //광원 설정
this._setupModel(); //3차원모델 셋업
window.onresize = this.resize.bind(this); //창 크기 변경될 때 위한 resize 이벤트에 resize메서드 지정. bind 이유: this가 가리키는 것을 이벤트가 아닌 App 클래스로 하기 위함.
this.resize(); //렌더러나 카메라의 속성을 창 크기에 맞게 설정
requestAnimationFrame(this.render.bind(this)); //requestAnimationFrame이 렌더 메서드를 적당한 시점에 호출
}
}
window.onload = function() {
new App();
}
- 필드화 : this._ 사용해서 정의함. 개발자들의 약속이고, 해당 객체에서만 사용가능한 private method, 변수임.
- 지금은 renderer와 scene 설정만 끝난 상황이고 카메라, light, model 설정 메서드 작성 해야 함
✅ 전체 코드
import * as THREE from '../build/three.module.js';
class App {
constructor() { //App 클래스 생성자
const divContainer = document.querySelector("#webgl-container"); //id 저거인 태그 선택해서 divContainer 상수에 저장
this._divContainer = divContainer; //본 클래스의 필드로 재정의 (이유: divContainer를 다른 메서드에서 참조하기 위함)
const renderer = new THREE.WebGLRenderer({ antialias: true}); //antialias(계단현상 노) 옵션으로 렌더러 객체 생성
renderer.setPixelRatio(window.devicePixelRatio); //픽셀 비율 설정
divContainer.appendChild(renderer.domElement); //domElement를 자식으로 추가
this._renderer = renderer; //필드화
const scene = new THREE.Scene(); //씬 객체 생성
this._scene = scene; //필드화
this._setupCamera(); //카메라객체
this._setupLight(); //광원 설정
this._setupModel(); //3차원모델 셋업
window.onresize = this.resize.bind(this); //창 크기 변경될 때 위한 resize 이벤트에 resize메서드 지정. bind 이유: this가 가리키는 것을 이벤트가 아닌 App 클래스로 하기 위함.
this.resize(); //렌더러나 카메라의 속성을 창 크기에 맞게 설정
requestAnimationFrame(this.render.bind(this)); //requestAnimationFrame이 렌더 메서드를 적당한 시점에 호출
}
_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 material = new THREE.MeshPhongMaterial({color: 0x44a88}); //파란색 계열의 재질 객체 생성
const cube = new THREE.Mesh(geometry, material); //mesh 생성해서 큐브라는 상수에 담음
this._scene.add(cube); //큐브를 씬 객체의 구성요소로 추가
this._cube = cube; //큐브 객체의 필드 정의
}
resize() {//창 크기가 변경될 때의 메서드
const width = this._divContainer.clientWidth; //div 컨테이너(webgl-container) 의 속성값 가져와서
const height = this._divContainer.clientHeight;
this._camera.aspect = width / height; //카메라의 속성값 설정
this._camera.updateProjectionMatrix();
this._renderer.setSize(width, height); //렌더러의 크기 설정
}
render(time) { //렌더 메서드. time: 렌더링이 처음 시작된 이후 경과된 시간(ms)
this._renderer.render(this._scene, this._camera); //렌더러가 씬을 카메라의 시점으로 렌더링하라
this.update(time); //밑에서 메서드 정의
requestAnimationFrame(this.render.bind(this)); //계속 렌더 메서드가 반복 호출되도록
}
update(time) {
time *= 0.001; //ms -> s 단위로 변경
this._cube.rotation.x = time; //시간값을 큐브 모델의 x,y 축에 대한 회전 값에 time값을 지정 -> 시간은 계속 변하므로,
this._cube.rotation.y = time; //x, y축으로 큐브가 계속 회전하게 됨
}
}
window.onload = function() {
new App();
}
❗️ 시행착오
1. html 파일에 div 태그 넣는 것을 잊음
2. position 속성 사용할 때 자동 완성으로 getWorldPostion이 들어가서 오류 -> 이거 고치니까 정육면체 제대로 나왔다.
✅ 결과
실행이 안되면 개발자 도구(F12)에서 확인하자