GIS Developer ๋์ Three.js ๊ฐ์ข : https://www.youtube.com/watch?v=RxIwudO-YqY
์ฌ์ง(Material)
Objct3D ์ Line์ ์์๋ฐ์ ํด๋์ค
- LineSegments
- LineLoop
์ฌ์ง(Material) ํด๋์ค
- PointsMaterial : Points ํ์ ์ Object3D ๊ฐ์ฒด๋ฅผ ์ํ ์ฌ์ง, ์ขํ๋ฅผ ์ ์ผ๋ก ๋ ๋๋ง
- LineBasicMaterial : Line ํ์
์ Object3D ๊ฐ์ฒด๋ฅผ ์ํ ์ฌ์ง. ์ขํ๋ฅผ ์์๋๋ก ์ฐ๊ฒฐํด์ ๋ผ์ธ์ผ๋ก ๋ ๋๋ง
- LineDashedMaterial : LineBasicMaterial ๋ฅผ ์์๋ฐ๋ ์์ ํด๋์ค
- MeshBasicMaterial : Mesh ํ์
์ Object3D ๊ฐ์ฒด๋ฅผ ์ํ ์ฌ์ง. mesh๋ฅผ ์ง์ ๋ ์์์ผ๋ก ๋ ๋๋ง
- MeshLambertMaterial : Mesh ํ์
์ Object3D ๊ฐ์ฒด๋ฅผ ์ํ ์ฌ์ง. mesh๋ฅผ ๊ตฌ์ฑํ๋ ์ ์ ์์ ๊ด์์ ์ํฅ์ ๊ณ์ฐํ๋ ์ฌ์ง
- MeshPhongMaterial : Mesh ํ์
์ Object3D ๊ฐ์ฒด๋ฅผ ์ํ ์ฌ์ง. mesh๊ฐ ๋ ๋๋ง๋๋ ํฝ์
๋จ์๋ก ๊ด์์ ์ํฅ์ ๊ณ์ฐํ๋ ์ฌ์ง
- MeshStandardMaterial : Mesh ํ์
์ Object3D ๊ฐ์ฒด๋ฅผ ์ํ ์ฌ์ง. PBR(๋ฌผ๋ฆฌ๊ธฐ๋ฐ ๋ ๋๋ง)์ ์ํ ์ฌ์ง. ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉ๋๋ฉฐ, ์๋๋ฉด์์๋ ์๋์ ์ผ๋ก ๋๋ฆฌ์ง๋ง ํจ์ฌ ๊ณ ํ์ง์ ๋ ๋๋ง ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์์.
- MeshPhysicalMaterial : MeshStandardMaterial ๋ฅผ ์์๋ฐ๋ ์์ ํด๋์ค. ๋ณด๋ค ๋ฐ์ ๋ ๋ฌผ๋ฆฌ๊ธฐ๋ฐ๋ ๋๋ง ์ฌ์ง. ์ฌ์ง์ ํ๋ฉด์ ์ฝํ
ํจ๊ณผ๋ฅผ ์ค ์ ์๊ณ ๋จ์ ํฌ๋ช
๋ ์ฒ๋ฆฌ๊ฐ ์๋ ์ค์ ์ ๋ฆฌ๊ฐ์ ํจ๊ณผ๋ฅผ ํํํ ์ ์์
- MeshDepthMaterial : Mesh ํ์
์ Object3D ๊ฐ์ฒด๋ฅผ ์ํ ์ฌ์ง
- MeshNormalMaterial : Mesh ํ์
์ Object3D ๊ฐ์ฒด๋ฅผ ์ํ ์ฌ์ง
- MeshToonMaterial : Mesh ํ์
์ Object3D ๊ฐ์ฒด๋ฅผ ์ํ ์ฌ์ง
์ฌ์ง(Material) ํด๋์ค์ ์์ฑ
: ๋ชจ๋ ์ฌ์ง ํด๋์ค๋ฅผ ์์๋ฐ๋ ํด๋์ค๋ ์ฌ์ง ํด๋์ค์ ์์ฑ์ ์ฌ์ฉํ ์ ์๋ค
- visible : Mesh๊ฐ ๋ณด์ผ์ง ์๋ณด์ผ์ง์ ๋ํ ์ฌ๋ถ
- transparent : opacity ๋ฅผ ์ฌ์ฉํ ์ง์ ๋ํ ์ฌ๋ถ
- opacity : ๋ถํฌ๋ช ๋๋ฅผ ์ง์ ํ๋ ๊ฐ
- depthTest : ๋ ๋๋ง ๋๊ณ ์๋ mesh์ ํฝ์ ์์น์ z ๊ฐ์ z ๋ฒํผ ๊ฐ์ ์ด์ฉํด ๊ฒ์ฌํ ์ง์ ๋ํ ์ฌ๋ถ
- depthWrite: ๋ ๋๋ง ๋๊ณ ์๋ mesh์ ํฝ์ ์ ๋ํ z ๊ฐ์ z ๋ฒํผ์ ๊ธฐ๋กํ ์ง์ ๋ํ ์ฌ๋ถ
- side : mesh๋ฅผ ๊ตฌ์ฑํ๋ ์ผ๊ฐํ ๋ฉด์ ๋ํด ์ ๋ฉด or ๋ท ๋ฉด or ๋ชจ๋ ๋ ๋๋งํ ๊ฒ์ธ์ง๋ฅผ ์ง์ (๊ด์์ ์ํฅ์ ๋ฐ๋ ์ฌ์ง์์ ์ฐจ์ด๋ฅผ ํ์ ๊ฐ๋ฅ)
* Depth Buffer (z ๋ฒํผ) ๋? 3์ฐจ์ ๊ฐ์ฒด๋ฅผ ์นด๋ฉ๋ผ๋ฅผ ํตํด ์ขํ๋ฅผ ๋ณํ์์ผ ํ๋ฉด์์ ๋ ๋๋ง ๋ ๋ ํด๋น 3์ฐจ์ ๊ฐ์ฒด๋ฅผ ๊ตฌ์ฑํ๋ ๊ฐ ํฝ์ ์ ๋ํ z๊ฐ ์ขํ๊ฐ์ 0 ~ 1๋ก ์ ๊ทํ์ํจ๋ค. ์ด ์ ๊ทํ๋ z ๊ฐ์ด ์ ์ฅ๋ ๋ฒํผ๊ฐ z ๋ฒํผ์ด๋ค. ์ด ๊ฐ์ด ์์์๋ก ์นด๋ฉ๋ผ์์ ๊ฐ๊น์ด 3์ฐจ์ ๊ฐ์ฒด์ ํฝ์ ์ด๋ค. ์ด z ๋ฒํผ๋ ์ฃผ๋ก ๋ ๋ฉ๋ฆฌ ์๋ 3์ฐจ์ ๊ฐ์ฒด๊ฐ ๊ฐ๊น์ด ๊ฐ์ฒด๋ฅผ ๋ฎ์ด์ ๋ ๋๋ง๋์ง ์๋๋ก ํ๊ธฐ ์ํด ์ฌ์ฉ๋๋ค.
import * as THREE from '../build/three.module.js';
import {OrbitControls} from '../examples/jsm/controls/OrbitControls.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(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.z = 3;
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);
}
/* PointsMaterial */
_setupModel1() {
/* 10000๊ฐ์ ์ขํ๋ฅผ vertices ๋ฐฐ์ด์ ์ถ๊ฐ */
const vertices = [];
for (let i = 0; i < 10000; i++) {
/* THREE.Math.randFloatSpread(5) : -5~5 ์ฌ์ด์ ๋์๊ฐ ์์ฑ */
const x = THREE.Math.randFloatSpread(5);
const y = THREE.Math.randFloatSpread(5);
const z = THREE.Math.randFloatSpread(5);
vertices.push(x, y, z);
}
const geometry = new THREE.BufferGeometry();
/* geometry.setAttribute()์ ํตํด geometry์ position ์์ฑ์ vertices ๋ฐฐ์ด์ Float32BufferAttribute ๋ก ๋ฉํํ์ฌ ์ ๋ฌ */
/* new THREE.Float32BufferAttribute(vertices, 3) : vertices ๋ฐฐ์ด์ ์ ์ฅ๋ ์ขํ๊ฐ xyz๋ก ํ๋์ ์ขํ๋ผ๋ ์๋ฏธ */
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
//console.log(geometry.attributes.position)
/* ์ ์ด๋ฏธ์ง */
const sprite = new THREE.TextureLoader().load('../examples/textures/sprites/disc.png');
/* PointsMaterial ์ฌ์ง ์์ฑ */
/* map : ๋ ๋๋ง, alphaTest : ์ด๋ฏธ์ง์ ํฝ์
๊ฐ ์ค ์ํ๊ฐ์ด ์ด alphaTest ๋ณด๋ค ํด๋๋ง ํฝ์
์ด ๋ ๋๋ง ๋๋๋ก ํจ, color : ํฌ์ธํธ์ ์์ ๊ฐ, size : ํฌ์ธํธ์ ํฌ๊ธฐ, sizeAttenuation : ํฌ์ธํธ๊ฐ ์นด๋ฉ๋ผ์ ๊ฑฐ๋ฆฌ์ ๋ฐ๋ผ ํฌ๊ธฐ๊ฐ ๊ฐ์๋๋๋ก ํ๋ ์ฌ๋ถ */
const material = new THREE.PointsMaterial({map: sprite, alphaTest: 0.5, color: 0x00ffff, size: 5, sizeAttenuation: false});
/* Points ํ์
์ Object3D ์์ฑ */
const points = new THREE.Points(geometry, material);
this._scene.add(points);
}
/* LineBasicMaterial */
_setupModel2() {
/* line ์ ๋ํ ์ขํ */
const vertices = [
-1, 1, 0,
1, 1, 0,
-1, -1, 0,
1, -1, 0,
];
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
//const material = new THREE.LineBasicMaterial({color: 0xff0000});
const material = new THREE.LineDashedMaterial({color: 0xff0000, dashSize: 0.2, gapSize: 0.1, scale: 1});
//const line = new THREE.Line(geometry, material);
//const line = new THREE.LineSegments(geometry, material);
const line = new THREE.LineLoop(geometry, material);
line.computeLineDistances(); //LineDashedMaterial
this._scene.add(line);
}
/* Mesh Material */
_setupModel() {
const material = new THREE.MeshPhysicalMaterial ({
/* material */
visible: true,
transparent: false,
opacity: 1,
depthTest: true,
depthWrite: true,
side: THREE.FrontSide,
/* MeshBasicMaterial */
/* color: 0xffffff, //mesh ์ ์์,
wireframe: false //mesh ๋ฅผ ์ ํํ๋ก ๋ ๋๋ง ํ ๊ฒ์ธ์ง์ ๋ํ ์ฌ๋ถ */
/* MeshLambertMaterial */
/* color: 0xffffff,
emissive: 0x00000, //๊ด์์ ์ํฅ์ ๋ฐ์ง ์๋ ์ฌ์ง ์์ฒด์์ ๋ฐฉ์ถํ๋ ์์ ๊ฐ
wireframe: false, */
/* MeshPhongMaterial */
/* color: 0xff0000,
emissive: 0x00000,
specular: 0xffff00, // ๊ด์์ ์ํด ๋ฐ์ฌ ๋๋ ์์
shininess: 10, // ๋ฐ์ฌ๋๋ ์ ๋
flatShading: false, // ํํธํ๊ฒ ๋ ๋๋ง ํ ์ง์ ๋ํ ์ฌ๋ถ
wireframe: false, */
/* MeshStandardMaterial */
/* color: 0xff0000,
emissive: 0x00000,
roughness: 0, // ๊ฑฐ์น ๊ธฐ
metalness: 0, // ๊ธ์์ฑ
wireframe: false,
flatShading: false, */
/* MeshPhysicalMaterial */
color: 0xff0000,
emissive: 0x00000,
roughness: 1,
metalness: 0,
clearcoat: 0.4, // ํ๋ฉด์ ์ฝํ
ํจ๊ณผ๊ฐ ์ ์ฉ๋๋ ์ ๋
clearcoatRoughness: 0, // ์ฝํ
์ ๋ํ ๊ฑฐ์น ๊ธฐ ๊ฐ
wireframe: false,
flatShading: false,
});
const box = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), material);
box.position.set(-1, 0, 0);
this._scene.add(box);
const sphere = new THREE.Mesh(new THREE.SphereGeometry(0.7, 32, 32), material);
sphere.position.set(1, 0, 0);
this._scene.add(sphere);
}
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
}
}
window.onload = function() {
new App();
}
ํ ์ค์ณ(Texture)๋ฅผ ์ํ ์ฌ์ง์ ์์ฑ ๋ชฉ๋ก
MeshBasicMaterial | MeshLambertMaterial | MeshPhongMaterial | MeshStandardMaterial | MeshPhysicalMaterial |
alphaMap | alphaMap | alphaMap | alphaMap | alphaMap |
aoMap | aoMap | aoMap | aoMap | aoMap |
bumpMap | bumpMap | bumpMap | ||
displacementMap | displacementMap | displacementMap | ||
emissiveMap | emissiveMap | emissiveMap | emissiveMap | |
envMap | envMap | envMap | envMap | envMap |
lightMap | lightMap | lightMap | lightMap | lightMap |
map | map | map | map | map |
metalnessMap | metalnessMap | |||
normalMap | normalMap | normalMap | ||
roughnessMap | roughnessMap | |||
clearcoatMap | ||||
clearcoatNormalMap | ||||
clearcoatRoughnessMap | ||||
transmissionMap | ||||
specularMap | specularMap | specularMap |
map ์์ฑ
: ํ ์ค์ณ ๊ฐ์ฒด๋ฅผ ์ง์ ํ๋ฉด geometry์ ํ๋ฉด์ ์ง์ ๋ ํ ์ค์ณ์ ์ด๋ฏธ์ง๊ฐ ์ท์ฒ๋ผ ์ ํ์ง๋ค.
: ๊ธฐ๋ณธ์ ์ผ๋ก ํ ์ค์ณ ๋งตํ์ geometry์ UV ์ขํ ๊ฐ๋ ์ผ๋ก ๋งตํ ๋์ด ์๋ค. ์ด UV ์ขํ๋ Three.js ์์ ์ ๊ณตํ๋ geometry์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ง์ ๋์ด ์๊ณ ์ด ๋ฐ์๋ UV ์ขํ๋๋ก ํ ์ค์ณ๊ฐ ๋งตํ๋๋ค.
์๋ ์ด๋ฏธ์ง๋ Three.js ์์ ์ฌ์ฉ๋๋ UV ์ขํ๋ฅผ ๋ณด์ฌ์ค๋ค. U๋ ์ด๋ฏธ์ง์ ์ํ ๋ฐฉํฅ์ ๋ํ ์ถ์ด๊ณ V๋ ์์ง ๋ฐฉํฅ์ ๋ํ ์ถ์ด๋ค.
- repeat : ํ ์ค์ณ์ ๋ฐ๋ณต
- wrapS, wrapT : ํ ์ค์ณ ์ด๋ฏธ์ง๊ฐ ๋ฐ๋ณต๊ณผ ๊ด๋ จ๋ ์์ฑ
- offset : ํ ์ค์ณ ์ด๋ฏธ์ง์ ์์ ์์น
- rotation : ํ ์ค์ณ ์ด๋ฏธ์ง์ ํ์
- center : ํ์ ์ ๊ธฐ์ค ์ขํ๋ center ์์ฑ์ผ๋ก ์กฐ์ ํ ์ ์์
- magFilter : ์๋ ์ด๋ฏธ์ง ํฌ๊ธฐ๋ณด๋ค ํ๋ ์ ๋ ๋๋ง ํ๋ ํํฐ
- minFilter : ์๋ ์ด๋ฏธ์ง ํฌ๊ธฐ๋ณด๋ค ์ถ์ ์ ๋ ๋๋ง ํ๋ ํํฐ. minMap์ ์ฌ์ฉํ๋ ์์ฑ์ด ๊ฐ์ฅ ๊ฒฐ๊ณผ๋ฌผ์ด ์ข์ง๋ง minMap์ ์์ฑ์ ์ํ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ด ์๋นํ๊ณ ๋ ๋๋ง ์ ํ๋์ ํฝ์ ๊ฐ์ ๊ฒฐ์ ํ๊ธฐ ์ํ ๊ณ์ฐ์ ํ์ํ ์ฐ์ฐ๋์ด ๊ฐ ์์ฑ์ ๋ฐ๋ผ ๋ชจ๋ ๋ค๋ฅด๋ฏ๋ก ์ฌ์ฉํ๋ ํ ์ค์ณ ๋งตํ์ ํฌ๊ธฐ ๋ฑ์ ๋ฐ๋ผ์ ์ ์ ํ minFilter ์์ฑ์ ์ฌ์ฉํด์ผ ํ๋ค.
* minMap : ์๋์ ์ด๋ฏธ์ง ํฌ๊ธฐ์์ ์ ๋ฐ์ผ๋ก ์ค์ด๋ ๊ฒ์ ๋ฐ๋ณตํด์ ๋ฏธ๋ฆฌ ๋ง๋ค์ด ๋์ ์ด๋ฏธ์ง ์
map ์ธ์ ๋ค์ํ texure ์ฌ์ฉํด๋ณด๊ธฐ
๋ค์ํ texure ์์ฑ์ ์ฌ์ฉํด ๋ณด๊ธฐ ์ํด glass ์ด๋ฏธ์ง๋ฅผ ๋ค์ด๋ฐ๋๋ค.
* ํ ์ค์ณ ์ด๋ฏธ์ง ์ ๊ณต ์ฌ์ดํธ : https://3dtextures.me/
3D TEXTURES
Free seamless PBR textures with diffuse, normal, height, AO and roughness maps.
3dtextures.me
- normalMap : ๋ฒ์ ๋ฒกํฐ๋ฅผ ์ด๋ฏธ์งํํด์ ์ ์ฅํด ๋ ๊ฒ. (๋ฒ์ ๋ฒกํฐ๋ mesh์ ํ๋ฉด์ ๋ํ ์์ง ๋ฒกํฐ๋ก ๊ด์์ ๋ํ ์ํฅ์ ๊ณ์ฐํ๋๋ฐ ์ฌ์ฉ๋๋ค.)
: ์ง์ ๋์ง ์์ ํน์ ์ง์ ์ ๋ํ ๋ ธ๋ง ๋ฒกํฐ๋ mesh์ ๊ตฌ์ฑ ์ขํ์ ๋ ธ๋ง ๋ฒกํฐ๋ค์ ์ด์ฉํด ๋ณด๊ฐ๋์ด ๊ณ์ฐ๋๋ค.
๊ทธ๋ฐ๋ฐ ๋ ธ๋ง๋ฒกํฐ๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋ฉด ๋ฐ์ค ํ๋ฉด์ ๋ ธ๋ง ๋ฒกํฐ๋ฅผ normalMap ์ด๋ฏธ์ง์ RGB ๊ฐ์ ์ด์ฉํด ๊ณ์ฐํ๊ฒ ๋๋๋ฐ, ์ด๋ ๊ฒ ๋๋ฉด ์ธ์์ ์ผ๋ก mesh ํ๋ฉด์ ๊ฐ ํฝ์ ์ ๋ํด ๋ฒ์ ๋ฒกํฐ๋ฅผ ์ง์ ํ ์ ์๊ฒ ๋๊ณ ๊ฐ ํฝ์ ๋จ์๋ก ๊ด์ ํ๊ณผ๊ฐ ๋ฌ๋ผ์ ธ ์ ์ฒด๊ฐ์ ํํํ ์ ์๊ฒ ๋๋ค.
ํ์ง๋ง ์ด๋ mesh์ ์ง์ค๋ฉํธ๋ฆฌ ํ์์ด ๋ฐ๋๋ ๊ฒ์ ์๋๊ธฐ ๋๋ฌธ์ ์ด ์ ์ฒด๊ฐ์ ์ฐฉ์ ํ์์ด๋ค. ๊ทธ๋ผ์๋ ๋งค์ฐ ์ ์ geometry ์ขํ ๊ตฌ์ฑ๋ง์ผ๋ก ์ ์ฒด๊ฐ์ ๋งค์ฐ ํจ๊ณผ์ ์ผ๋ก ํํํ ์ ์๋ ๋ฐฉ๋ฒ์ด๋ค.
- displacementMap : ์ค์ ๋ก mesh์ ์ง์ค๋ฉํธ๋ฆฌ์ ์ขํ๋ฅผ ๋ณํ์์ผ ์ ์ฒด๊ฐ์ ํํํ๋ค. map ์ด๋ฏธ์ง์ ํฝ์ ๊ฐ์ด ๋ฐ์ ์๋ก ์ขํ์ ๋ณ์๊ฐ ์ปค์ง๊ฒ ๋๋ค.
๊ตฌ ํํ์ geometry ๋ณ์๊ฐ ๋ํ๋์ง๋ง ๊ธฐ๋ณธ ๋ฐ์ค ํํ์ geometry๋ ๋ณ์ ํ์์ด ์ ์ฉ๋์ง ์๋๋ค. displacementMap ์ ์ค์ ์ง์ค๋ฉํธ๋ฆฌ์ ๊ตฌ์ฑ ์ขํ๋ฅผ ๋ณ๊ฒฝ์ํค๊ธฐ ๋๋ฌธ์ ์ด ๋ฐ์ค์ ํ๋ฉด์ ๋ํ ๊ตฌ์ฑ ์ขํ๊ฐ ์ ๊ณต ๋์ด์ผ ํ๋ค. ๋ฐ์ค ํํ์ geometry๋ ์ฌ๋ฌ๊ฐ์ ๋ฉด์ผ๋ก ๋ถํ ์์ผ์ผ ๊ทธ ํจ๊ณผ๊ฐ ๋ํ๋๋ค.
ํ์ง๋ง ํจ๊ณผ๋ฅผ ์ํด ์ขํ๋ฅผ ๋ ๋ง์ด ์ถ๊ฐํ๋ ๊ฒ์ ๋ ๋๋ง ์๋๋ฉด์์ ๋นํจ์จ์ ์ด๋ค. ์ด๋ฅผ ์ํด ์ ์ ํ normalMap ๊ณผ ํจ๊ป displacementMap ํจ๊ณผ๋ฅผ ์ํ ์ ์ ํ ๋ฉด ๋ถํ ์ด ํ์ํ๋ค.
- aoMap : ํ ์ค์ณ ์ด๋ฏธ์ง์ ๋ฏธ๋ฆฌ ์์ ํจ๊ณผ๋ฅผ ๊ทธ๋ ค ๋ฃ์ ๊ฒ. aoMap ์ด ์ ์ฉ๋๊ธฐ ์ํด์๋ ambient light ์ geometry์ uv2 ๋ฐ์ดํฐ ์ง์ ์ด ํ์ํ๋ค.
aoMap ์ ์ด์ฉํ๋ฉด ๋ฏธ๋ฆฌ ๋ง๋ค์ด์ง ์ธ๋ฐํ ๊ทธ๋ฆผ์์ ๊ฐ์ ๋๋์ ํจ๊ณผ๋ฅผ ์ง์ ํ ์ ์๋ค.
- roughnessMap : ๊ฑฐ์น ๊ธฐ์ ๋ํ ์ฌ์ง์ด ์ ์ฉ๋๋ค. ์ด ์์ฑ์ ๋ํ ๋งต ์ด๋ฏธ์ง์ ํฝ์ ๊ฐ์ด ๋ฐ์์๋ก ๊ฑฐ์น ๊ธฐ๊ฐ ๊ฐํ๋๋ค.
- metalnessMap : ๊ธ์ ์ฌ์ง์ ๋ํ ๋๋์ ๋ถ์ฌํ๋ค.
- alphaMap : ํฌ๋ช ๋์ ๋ํ ๋งต ์์ฑ. ์ด๋ฏธ์ง์ ํฝ์ ๊ฐ์ด ๋ฐ์์๋ก ๋ถํฌ๋ช ํ๊ฒ ๋๋ค. ํฌ๋ช ๋์ ๋ํ ํ์ฑํ๊ฐ ํ์ํ๋ค.
- lightMap : ์ง์ ๋ ์ด๋ฏธ์ง์ ์์์ผ๋ก ๋ฐ๊ดํ๋ ๋๋์ ํํํ ์ ์๋ค. eometry์ uv2 ๋ฐ์ดํฐ ์ง์ ์ด ํ์ํ๋ค.
- bumpMap : normalMap ์ด ์ฌ์ฉ๋๋ฉด ๋ฌด์๋๋ ์์ฑ์ด๋ค. ์ค๋ฌด์์๋ normalMap ์ ์ฃผ๋ก ์ฌ์ฉํ๋ค.
// 04-metarial.js
import * as THREE from '../build/three.module.js';
import {OrbitControls} from '../examples/jsm/controls/OrbitControls.js';
import {VertexNormalsHelper} from '../examples/jsm/helpers/VertexNormalsHelper.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(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.z = 3;
this._camera = camera;
// ์นด๋ฉ๋ผ๋ฅผ scene์ ์์์ผ๋ก ์ถ๊ฐ
this._scene.add(camera);
}
_setupLight() {
// aoMap ์ ์ํด ambientLight ์ถ๊ฐ
const ambientLight = new THREE.AmbientLight(0xffffff, 0.2);
this._scene.add(ambientLight);
const color = 0xffffff;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
//this._scene.add(light);
// ๊ด์์ด ์นด๋ฉ๋ผ์ ํจ๊ป ํ์ ํ๋๋ก ๊ด์์ ์นด๋ฉ๋ผ์ ์์์ผ๋ก ์ถ๊ฐ
this._camera.add(light);
}
/* PointsMaterial */
_setupModel1() {
/* 10000๊ฐ์ ์ขํ๋ฅผ vertices ๋ฐฐ์ด์ ์ถ๊ฐ */
const vertices = [];
for (let i = 0; i < 10000; i++) {
/* THREE.Math.randFloatSpread(5) : -5~5 ์ฌ์ด์ ๋์๊ฐ ์์ฑ */
const x = THREE.Math.randFloatSpread(5);
const y = THREE.Math.randFloatSpread(5);
const z = THREE.Math.randFloatSpread(5);
vertices.push(x, y, z);
}
const geometry = new THREE.BufferGeometry();
/* geometry.setAttribute()์ ํตํด geometry์ position ์์ฑ์ vertices ๋ฐฐ์ด์ Float32BufferAttribute ๋ก ๋ฉํํ์ฌ ์ ๋ฌ */
/* new THREE.Float32BufferAttribute(vertices, 3) : vertices ๋ฐฐ์ด์ ์ ์ฅ๋ ์ขํ๊ฐ xyz๋ก ํ๋์ ์ขํ๋ผ๋ ์๋ฏธ */
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
//console.log(geometry.attributes.position)
/* ์ ์ด๋ฏธ์ง */
const sprite = new THREE.TextureLoader().load('../examples/textures/sprites/disc.png');
/* PointsMaterial ์ฌ์ง ์์ฑ */
/* map : ๋ ๋๋ง, alphaTest : ์ด๋ฏธ์ง์ ํฝ์
๊ฐ ์ค ์ํ๊ฐ์ด ์ด alphaTest ๋ณด๋ค ํด๋๋ง ํฝ์
์ด ๋ ๋๋ง ๋๋๋ก ํจ, color : ํฌ์ธํธ์ ์์ ๊ฐ, size : ํฌ์ธํธ์ ํฌ๊ธฐ, sizeAttenuation : ํฌ์ธํธ๊ฐ ์นด๋ฉ๋ผ์ ๊ฑฐ๋ฆฌ์ ๋ฐ๋ผ ํฌ๊ธฐ๊ฐ ๊ฐ์๋๋๋ก ํ๋ ์ฌ๋ถ */
const material = new THREE.PointsMaterial({map: sprite, alphaTest: 0.5, color: 0x00ffff, size: 5, sizeAttenuation: false});
/* Points ํ์
์ Object3D ์์ฑ */
const points = new THREE.Points(geometry, material);
this._scene.add(points);
}
/* LineBasicMaterial */
_setupModel2() {
/* line ์ ๋ํ ์ขํ */
const vertices = [
-1, 1, 0,
1, 1, 0,
-1, -1, 0,
1, -1, 0,
];
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
//const material = new THREE.LineBasicMaterial({color: 0xff0000});
const material = new THREE.LineDashedMaterial({color: 0xff0000, dashSize: 0.2, gapSize: 0.1, scale: 1});
//const line = new THREE.Line(geometry, material);
//const line = new THREE.LineSegments(geometry, material);
const line = new THREE.LineLoop(geometry, material);
line.computeLineDistances(); //LineDashedMaterial
this._scene.add(line);
}
/* Mesh Material */
_setupModel() {
/* TextureLoader ๊ฐ์ฒด ์์ฑ */
const textureLoader = new THREE.TextureLoader();
/* ์ด๋ฏธ์ง ๊ฒฝ๋ก, ํ
์ค์ณ ์์ฑ ์ฑ๊ณต ์ ์ฝ๋ฐฑ */
const map21 = textureLoader.load(
'../examples/textures/uv_grid_opengl.jpg',
texture => {
texture.repeat.x = 1;
texture.repeat.y = 1;
// repeat ์ ํจ๊ป ์ฌ์ฉ
// ํ
์ค์ณ ์ด๋ฏธ์ง๊ฐ ๋ฐ๋ณต๋จ
/* texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping; */
// ์ฒ์์๋ง ์ด๋ฏธ์ง๊ฐ ํ๋ฒ ๋งตํ๋๊ณ ์ดํ ๋ฐ๋ณต๋ถํฐ๋ ์ด๋ฏธ์ง์ ๋๋จ ํฝ์
๋ก ์์ญ์ ์ฑ์
texture.wrapS = THREE.ClampToEdgeWrapping;
texture.wrapT = THREE.ClampToEdgeWrapping;
// ์ด๋ฏธ์ง๋ฅผ x ์ y ๋ฐฉํฅ์ผ๋ก ๋ฐ๋ณตํ๋ ์ง์๋ฒ์งธ ๋ฐ๋ณต์์๋ ์ด๋ฏธ์ง๊ฐ ๊ฑฐ์ธ์ ๋ฐ์ฌ๋ ๋ชจ์์ผ๋ก ๋งตํ๋จ
/* texture.wrapS = THREE.MirroredRepeatWrapping;
texture.wrapT = THREE.MirroredRepeatWrapping; */
// ํ
์ค์ฒ ์ด๋ฏธ์ง์ ์์ ์์น
texture.offset.x = 0;
texture.offset.y = 0;
// ํ
์ค์ฒ ์ด๋ฏธ์ง์ ํ์ ๊ฐ
texture.rotation = THREE.MathUtils.degToRad(0);
// ํ์ ์ ๊ธฐ์ค ์ขํ๋ center ์์ฑ์ผ๋ก ์กฐ์ ํ ์ ์์
texture.center.x = 0.5;
texture.center.y = 0.5;
// ํํฐ ์์ฑ
// ์๋ ์ด๋ฏธ์ง ํฌ๊ธฐ๋ณด๋ค ํ๋ ์ ๋ ๋๋ง ํ๋ ํํฐ
//texture.magFilter = THREE.LinearFilter; // ๊ฐ์ฅ ๊ฐ๊น์ด 4๊ฐ์ ํฝ์
์์์ ์ป์ด์ ์ ํ ๋ณด๊ฐํ ๊ฐ์ ์ฌ์ฉ. ํ๋ํ ์๋ก ๋ญ๊ฐ์ง
texture.magFilter = THREE.NearestFilter; // ๊ฐ์ฅ ๊ฐ๊น์ด ํ๋์ ํฝ์
์์์ ๊ฐ์ ธ์์ ๊ทธ๋๋ก ์ฌ์ฉ. ํ๋ํ ์๋ก ๊ณ๋จ ํ์์ด ๋ฐ์
// ์๋ ์ด๋ฏธ์ง ํฌ๊ธฐ๋ณด๋ค ์ถ์ ์ ๋ ๋๋ง ํ๋ ํํฐ
//texture.minFilter = THREE.NearestMipmapLinearFilter; // ๋ ๋๋งํ ๋งตํ ํฌ๊ธฐ์ ํฌ๊ธฐ๊ฐ ๊ฐ์ฅ ๊ฐ๊น์ด minMap ์ด๋ฏธ์ง 2๊ฐ๋ฅผ ์ ํํ๊ณ minMap ์ด๋ฏธ์ง๋ก๋ถํฐ ๊ฐ์ฅ ๊ฐ๊น์ด ํฝ์
1๊ฐ์ฉ์ ์ป์ ๋ค ์ป์ด์ง 2๊ฐ์ ํฝ์
์ ๊ฐ์ค์น ํ๊ท ๊ฐ์ ์ฌ์ฉ
//texture.minFilter = THREE.NearestFilter; // ๊ฐ์ฅ ๊ฐ๊น์ด ํ๋์ ํฝ์
์์์ ๊ฐ์ ธ์์ ๊ทธ๋๋ก ์ฌ์ฉ
//texture.minFilter = THREE.LinearFilter; // ๊ฐ์ฅ ๊ฐ๊น์ด 4๊ฐ์ ํฝ์
์์์ ์ป์ด์ ์ ํ ๋ณด๊ฐํ ๊ฐ์ ์ฌ์ฉ
//texture.minFilter = THREE.NearestMipmapNearestFilter; // ๋ ๋๋งํ ๋งตํ ํฌ๊ธฐ์ ๊ฐ์ฅ ๊ฐ๊น์ด minMap ์ด๋ฏธ์ง๋ฅผ 1๊ฐ ์ ํํ ๊ฐ์ฅ ๊ฐ๊น์ด 1๊ฐ์ ํฝ์
๊ฐ์ ๊ฐ์ ธ์์ ์ฌ์ฉ
texture.minFilter = THREE.LinearMipmapNearestFilter; // ๋ ๋๋ง ํ ๋งตํ ํฌ๊ธฐ์ ๊ฐ์ฅ ๊ฐ๊น์ด minMap ์ด๋ฏธ์ง 1๊ฐ๋ฅผ ์ ํํ๊ณ ๊ฐ์ฅ ๊ฐ๊น์ด 4๊ฐ์ ํฝ์
์ ๊ฐ์ ธ์์ ์ ํ ๋ณด๊ฐํ์ฌ ์ฌ์ฉ
texture.minFilter = THREE.LinearMipmapLinearFilter; // ๋ ๋๋งํ ๋งตํ ํฌ๊ธฐ์ ๊ฐ์ฅ ๊ฐ๊น์ด minMap ์ด๋ฏธ์ง๋ฅผ 2๊ฐ ์ ํํ๊ณ ๊ฐ๊ฐ ์ด๋ฏธ์ง์์ ๊ฐ์ฅ ๊ฐ๊น์ด 4๊ฐ์ ํฝ์
์ ๊ฐ์ ธ์ ์ ํ ๋ณด๊ฐํ์ฌ ์ป์ 2๊ฐ์ ์์ ๊ฐ์ ๊ฐ์ค์น ํ๊ท ํ์ฌ ์ฌ์ฉ
}
);
const material1 = new THREE.MeshPhysicalMaterial({
/* material */
visible: true,
transparent: false,
opacity: 1,
depthTest: true,
depthWrite: true,
side: THREE.FrontSide,
/* MeshBasicMaterial */
/* color: 0xffffff, //mesh ์ ์์,
wireframe: false //mesh ๋ฅผ ์ ํํ๋ก ๋ ๋๋ง ํ ๊ฒ์ธ์ง์ ๋ํ ์ฌ๋ถ */
/* MeshLambertMaterial */
/* color: 0xffffff,
emissive: 0x00000, //๊ด์์ ์ํฅ์ ๋ฐ์ง ์๋ ์ฌ์ง ์์ฒด์์ ๋ฐฉ์ถํ๋ ์์ ๊ฐ
wireframe: false, */
/* MeshPhongMaterial */
/* color: 0xff0000,
emissive: 0x00000,
specular: 0xffff00, // ๊ด์์ ์ํด ๋ฐ์ฌ ๋๋ ์์
shininess: 10, // ๋ฐ์ฌ๋๋ ์ ๋
flatShading: false, // ํํธํ๊ฒ ๋ ๋๋ง ํ ์ง์ ๋ํ ์ฌ๋ถ
wireframe: false, */
/* MeshStandardMaterial */
/* color: 0xff0000,
emissive: 0x00000,
roughness: 0, // ๊ฑฐ์น ๊ธฐ
metalness: 0, // ๊ธ์์ฑ
wireframe: false,
flatShading: false, */
/* MeshPhysicalMaterial */
/* color: 0xff0000,
emissive: 0x00000,
roughness: 1,
metalness: 0,
clearcoat: 0.4, // ํ๋ฉด์ ์ฝํ
ํจ๊ณผ๊ฐ ์ ์ฉ๋๋ ์ ๋
clearcoatRoughness: 0, // ์ฝํ
์ ๋ํ ๊ฑฐ์น ๊ธฐ ๊ฐ
wireframe: false,
flatShading: false, */
});
const map = textureLoader.load('images/glass/Glass_Window_002_basecolor.jpg');
const mapAO =textureLoader.load('images/glass/Glass_Window_002_ambientOcclusion.jpg');
const mapHeight =textureLoader.load('images/glass/Glass_Window_002_height.png');
const mapNormal =textureLoader.load('images/glass/Glass_Window_002_normal.jpg');
const mapRoughness =textureLoader.load('images/glass/Glass_Window_002_roughness.jpg');
const mapMetalic =textureLoader.load('images/glass/Glass_Window_002_metallic.jpg');
const mapAlpha =textureLoader.load('images/glass/Glass_Window_002_opacity.jpg');
const mapLight =textureLoader.load('images/glass/light.jpg');
const material = new THREE.MeshStandardMaterial({
map: map,
normalMap: mapNormal,
displacementMap: mapHeight,
displacementScale: 0.2, // ๋ณ์ ํจ๊ณผ๋ฅผ ์กฐ์
displacementBias: -0.15, // ๋ณ์ ๊ฒฐ๊ณผ๋ฅผ ์กฐ์
aoMap: mapAO,
aoMapIntensity: 1,
roughnessMap: mapRoughness,
roughness: 1,
metalnessMap: mapMetalic,
metalness: 0.5,
//alphaMap: mapAlpha,
transparent: true,
side: THREE.DoubleSide, // mesh์ ๋ท๋ฉด๋ ๋ ๋๋ง
lightMap: mapLight,
lightMapIntensity: 1,
});
// displacementMap ์ ์ ์ฉ์ ์ํด ๊ฐ ๋ฉด์ ๋ํด 256๊ฐ๋ก segmentํ ์์ผ์ค
const box = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1, 256, 256, 256), material);
box.position.set(-1, 0, 0);
// aoMap, lightMap ์ ์ ์ฉ์ ์ํด uv2 ์์ฑ ์ง์
box.geometry.attributes.uv2 = box.geometry.attributes.uv;
this._scene.add(box);
// box์ ๋ํด์ ๋ฒ์ ๋ฒกํฐ๋ฅผ ์๊ฐํ
/* const boxHelper = new VertexNormalsHelper(box, 0.1, 0xffff00);
this._scene.add(boxHelper); */
// displacementMap ์ ํจ๊ณผ ๊ทน๋ํ๋ฅผ ์ํด 512๊ฐ๋ก segmentํ ์์ผ์ค
const sphere = new THREE.Mesh(new THREE.SphereGeometry(0.7, 512, 512), material);
sphere.position.set(1, 0, 0);
// aoMap, lightMap ์ ์ ์ฉ์ ์ํด uv2 ์์ฑ ์ง์
sphere.geometry.attributes.uv2 = sphere.geometry.attributes.uv;
this._scene.add(sphere);
// sphere์ ๋ํด์ ๋ฒ์ ๋ฒกํฐ๋ฅผ ์๊ฐํ
/* const sphereHelper = new VertexNormalsHelper(sphere, 0.1, 0xffff00);
this._scene.add(sphereHelper); */
}
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
}
}
window.onload = function() {
new App();
}