GIS Developer ๋์ Three.js ๊ฐ์ข : https://www.youtube.com/watch?v=ITA9no8Bsio
์ง์ค๋ฉํธ๋ฆฌ (Geometry)
: 3์ฐจ์ ์ค๋ธ์ ํธ์ ํ์์ ์ ์
Three.js ์์ ์ ๊ณตํ๋ ๊ธฐ๋ณธ ์ง์ค๋ฉํธ๋ฆฌ
: ์ง์ค๋ฉํธ๋ฆฌ๋ ๊ธฐ๋ณธ์ ์ผ๋ก BufferGeometry ๋ฅผ ์์๋ฐ๊ณ ์๋ค.
* BufferGeometry
- BoxGeometry : ๊ฐ๋ก, ์ธ๋ก, ๊น์ด์ ๋ํ ํฌ๊ธฐ์ ๊ฐ๋ก, ์ธ๋ก, ๊น์ด ๊ฐ๊ฐ์ ๋ํ ๋ถํ (Segments) ์๋ก ์ ์๋๋ค. (๋ถํ ์ LineBasicMaterial์ ํตํด ์๊ฐ์ ์ผ๋ก ํ์ธ ๊ฐ๋ฅ)
- CircleGeometry : ์ํ์ ํฌ๊ธฐ์ธ ๋ฐ์ง๋ฆ, ์ํ์ ๊ตฌ์ฑํ๋ ๋ถํ ๊ฐ์, ์์ ๊ฐ๋, ์ฐ์ฅ ๊ฐ๋๋ฅผ ์ธ์๋ก ๋ฐ๋๋ค.
- ConeGeometry : ๋ฐ๋ฉด์ ํด๋น๋๋ ์์ ๋ฐ์ง๋ฆ ํฌ๊ธฐ, ์๋ฟ์ ๋์ด, ์๋ฟ ๋ฐ๋ฉด์ ๋ถํ ๊ฐ์, ์๋ฟ ๋์ด ๋ฐฉํฅ์ ๋ํ ๋ถํ ๊ฐ์, ์๋ฟ ๋ฐ๋ฉด์ ์ด์ด ๋์ ๊ฒ์ธ์ง์ ๋ํ ์ฌ๋ถ, ์๋ฟ์ ์์ ๊ฐ, ์ฐ์ฅ ๊ฐ์ ์ธ์๋ก ๋ฐ๋๋ค.
- CylinderGeometry : ์๋ฉด์ ๋ฐ์ง๋ฆ ํฌ๊ธฐ, ๋ฐ๋ฉด์ ๋ฐ์ง๋ฆ ํฌ๊ธฐ, ์๊ธฐ๋ฅ์ ๋์ด, ์๊ธฐ๋ฅ ๋๋ ๋ฐฉํฅ์ ๋ํ ๋ถํ ๊ฐ์, ์๊ธฐ๋ฅ์ ๋์ด ๋ฐฉํฅ์ ๋ํ ๋ถํ ๊ฐ์, ์๊ธฐ๋ฅ์ ์๋ฉด๊ณผ ๋ฐ๋ฉด์ ์ด์ด ๋์ ๊ฒ์ธ์ง์ ๋ํ ์ฌ๋ถ, ์๊ธฐ๋ฅ์ ์์ ๊ฐ, ์ฐ์ฅ ๊ฐ์ ์ธ์๋ก ๋ฐ๋๋ค.
- SphereGeometry : ๊ตฌ์ ๋ฐ์ง๋ฆ ํฌ๊ธฐ, ์ํ ๋ฐฉํฅ์ ๋ํ ๋ถํ ์, ์์ง ๋ฐฉํฅ์ ๋ํ ๋ถํ ์, ์ํ ๋ฐฉํฅ์ ๋ํ ๊ตฌ์ ์์ ๊ฐ, ์ฐ์ฅ ๊ฐ, ์์ง ๋ฐฉํฅ์ ๋ํ ๊ตฌ์ ์์ ๊ฐ, ์ฐ์ฅ ๊ฐ์ ์ธ์๋ก ๋ฐ๋๋ค.
- RingGeometry : 2์ฐจ์ ๋ฐ์ง ํํ์ ์ง์ค๋ฉํธ๋ฆฌ. ๋ด๋ถ ๋ฐ์ง๋ฆ ํฌ๊ธฐ, ์ธ๋ถ ๋ฐ์ง๋ฆ ํฌ๊ธฐ, ๊ฐ์ฅ ์๋ฆฌ ๋๋ ๋ฐฉํฅ์ ๋ํ ๋ถํ ์, ๋ด๋ถ ๋ฐฉํฅ์ ๋ํ ๋ถํ ์, ์์ ๊ฐ, ์ฐ์ฅ ๊ฐ์ ์ธ์๋ก ๋ฐ๋๋ค.
- PlaneGeometry : ํ๋ฉด ๋ชจ์์ ์ฌ๊ฐํ ์ง์ค๋ฉํธ๋ฆฌ. GIS์์ 3์ฐจ์ ์งํ๋ค์ ํํํ๋๋ฐ ์ ์ฉํ๊ฒ ์ฌ์ฉ ๋จ. ๋๋น์ ๋ํ ๊ธธ์ด, ๋์ด์ ๋ํ ๊ธธ์ด, ๋๋น ๋ฐฉํฅ์ ๋ํ ๋ถํ ์, ๋์ด ๋ฐฉํฅ์ ๋ํ ๋ถํ ์๋ฅผ ์ธ์๋ก ๋ฐ๋๋ค.
- TorusGeometry : 3์ฐจ์ ๋ฐ์ง ํํ์ ์ง์ค๋ฉํธ๋ฆฌ. ํ ๋ฌ์ค์ ๋ฐ์ง๋ฆ ํฌ๊ธฐ, ํ ๋ฌ์ค ์ํต์ ๋ฐ์ง๋ฆ ํฌ๊ธฐ, ํ ๋ฌ์ค์ ๋ฐฉ์ฌ ๋ฐฉํฅ์ ๋ํ ๋ถํ ์, ํ ๋ฌ์ค ์ํต์ ๋ํ ๋ถํ ์, ์ฐ์ฅ ๊ฐ์ ์ธ์๋ก ๋ฐ๋๋ค.
- TorusKnotGeometry : ํ ๋ฌ์ค ๋ซ์ ํ์ฉ๋๊ฐ ๋ฎ๋ค. ํ ๋ฌ์ค๋ซ์ ๋ฐ์ง๋ฆ ํฌ๊ธฐ, ํ ๋ฌ์ค๋ซ ์ํต์ ๋ฐ์ง๋ฆ ํฌ๋ฆฌ, ๋ถํ ์, ๋ถํ ์, ํ ๋ฌ์ค๋ซ์ ๋ฐ๋ณต ์, ํ ๋ฌ์ค ๋ซ์ ๋ฐ๋ณต ์๋ฅผ ์ธ์๋ก ๋ฐ๋๋ค.
- ShapeGeometry : shape ๋ํ์ ๋ฉ์ฌ์ ํํ๋ก ์์ฑํ๋ค. shape ํด๋์ค์ ๊ฐ์ฒด, ๋ถํ ์๋ฅผ ์ธ์๋ก ๋ฐ๋๋ค.
- TubeGeometry : curve ํด๋์ค์ ๊ฐ์ฒด, ํ๋ธ์ ์งํ ๋ฐฉํฅ์ ๋ํ ๋ถํ ์, ํ๋ธ์ ์ํต์ ๋ํ ๋ฐ์ง๋ฆ ํฌ๊ธฐ, ์ํต์ ๋ํ ๋ถํ ์, ํ๋ธ์ ์์๊ณผ ๋์ ๋ซ์์ง์ ๋ํ ์ฌ๋ถ
- LatheGeometry : ์ถ์ ๋์นญ์ผ๋ก ์ด์ด์ง๋ ์ง์ค๋ฉํธ๋ฆฌ. ํ์ ์ํฌ ๋์์ ๋ํ ์ขํ ๋ฐฐ์ด, ๋ถํ ์, ์์ ๊ฐ, ์ฐ์ฅ ๊ฐ์ ์ธ์๋ก ๋ฐ๋๋ค.
- ExtrudeGeometry : ํ๋ฉด shape ์ ๊น์ด ๊ฐ์ ๋ถ์ฌํ๊ณ mesh์ ์ ๋ฉด๊ณผ ๋ฐ ๋ฉด์ ๋น์ค๋ฌํ๊ฒ ์ฒ๋ฆฌ(๋ฒ ๋ฒจ๋ง)ํ๋ ์ง์ค๋ฉํธ๋ฆฌ. shape ํด๋์ค์ ๊ฐ์ฒด, ๊น์ด ๋ฐฉํฅ์ ๋ํ ๋ถํ ์, ๊น์ด ๊ฐ, ๋ฒ ๋ฒจ๋ง ์ฒ๋ฆฌ๋ฅผ ํ ๊ฒ์ธ์ง์ ๋ํ ์ฌ๋ถ, ๋ฒ ๋ฒจ๋ง์ ๋ํ ๋๊ป, shape์ ์ธ๊ณฝ์ ์ผ๋ก๋ถํฐ ์ผ๋ง๋ ๋ฉ๋ฆฌ ๋ฒ ๋ฒจ๋ง ํ ๊ฒ์ธ์ง์ ๋ํ ํฌ๊ธฐ, ๋ฒ ๋ฒจ๋ง ๋จ๊ณ ์๋ฅผ ์ธ์๋ก ๋ฐ๋๋ค.
- TextGeometry : ExtrudeGeometry ์ ํ์ ํด๋์ค. 3์ฐจ์ mesh๋ก ์์ฑํ ๋ฌธ์์ด, fontLoader๋ฅผ ํตํด ์ป์ด์จ ํฐํธ ๊ฐ์ฒด, text ๋ฉ์ฌ์ ํฌ๊ธฐ, ๊น์ด ๊ฐ, ํ๋์ ์ปค๋ธ๋ฅผ ๊ตฌ์ฑํ๋ ์ ์ ์ ๊ฐ์, ๋ฒ ๋ฒจ๋ง ์ฒ๋ฆฌ๋ฅผ ํ ๊ฒ์ธ์ง์ ๋ํ ์ฌ๋ถ, ๋ฒ ๋ฒจ๋ง์ ๋ํ ๋๊ป, shape์ ์ธ๊ณฝ์ ์ผ๋ก๋ถํฐ ์ผ๋ง๋ ๋ฉ๋ฆฌ ๋ฒ ๋ฒจ๋ง ํ ๊ฒ์ธ์ง์ ๋ํ ํฌ๊ธฐ, ๋ฒ ๋ฒจ๋ง ๋จ๊ณ ์๋ฅผ ์ธ์๋ก ๋ฐ๋๋ค.
- WireframeGeometry : ์์ด์ดํ๋ ์ ํํ๋ก ์ง์ค๋ฉํธ๋ฆฌ๋ฅผ ํํํ๋ค.
- ParametricGeometry : ์ํ์ ํจ์์์ 2๊ฐ์ ๊ฐ์ ์ ๋ ฅํ์ฌ ์ป์ ์ ์๋ ์ขํ๋ก ๊ตฌ์ฑ๋๋ ์ง์ค๋ฉํธ๋ฆฌ๋ค.
- EdgesGeometry : ์ง์ค๋ฉํธ๋ฆฌ๋ฅผ ๊ตฌ์ฑํ๋ ์ธ์ ๋ฉด์ ๊ฐ๋์ ๋ฐ๋ผ ์ง์ค๋ฉํธ๋ฆฌ๋ฅผ ์ฌ๊ตฌ์ฑํ๋ค.
- PolyhedronGeometry : ๋ค๋ฉด์ฒด๋ฅผ ๊ตฌ์ฑํ๋ ์ง์ค๋ฉํธ๋ฆฌ๋ค.
- IcosahedronGeometry : PolyhedronGeometry ๋ฅผ ์์๋ฐ๋ 20๋ฉด์ฒด ์ง์ค๋ฉํธ๋ฆฌ
- OctahedronGeometry : PolyhedronGeometry ๋ฅผ ์์๋ฐ๋ 8๋ฉด์ฒด ์ง์ค๋ฉํธ๋ฆฌ
- DodecahedronGeometry : PolyhedronGeometry ๋ฅผ ์์๋ฐ๋ 12๋ฉด์ฒด ์ง์ค๋ฉํธ๋ฆฌ
- TetrahedronGeometry : PolyhedronGeometry ๋ฅผ ์์๋ฐ๋ 4๋ฉด์ฒด ์ง์ค๋ฉํธ๋ฆฌ
์ง์ค๋ฉํธ๋ฆฌ ํ์์ ์ ์ํ๊ธฐ ์ํ ๋ฐ์ดํฐ
: ์ด ๋ฐ์ดํฐ๋ค์ 3์ฐจ์์ผ๋ก ์๊ฐํ๋ ๋ GPU์ ํ๋ฒ์ ์ ๋ฌ๋์ด ๋น ๋ฅด๊ฒ ์ฒ๋ฆฌ๋๋ค.
- ์ ์ (Vertex) : xyz ์ถ์ ๋ํ ์ขํ
- ์ ์ ์ธ๋ฑ์ค(Vertex Index) : 3์ฐจ์ ์ค๋ธ์ ํธ์ ๋ฉด์ ๊ตฌ์ฑ
- ์์ง ๋ฒกํฐ(Normal Vector) : ์ ์ ์ ๋ํ ์์ง ๋ฒกํฐ
- ์ ์ ์์(Vertex Color)
- ํ ์ค์ฒ ๋งตํ์ ์ํ UV ์ขํ
- ์ฌ์ฉ์ ์ ์ ๋ฐ์ดํฐ
// 02-geometry.js
import * as THREE from '../build/three.module.js';
import {OrbitControls} from '../examples/jsm/controls/OrbitControls.js';
import {FontLoader} from "../examples/jsm/loaders/FontLoader.js"
import {TextGeometry} from "../examples/jsm/geometries/TextGeometry.js"
class App {
constructor() {
const divContainer = document.querySelector('#webgl-container');
this._divContainer = divContainer;
const renderer = new THREE.WebGLRenderer({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(์นด๋ฉ๋ผ ๊ฐ์ฒด, ๋ง์ฐ์ค ์ด๋ฒคํธ๋ฅผ ๋ฐ๋ DOM ์์) */
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.x = -15;
camera.position.z = 15;
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, 1, 1, 1);
//const geometry = new THREE.CircleGeometry(1, 8, 0, Math.PI * 2);
//const geometry = new THREE.ConeGeometry(1, 1, 8, 1, false, 0, Math.PI * 2);
//const geometry = new THREE.CylinderGeometry(1, 1, 1, 8, 1, false, 0, Math.PI * 2);
//const geometry = new THREE.SphereGeometry(1, 32, 16, 0, Math.PI * 2, 0, Math.PI);
//const geometry = new THREE.RingGeometry(0.5, 1, 8, 1, 0, Math.PI * 2);
//const geometry = new THREE.PlaneGeometry(1, 1, 1, 1);
//const geometry = new THREE.TorusGeometry(1, 0.4, 8, 6, Math.PI * 2);
//const geometry = new THREE.TorusKnotGeometry(1, 0.4, 64, 8, 2, 3);
// ShapeGeometry
/* 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.ShapeGeometry(shape); */
// TubeGeometry
/* class CustomSinCurve extends THREE.Curve {
constructor(scale = 1) {
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);
}
}
// ํ๋ธ๊ฐ ์ด์ด์ง๋ ํํ๋ฅผ ๊ฒฐ์ ํ๊ธฐ ์ํ curve ๊ฐ์ฒด ์์ฑ
const path = new CustomSinCurve(4);
const geometry = new THREE.TubeGeometry(path, 64, 1, 8, false); */
// LatheGeometry
/* const points = [];
for (let i = 0; i < 10; ++i) {
points.push(new THREE.Vector2(Math.sin(i * 0.2) * 3 + 3, (i - 5) * 0.8));
}
const geometry = new THREE.LatheGeometry(points, 12, 0, Math.PI * 2); */
// ExtrudeGeometry
/* 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);
const settings = {
steps: 1,
depth: 4,
bevelEnabled: false,
bevelThickness: 0.1,
bevelSize: 0.1,
bevelSegments: 1,
}
const geometry = new THREE.ExtrudeGeometry(shape, settings); */
//TextGeometry
// TTF ๋ฑ๊ณผ ๊ฐ์ ํฐํธ ํ์ผ์ Three.js์์ ํฐํธ๋ก ์ฌ์ฉํ ์ ์๋ ํฌ๋งท์ผ๋ก ๋ณ๊ฒฝํด์ ์ฌ์ฉํจ (json)
// ํฐํธ๋ฅผ ๋ก๋
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);
});
const geometry = new TextGeometry('Three', {
font: font,
size: 5,
height: 1.5,
curveSegments: 4,
// setting for ExtrudeGeometry
bevelEnabled: true,
bevelThickness: 0.7,
bevelSize: 0.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;
}
loadFont(this);
/* // ํ์ ์์์ ์ฌ์ง
const fillMaterial = new THREE.MeshPhongMaterial({color: 0x515151});
// mesh ํ์
์ ์ค๋ธ์ ํธ ์์ฑ
const cube = new THREE.Mesh(geometry, fillMaterial);
// ๋
ธ๋์ ์ ์ ๋ํ ์ฌ์ง
const lineMaterial = new THREE.LineBasicMaterial({color: 0xffff00});
// line ํ์
์ ์ค๋ธ์ ํธ ์์ฑ
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; */
}
/* _setupModel() {
// shape method
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);
} */
/* _setupModel() {
// curve class
// ์ปค๋ธ๋ฅผ t ๋งค๊ฐ๋ณ์ ๋ฐฉ์ ์์ผ๋ก ์ ์
class CustomSinCurve extends THREE.Curve {
constructor(scale = 1) {
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);
}
}
const path = new CustomSinCurve(4);
const geometry = new THREE.BufferGeometry();
// getPoints : ์ปค๋ธ๋ฅผ ๊ตฌ์ฑํ๋ ์ขํ์ ๊ฐ์๋ฅผ ์ธ์๋ก ๋ฐ๋๋ค. ๊ฐ์ด ๋์ ์๋ก ๋ ๋ถ๋๋ฌ์ด ๊ณก์ ์ ์ป๋๋ค.
const points = path.getPoints(10);
geometry.setFromPoints(points);
const material = new THREE.LineBasicMaterial({color: 0xffff00});
const line = new THREE.Line(geometry, material);
this._scene.add(line);
} */
/* _setupModel() {
const points = [];
for (let i = 0; i < 10; ++i) {
points.push(new THREE.Vector2(Math.sin(i * 0.2) * 3 + 3, (i - 5) * 0.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);
} */
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();
}
์ฌ ๊ทธ๋ํ(Scene Graph)๋ฅผ ์ด์ฉํ ๊ณต๊ฐ๊ตฌ์ฑ
Objct3D ์ ํ์ ํด๋์ค
: 3์ฐจ์ ๊ฐ์ฒด๋ 3์ฐจ์ ๊ณต๊ฐ ์์ ๋์ด๊ฒ ๋๋ค.
- Mesh : 3๊ฐํ ๋ฉด์ผ๋ก ๊ตฌ์ฑ๋ ๊ฐ์ฒด๋ฅผ ํํ
- Line : ์ ํ ๊ฐ์ฒด๋ฅผ ํํ
- Points : ์ ์ ํํ
Objct3D ์ ์์ฑ
: 3์ฐจ์ ๊ฐ์ฒด๊ฐ 3์ฐจ์ ๊ณต๊ฐ ์์ ๋์ฌ์ง๊ธฐ ์ํด์๋ ์์น, ํ์ , ํฌ๊ธฐ ๊ฐ์ด ํ์ํ๋ค.
: ์ด ๊ฐ์ 4x4 ํฌ๊ธฐ์ ํ๋ ฌ ์ ๋ณด๋ก ๋ณํ๋๋ค.
- position : xyz ์ถ์ ๋ํ ์์น๊ฐ
- rotation : xyz ์ถ์ ๋ํ ํ์ ๊ฐ
- scale : xyz ์ถ์ ๋ํ ํฌ๊ธฐ์ ๋ฐฐ์๊ฐ
์ฌ ๊ทธ๋ํ(Scene Graph)
: 3์ฐจ์ ๊ณต๊ฐ ์์ ์ฅ๋ฉด ๊ตฌ์ฑ
: ์ฌ ๊ทธ๋ํ๋ฅผ ์ด์ฉํ๋ฉด 3์ฐจ์ ๊ฐ์ฒด๋ฅผ ์ํ๋ ๊ณต๊ฐ ์์ ๊ตฌ์ฑํ๊ธฐ ์ํด ๋ณต์กํ ์ํ์์ ์ฌ์ฉํ์ง ์์๋ ๋๋ค.
// 03-scenegraph.js
import * as THREE from '../build/three.module.js';
class App {
constructor() {
const divContainer = document.querySelector('#webgl-container');
this._divContainer = divContainer;
const renderer = new THREE.WebGLRenderer({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();
window.onresize = this.resize.bind(this);
this.resize();
requestAnimationFrame(this.render.bind(this));
}
_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 = 50;
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() {
/* Object3D ํ์
์ ํ์ ๊ฐ์ฒด ์์ฑ */
const solarSystem = new THREE.Object3D();
this._scene.add(solarSystem);
/* ๊ตฌ ๋ชจ์์ geometry ์์ฑ */
const radius = 1;
const widthSegment = 12;
const heightSegment = 12;
const sphereGeometry = new THREE.SphereGeometry(radius, widthSegment, heightSegment);
/* ํ์์ ์ฌ์ง */
const sunMaterial = new THREE.MeshPhongMaterial({emissive: 0xffff00, flatShading: true});
/* ํ์ mesh๋ฅผ ํ์ ๊ฐ์ฒด์ ์ถ๊ฐ */
const sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial);
sunMesh.scale.set(3, 3, 3);
solarSystem.add(sunMesh);
/* ์ง๊ตฌ */
const earthOrbit = new THREE.Object3D();
/* ์ง๊ตฌ ๊ฐ์ฒด๋ฅผ ํ์ ๊ฐ์ฒด์ ์์์ผ๋ก ์ถ๊ฐ */
solarSystem.add(earthOrbit);
const earthMaterial = new THREE.MeshPhongMaterial({color: 0x2223ff, emissive: 0x111244, flatShading: true});
const earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial);
/* ํ์ ๊ฐ์ฒด๋ก ๋ถํฐ 10๋งํผ ๋จ์ด์ง ๊ณณ์ ๋ฐฐ์น */
earthOrbit.position.x = 10;
earthOrbit.add(earthMesh);
/* ๋ฌ */
const moonOrbit = new THREE.Object3D();
/* ๋ฌ ๊ฐ์ฒด๋ ์ง๊ตฌ ๊ฐ์ฒด์ ์์์ด๋ฏ๋ก ์ง๊ตฌ๋ฅผ ๊ธฐ์ค์ผ๋ก 2๋งํผ ๋จ์ด์ง ๊ณณ์ ๋ฐฐ์น๋จ (ํ์ ๊ธฐ์ค 12) */
moonOrbit.position.x = 2;
/* ๋ฌ ๊ฐ์ฒด๋ฅผ ์ง๊ตฌ ๊ฐ์ฒด์ ์์์ผ๋ก ์ถ๊ฐ */
earthOrbit.add(moonOrbit);
const moonMaterial = new THREE.MeshPhongMaterial({color: 0x888888, emissive: 0x222222, flatShading: true});
const moonMesh = new THREE.Mesh(sphereGeometry, moonMaterial);
moonMesh.scale.set(0.5, 0.5, 0.5);
moonOrbit.add(moonMesh);
/* ๋ค๋ฅธ ๋ฉ์๋์์ ์ฐธ์กฐ ํ ์ ์๋๋ก field ํ */
this._solarSystem = solarSystem;
this._earthOrbit = earthOrbit;
this._moonOrbit = moonOrbit;
}
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; // second unit
/* ํ์ ๊ฐ์ฒด๋ฅผ y ์ถ์ ๊ธฐ์ค์ผ๋ก ํ์ : ์ง๊ตฌ์ ๋ฌ๋ ํ์์ ์ค์ฌ์ผ๋ก ํ์ */
this._solarSystem.rotation.y = time / 2;
this._earthOrbit.rotation.y = time * 2;
this._moonOrbit.rotation.y = time * 5;
}
}
window.onload = function() {
new App();
}