Ver3.5 ๐ WebGL & WebXR๋ก ๋ง๋๋์ฐจ์ธ๋ ์น๋์์ธ

๐ WebGL & WebXR๋ก ๋ง๋๋
์ฐจ์ธ๋ ์น๋์์ธ
๋ธ๋ผ์ฐ์ ๊ฐ 3D ์ธ๊ณ๋ฅผ ํ๋ ์๊ฐ โ ์น๋์์ธ์ ๊ฒฝ๊ณ๊ฐ ์ฌ๋ผ์ง๋ค
์ผ, ์์งํ ๋งํด๋ณผ๊ฒ. ์์ฆ ์น์ฌ์ดํธ ๋ณด๋ค ๋ณด๋ฉด "์ด? ์ด๊ฒ ์น์ด์ผ, ๊ฒ์์ด์ผ?" ์ถ์ ์๊ฐ์ด ์์ง ์์? ๐ค
๋ง์ฐ์ค ์์ง์ด๋ฉด 3D ์ค๋ธ์ ํธ๊ฐ ๋ฐ๋ผ ๋๊ณ , ์คํฌ๋กคํ๋ฉด ์ฐ์ฃผ ๊ณต๊ฐ์ ๋ ์๋ค๋๋ ๊ฒ ๊ฐ๊ณ , ์ฌ์ง์ด VR ํค๋์
๋ผ๋ฉด ๊ทธ ์นํ์ด์ง ์์ผ๋ก ๋ค์ด๊ฐ ์ ์๋ ์๋๊ฐ ๋๊ฑฐ๋ .
์ด๊ฒ ๋ค WebGL์ด๋ WebXR ๋๋ถ์ด์ผ. ์ด ๋ ๊ธฐ์ ์ด ๋ญ์ง, ์ด๋ป๊ฒ ์ฐ๋์ง, ๊ทธ๋ฆฌ๊ณ ์ค์ ์น๋์์ธ์์ ์ด๋ป๊ฒ ํ์ฉ๋๋์ง โ ์ค๋ ์์ ์น์ ํ๊ฒ ํํค์ณ๋ณผ ๊ฑฐ์ผ. ๊ฐ๋ฐ์๊ฐ ์๋์ด๋ ์ดํดํ ์ ์๊ฒ, ๋์์ด๋ ์ ์ฅ์์๋ "์ ์ด๋ ๊ฒ ์ฐ๋ฉด ๋๊ฒ ๊ตฌ๋!" ์ถ๊ฒ ์ค๋ช ํด์ค๊ฒ. ๐
๐ฎ WebGL์ด ๋ญ๋ฐ? โ GPU๋ฅผ ๋ธ๋ผ์ฐ์ ์ ๊ฝ๋ค
WebGL(Web Graphics Library)์ ํ๋ง๋๋ก ์น ๋ธ๋ผ์ฐ์ ์์ GPU๋ฅผ ์ง์ ์ธ ์ ์๊ฒ ํด์ฃผ๋ ๊ธฐ์ ์ด์ผ. ์๋ 3D ๊ทธ๋ํฝ์ ๊ฒ์ ์์ง์ด๋ ์ ๋ฌธ ์ํํธ์จ์ด์์๋ ๊ฐ๋ฅํ ๊ฑฐ์์์. ๊ทธ๋ฐ๋ฐ WebGL์ด ๋ฑ์ฅํ๋ฉด์ "ํ๋ฌ๊ทธ์ธ ์์ด, ๋ธ๋ผ์ฐ์ ๋ง์ผ๋ก 3D ๋ ๋๋ง"์ด ๊ฐ๋ฅํด์ง ๊ฑฐ์ง.
๐ WebGL์ ํ์ ๋ฐฐ๊ฒฝ
WebGL์ OpenGL ES 2.0์ ๊ธฐ๋ฐ์ผ๋ก ๋ง๋ค์ด์ก์ด. OpenGL์ด ๋ญ๋๊ณ ? 1992๋
๋ถํฐ ์ฐ์ธ ์คํ์์ค ๊ทธ๋ํฝ API์ผ. ์ด๊ฑธ ์น ํ๊ฒฝ์ ๋ง๊ฒ JavaScript๋ก ์ ์ดํ ์ ์๋๋ก ๋ง๋ ๊ฒ WebGL์ด์ผ.
2011๋
์ WebGL 1.0์ด ๊ณต์ ์คํ์ผ๋ก ๋์๊ณ , 2017๋
์ WebGL 2.0์ด ๋ฑ์ฅํ์ด. WebGL 2.0์ OpenGL ES 3.0 ๊ธฐ๋ฐ์ผ๋ก ํจ์ฌ ๋ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ๋ค์ ์ง์ํด.
๐ WebGL์ ํต์ฌ ๊ฐ๋ โ ์ ฐ์ด๋๊ฐ ๋ญ์ผ?
WebGL์ ์ดํดํ๋ ค๋ฉด ์ ฐ์ด๋(Shader)๋ฅผ ์์์ผ ํด. ์ ฐ์ด๋๋ GPU์์ ์คํ๋๋ ์์ ํ๋ก๊ทธ๋จ์ธ๋ฐ, ๋ ์ข ๋ฅ๊ฐ ์์ด:
์ ฐ์ด๋๋ GLSL(OpenGL Shading Language)์ด๋ผ๋ C์ธ์ด ๋น์ทํ ์ธ์ด๋ก ์์ฑํด. ์ฒ์์ ์ข ๋ฏ์ค์ง๋ง, ์ด๊ฑธ ๋ง์คํฐํ๋ฉด ์ง์ง ๋ฏธ์น ๋น์ฃผ์ผ ํจ๊ณผ๋ฅผ ๋ง๋ค ์ ์์ด.
// ๊ฐ๋จํ ๋ฒํ
์ค ์
ฐ์ด๋ ์์ (GLSL)
attribute vec4 a_position;
uniform mat4 u_matrix;
void main() {
gl_Position = u_matrix * a_position;
}
// ๊ฐ๋จํ ํ๋๊ทธ๋จผํธ ์
ฐ์ด๋ ์์ (GLSL)
precision mediump float;
uniform vec4 u_color;
void main() {
gl_FragColor = u_color;
}
๐ WebGL 1.0 vs WebGL 2.0 ๋น๊ต
| ํญ๋ชฉ | WebGL 1.0 | WebGL 2.0 |
|---|---|---|
| ๊ธฐ๋ฐ ์คํ | OpenGL ES 2.0 | OpenGL ES 3.0 |
| ์ถ์ ์ฐ๋ | 2011๋ | 2017๋ |
| ์ธ์คํด์ฑ | ํ์ฅ ํ์ | ๊ธฐ๋ณธ ์ง์ |
| Transform Feedback | ๋ฏธ์ง์ | ์ง์ |
| ๋ค์ค ๋ ๋ ํ๊ฒ | ํ์ฅ ํ์ | ๊ธฐ๋ณธ ์ง์ |
| ์ ์ ํ ์ค์ฒ | ๋ฏธ์ง์ | ์ง์ |
| ๋ธ๋ผ์ฐ์ ์ง์ | ๊ฑฐ์ ๋ชจ๋ ๋ธ๋ผ์ฐ์ | ์ต์ ๋ธ๋ผ์ฐ์ ๋๋ถ๋ถ |
๐ฅฝ WebXR์ด ๋ญ๋ฐ? โ ํ์ค๊ณผ ๊ฐ์์ ๊ฒฝ๊ณ๋ฅผ ํ๋ฌผ๋ค
WebXR์ Web Extended Reality์ ์ฝ์์ผ. XR์ด ๋ญ๋๊ณ ? AR(์ฆ๊ฐํ์ค) + VR(๊ฐ์ํ์ค) + MR(ํผํฉํ์ค)์ ํตํ์ด์ XR์ด๋ผ๊ณ ๋ถ๋ฌ. ๊ทธ๋ฆฌ๊ณ WebXR์ ์ด ๋ชจ๋ ๊ฑธ ์น ๋ธ๋ผ์ฐ์ ์์ ๊ตฌํํ ์ ์๊ฒ ํด์ฃผ๋ API์ผ.
์์ ์๋ WebVR์ด๋ผ๋ API๊ฐ ์์๋๋ฐ, ์ด๊ฒ VR๋ง ์ง์ํ์ด. ๊ทธ๋ฐ๋ฐ AR๊น์ง ํฌํจํ๋ฉด์ ๋ ๋์ ๊ฐ๋ ์ธ WebXR๋ก ํตํฉ๋๊ณ , 2019๋ ์ W3C์์ ๊ณต์ ํ์คํ ์์ ์ด ์์๋์ด. ํ์ฌ๋ WebXR Device API๊ฐ ์ฃผ์ ๋ธ๋ผ์ฐ์ ์์ ์ง์๋๊ณ ์์ด.
๐ WebXR์ ํต์ฌ ๊ธฐ๋ฅ๋ค
navigator.xr.requestSession('immersive-vr') ๋๋ 'immersive-ar'๋ก XR ์ธ์
์ ์์ํด. ์ฌ์ฉ์๊ฐ VR ํค๋์
์ ์ฐ๊ฑฐ๋ AR ๋ชจ๋๋ฅผ ์ผ๋ฉด ์ด ์ธ์
์ด ํ์ฑํ๋ผ.
ํค๋์ ์ ์์น์ ๋ฐฉํฅ์ ์ค์๊ฐ์ผ๋ก ์ถ์ ํด. ์ฌ์ฉ์๊ฐ ๊ณ ๊ฐ๋ฅผ ๋๋ฆฌ๋ฉด ๊ฐ์ ์ธ๊ณ๋ ๊ทธ์ ๋ง๊ฒ ์ ๋ฐ์ดํธ๋ผ. 6DoF(6 Degrees of Freedom) ์ถ์ ์ ์ง์ํด.
VR ์ปจํธ๋กค๋ฌ, ์ ์ถ์ , ๊ฒ์ด์ฆ(์์ ) ์ ๋ ฅ ๋ฑ ๋ค์ํ ์ ๋ ฅ ๋ฐฉ์์ ์ง์ํด.
XRInputSource API๋ก ์ปจํธ๋กค๋ฌ ๋ฒํผ ์ํ, ์กฐ์ด์คํฑ ๊ฐ ๋ฑ์ ์ฝ์ ์ ์์ด.
AR์์ ํนํ ์ค์ํ ๊ธฐ๋ฅ์ด์ผ. ์นด๋ฉ๋ผ๋ก ๋ณด์ด๋ ์ค์ ๊ณต๊ฐ์์ ํ๋ฉด(๋ฐ๋ฅ, ํ ์ด๋ธ ๋ฑ)์ ๊ฐ์งํ๊ณ , ๊ทธ ์์ 3D ์ค๋ธ์ ํธ๋ฅผ ๋ฐฐ์นํ ์ ์์ด.
AR ์ค๋ธ์ ํธ๋ฅผ ์ค์ ๊ณต๊ฐ์ ํน์ ์์น์ ๊ณ ์ ์ํค๋ ๊ธฐ๋ฅ์ด์ผ. ์ฌ์ฉ์๊ฐ ๋ฐฉ์ ๋์๋ค๋ ๋ ๊ฐ์ ์ค๋ธ์ ํธ๊ฐ ์ ์๋ฆฌ์ ์์ด.
๐ ๏ธ Three.js โ WebGL์ ์ธ๊ฐ๋ต๊ฒ ์ฐ๋ ๋ฒ
์์งํ ๋งํ ๊ฒ. ์์ WebGL ์ฝ๋๋ ์ง์ง ๋ณต์กํด. ์ผ๊ฐํ ํ๋ ๊ทธ๋ฆฌ๋ ๋ฐ๋ 100์ค ๋๋ ์ฝ๋๊ฐ ํ์ํด. ๊ทธ๋์ ๋ฑ์ฅํ ๊ฒ Three.js์ผ.
Three.js๋ WebGL์ ์ถ์ํํด์ ํจ์ฌ ์ฝ๊ฒ 3D ์ฌ์ ๋ง๋ค ์ ์๊ฒ ํด์ฃผ๋ JavaScript ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ผ. 2010๋ ์ Ricardo Cabello(mrdoob)๊ฐ ๋ง๋ค์๊ณ , ์ง๊ธ์ GitHub์์ 10๋ง ์คํ๊ฐ ๋๋ ์ด์ธ๊ธฐ ์คํ์์ค ํ๋ก์ ํธ์ผ. ๐
๐ฏ Three.js์ ํต์ฌ ๊ตฌ์ฑ ์์
| ๊ตฌ์ฑ ์์ | ์ญํ | ๋น์ |
|---|---|---|
| Scene | ๋ชจ๋ 3D ์ค๋ธ์ ํธ๊ฐ ์กด์ฌํ๋ ๊ณต๊ฐ | ์ํ ์ธํธ์ฅ |
| Camera | ์ฌ์ ๋ฐ๋ผ๋ณด๋ ์์ | ์ํ ์นด๋ฉ๋ผ |
| Renderer | ์ฌ์ ์ค์ ๋ก ํ๋ฉด์ ๊ทธ๋ฆฌ๋ ์์ง | ์ํ ์ดฌ์ ์ฅ๋น |
| Geometry | 3D ์ค๋ธ์ ํธ์ ํํ(๊ผญ์ง์ ๋ฐ์ดํฐ) | ์กฐ๊ฐ์์ ๋ผ๋ |
| Material | ์ค๋ธ์ ํธ์ ํ๋ฉด ์์ฑ(์์, ์ง๊ฐ) | ํ์ธํธ, ์ฌ์ง |
| Mesh | Geometry + Material์ ์กฐํฉ | ์์ฑ๋ ์กฐ๊ฐ์ |
| Light | ์ฌ์ ์กฐ๋ช | ์คํ๋์ค ์กฐ๋ช |
// Three.js๋ก ํ์ ํ๋ ํ๋ธ ๋ง๋ค๊ธฐ (๊ธฐ๋ณธ ์์)
import * as THREE from 'three';
// 1. ์ฌ ์์ฑ
const scene = new THREE.Scene();
// 2. ์นด๋ฉ๋ผ ์ค์ (์๊ทผ ์นด๋ฉ๋ผ)
const camera = new THREE.PerspectiveCamera(
75, // ์์ผ๊ฐ(FOV)
window.innerWidth / window.innerHeight, // ์ข
ํก๋น
0.1, // ๊ทผ๊ฑฐ๋ฆฌ ํด๋ฆฌํ
1000 // ์๊ฑฐ๋ฆฌ ํด๋ฆฌํ
);
camera.position.z = 5;
// 3. ๋ ๋๋ฌ ์์ฑ
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 4. ํ๋ธ ์์ฑ
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({ color: 0x7ab8d4 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// 5. ์กฐ๋ช
์ถ๊ฐ
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(5, 5, 5);
scene.add(light);
// 6. ์ ๋๋ฉ์ด์
๋ฃจํ
function animate() {
requestAnimationFrame(animate);
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
์ด๋? ์์ WebGL๋ณด๋ค ํจ์ฌ ์ฝ๊ธฐ ์ฝ์ง? ์ด๊ฒ Three.js์ ๋งค๋ ฅ์ด์ผ. ๋ณต์กํ GPU ํ์ดํ๋ผ์ธ์ ์ ๊ฒฝ ์ฐ์ง ์๊ณ "์ฌ์ ํ๋ธ ์ถ๊ฐํ๊ณ , ์กฐ๋ช ์ผ๊ณ , ๋ ๋๋งํด!" ์ด๋ฐ ์์ผ๋ก ์ง๊ด์ ์ผ๋ก ์์ ํ ์ ์์ด.
๐ Three.js์ ์ฃผ์ ๊ธฐ๋ฅ๋ค
๐จ ์น๋์์ธ์์ WebGL ํ์ฉ โ ์ค์ ์ฌ๋ก๋ค
์ด๋ก ์ ์ถฉ๋ถํ ํ์ผ๋๊น, ์ด์ ์ง์ง ์ค์ํ ๊ฑฐ โ ์ค์ ๋ก ์ด๋ป๊ฒ ์ฐ์ด๋์ง ์์๋ณด์. ์น๋์์ธ, ํนํ ์์ธํ์ด์ง๋ ํฌํธํด๋ฆฌ์ค์์ WebGL์ด ์ด๋ป๊ฒ ํ์ฉ๋๋์ง ๊ตฌ์ฒด์ ์ธ ์ฌ๋ก๋ค์ ์ดํด๋ณผ๊ฒ.
๐๏ธ ์ฌ๋ก 1: 3D ์ ํ ๋ทฐ์ด (์์ธํ์ด์ง ํ๋ช )
์ด์ปค๋จธ์ค ์์ธํ์ด์ง์์ WebGL์ ํ์ฉ์ด ๊ฐ์ฅ ํญ๋ฐ์ ์ผ๋ก ๋๊ณ ์์ด. ๊ธฐ์กด์๋ ์ฌ๋ฌ ๊ฐ๋์์ ์ฐ์ ์ฌ์ง ์ฌ๋ฌ ์ฅ์ ๋ณด์ฌ์คฌ๋ค๋ฉด, ์ด์ ๋ ์ค์ 3D ๋ชจ๋ธ์ 360๋ ํ์ ์ํค๋ฉด์ ๋ณผ ์ ์์ด.
Nike, IKEA, Apple ๊ฐ์ ๋ธ๋๋๋ค์ด ์ด๋ฏธ ์ด ๋ฐฉ์์ ์ฐ๊ณ ์์ด. ํนํ IKEA์ AR ๊ธฐ๋ฅ์ WebXR์ ํ์ฉํด์ ๊ฐ๊ตฌ๋ฅผ ์ค์ ๋ฐฉ์ ๋ฐฐ์นํด๋ณด๋ ๊ฒฝํ์ ์ ๊ณตํ๊ณ ์์ง. ๊ตฌ๋งค ์ ํ์จ์ด ๊ธฐ์กด ๋๋น ์ต๋ 40% ํฅ์๋๋ค๋ ์ฐ๊ตฌ ๊ฒฐ๊ณผ๋ ์์ด.
2. GLTF/GLB ํฌ๋งท์ผ๋ก ์ต์ ํ ๋ด๋ณด๋ด๊ธฐ
3. Three.js + GLTFLoader๋ก ์น์ ๋ก๋
4. OrbitControls๋ก ๋ง์ฐ์ค/ํฐ์น ํ์ ๊ตฌํ
5. ํ๊ฒฝ ๋งต์ผ๋ก ์ฌ์ค์ ์ธ ๋ฐ์ฌ ํจ๊ณผ ์ถ๊ฐ
โจ ์ฌ๋ก 2: ํํฐํด ์์คํ ํ์ด๋ก ์น์
์น์ฌ์ดํธ ์ฒซ ํ๋ฉด(ํ์ด๋ก ์น์ )์ ํํฐํด ํจ๊ณผ๋ฅผ ๋ฃ์ผ๋ฉด ๋ฐฉ๋ฌธ์์ ์์ ์ ํ ์ก์ ์ ์์ด. ์์ฒ ๊ฐ์ ์ ๋ค์ด ๋ง์ฐ์ค๋ฅผ ๋ฐ๋ผ ์์ง์ด๊ฑฐ๋, ํน์ ํํ๋ฅผ ๋ง๋ค์๋ค๊ฐ ํฉ์ด์ง๋ ํจ๊ณผ โ ์ด๊ฒ ๋ค WebGL ํํฐํด ์์คํ ์ด์ผ.
Three.js์์๋ Points ์ค๋ธ์ ํธ์ PointsMaterial์ ์ฌ์ฉํด. ์์ญ๋ง ๊ฐ์ ํํฐํด๋ GPU์์ ์ฒ๋ฆฌํ๊ธฐ ๋๋ฌธ์ ๋ถ๋๋ฝ๊ฒ ๋์๊ฐ.
// ํํฐํด ์์คํ
๊ธฐ๋ณธ ๊ตฌํ
const particleCount = 50000;
const positions = new Float32Array(particleCount * 3);
for (let i = 0; i < particleCount; i++) {
positions[i * 3] = (Math.random() - 0.5) * 20; // x
positions[i * 3 + 1] = (Math.random() - 0.5) * 20; // y
positions[i * 3 + 2] = (Math.random() - 0.5) * 20; // z
}
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
const material = new THREE.PointsMaterial({
size: 0.02,
color: 0x7ab8d4,
transparent: true,
opacity: 0.8
});
const particles = new THREE.Points(geometry, material);
scene.add(particles);
๐ ์ฌ๋ก 3: GLSL ์ ฐ์ด๋ ๋ฐฐ๊ฒฝ ํจ๊ณผ
์ ฐ์ด๋ ์ํธ๋ WebGL์ ์ง์ง ๋งค๋ ฅ์ด์ผ. ์ํ ํจ์๋ง์ผ๋ก ์ ์ฒด์ฒ๋ผ ํ๋ฅด๋ ๋ฐฐ๊ฒฝ, ๋ ธ์ด์ฆ ํ ์ค์ฒ, ํ๋ก๊ทธ๋จ ํจ๊ณผ ๋ฑ์ ๋ง๋ค ์ ์์ด. ํ์ผ ํฌ๊ธฐ๋ ๊ฑฐ์ ์๋๋ฐ ๋น์ฃผ์ผ ์ํฉํธ๋ ์์ฒญ๋.
ํนํ Perlin Noise๋ Simplex Noise๋ฅผ ์ ฐ์ด๋์์ ํ์ฉํ๋ฉด ์์ฐ์ค๋ฌ์ด ์ ๊ธฐ์ ์์ง์์ ๋ง๋ค ์ ์์ด. ๋ง์ ํ ํฌ ์คํํธ์ ๋ค์ด ๋๋ฉ ํ์ด์ง ๋ฐฐ๊ฒฝ์ผ๋ก ์ด๋ฐ ์ ฐ์ด๋ ํจ๊ณผ๋ฅผ ์ฐ๊ณ ์์ด.
๐ ์ฌ๋ก 4: ์คํฌ๋กค ๊ธฐ๋ฐ 3D ์ ๋๋ฉ์ด์
์คํฌ๋กคํ๋ฉด 3D ์ฌ์ด ๋ณํ๋ ํจ๊ณผ โ Apple์ ์ ํ ํ์ด์ง์์ ๋ง์ด ๋ดค์ง? ์คํฌ๋กค ์์น์ ๋ฐ๋ผ ์นด๋ฉ๋ผ๊ฐ ์ด๋ํ๊ฑฐ๋, ์ค๋ธ์ ํธ๊ฐ ์กฐ๋ฆฝ๋๊ฑฐ๋, ์ฌ์ด ์ ํ๋๋ ๊ฑฐ์ผ.
์ด๊ฑธ ๊ตฌํํ๋ ค๋ฉด GSAP(GreenSock Animation Platform)์ ScrollTrigger ํ๋ฌ๊ทธ์ธ๊ณผ Three.js๋ฅผ ํจ๊ป ์ฌ์ฉํ๋ ๊ฒ ์ผ๋ฐ์ ์ด์ผ. ์คํฌ๋กค ์งํ๋(0~1)๋ฅผ Three.js ์ฌ์ ํ๋ผ๋ฏธํฐ์ ๋งคํํ๋ฉด ๋ผ.
๐ ์ฃผ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ํ๊ณ ์์ ์ ๋ฆฌ
WebGL/WebXR ๊ฐ๋ฐ์ ํ ๋ Three.js๋ง ์๋ ๊ฒ ์๋์ผ. ๋ค์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํ๋ ์์ํฌ๊ฐ ์์ด์ ๋ชฉ์ ์ ๋ง๊ฒ ์ ํํ ์ ์์ด.
๐ง ์ฃผ์ 3D ์น ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋น๊ต
| ๋ผ์ด๋ธ๋ฌ๋ฆฌ | ํน์ง | ์ ํฉํ ์ฉ๋ |
|---|---|---|
| Three.js | ๊ฐ์ฅ ๋ฒ์ฉ์ , ํฐ ์ปค๋ฎค๋ํฐ, ํ๋ถํ ์์ | ์ผ๋ฐ 3D ์น, ํฌํธํด๋ฆฌ์ค, ์ ํ ๋ทฐ์ด |
| Babylon.js | ๊ฒ์ ์์ง ์์ค, TypeScript ์ง์, ๊ฐ๋ ฅํ ๋ฌผ๋ฆฌ ์์ง | ์น ๊ฒ์, ๋ณต์กํ ์ธํฐ๋์ |
| A-Frame | HTML ํ๊ทธ ๊ธฐ๋ฐ, WebXR ํนํ, ์ฌ์ด ์ง์ ์ฅ๋ฒฝ | VR/AR ๊ฒฝํ, ๋น ๋ฅธ ํ๋กํ ํ์ดํ |
| React Three Fiber | React + Three.js ํตํฉ, ์ ์ธ์ 3D | React ๊ธฐ๋ฐ 3D ์น์ฑ |
| Spline | ๋ ธ์ฝ๋ 3D ๋์์ธ ํด, ์น ์๋ฒ ๋ ๊ฐ๋ฅ | ๋์์ด๋ ์นํ์ 3D ์์ |
| GSAP | ์ ๋๋ฉ์ด์ ํนํ, ScrollTrigger ๊ฐ๋ ฅ | ์คํฌ๋กค ๊ธฐ๋ฐ 3D ์ ๋๋ฉ์ด์ |
โ๏ธ React Three Fiber โ React ๊ฐ๋ฐ์์ ์ ํ
React๋ฅผ ์ฐ๋ ๊ฐ๋ฐ์๋ผ๋ฉด React Three Fiber(R3F)๋ฅผ ์ฃผ๋ชฉํด์ผ ํด. Three.js๋ฅผ React ์ปดํฌ๋ํธ ๋ฐฉ์์ผ๋ก ์ฌ์ฉํ ์ ์๊ฒ ํด์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ผ. ์ ์ธ์ ๋ฌธ๋ฒ์ผ๋ก 3D ์ฌ์ ๊ตฌ์ฑํ ์ ์์ด์ ์ฝ๋๊ฐ ํจ์ฌ ๊น๋ํด์ ธ.
// React Three Fiber ์์
import { Canvas } from '@react-three/fiber'
import { OrbitControls, Environment } from '@react-three/drei'
function ProductModel() {
return (
<mesh rotation={[0, Math.PI / 4, 0]}>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color="#7ab8d4" metalness={0.5} roughness={0.2} />
</mesh>
)
}
function App() {
return (
<Canvas camera={{ position: [0, 0, 5] }}>
<ambientLight intensity={0.5} />
<directionalLight position={[5, 5, 5]} />
<ProductModel />
<OrbitControls />
<Environment preset="studio" />
</Canvas>
)
}
๐ญ A-Frame โ WebXR์ HTML์ฒ๋ผ
A-Frame์ Mozilla๊ฐ ๋ง๋ WebXR ํ๋ ์์ํฌ์ผ. HTML ํ๊ทธ์ฒ๋ผ 3D ์ฌ์ ๊ตฌ์ฑํ ์ ์์ด์ ์ง์ ์ฅ๋ฒฝ์ด ์์ฒญ ๋ฎ์. VR ๊ฒฝํ์ ๋น ๋ฅด๊ฒ ํ๋กํ ํ์ดํํ ๋ ์ต๊ณ ์ผ.
<!-- A-Frame์ผ๋ก VR ์ฌ ๋ง๋ค๊ธฐ -->
<a-scene>
<a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9"></a-box>
<a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere>
<a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder>
<a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4"></a-plane>
<a-sky color="#ECECEC"></a-sky>
</a-scene>
โก ์ฑ๋ฅ ์ต์ ํ โ ์์ ๊ฒ๋ ๋น ๋ฅด๊ฒ
WebGL๋ก ๋ฉ์ง ๊ฑธ ๋ง๋ค์๋๋ฐ ๋๋ฆฌ๋ฉด ์๋ฌด ์์ฉ ์์์. ํนํ ๋ชจ๋ฐ์ผ์์ 3D๋ ๋ฐฐํฐ๋ฆฌ๋ ๋ง์ด ๋จน๊ณ ๋ฐ์ด๋ ์ฌํด. ๊ทธ๋์ ์ฑ๋ฅ ์ต์ ํ๋ ํ์์ผ.
๐ฏ ํต์ฌ ์ต์ ํ ๊ธฐ๋ฒ๋ค
Retina ๋์คํ๋ ์ด๋ ํฝ์ ๋น์จ์ด 2~3์ด์ผ. ๊ทธ๋๋ก ์ฐ๋ฉด ๋ ๋๋ง ๋ถํ๊ฐ 4~9๋ฐฐ ์ฆ๊ฐํด.
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))๋ก ์ต๋ 2๋ก ์ ํํ๋ ๊ฒ ์ข์.
๊ฐ์ ๋ชจ์์ ์ค๋ธ์ ํธ๊ฐ ๋ง์ ๋ ์ฌ์ฉํด. ๋๋ฌด ์ฒ, ํํฐํด, ๊ฑด๋ฌผ ๊ตฐ์ง ๋ฑ์์
InstancedMesh๋ฅผ ์ฐ๋ฉด ๋๋ก์ฐ ์ฝ์ 1๋ฒ์ผ๋ก ์ค์ผ ์ ์์ด. ์ฑ๋ฅ์ด ์์ญ ๋ฐฐ ํฅ์๋ผ.
3D ๋ชจ๋ธ์ ์น์ ์ฌ๋ฆฌ๊ธฐ ์ ์ gltf-transform ๋๊ตฌ๋ก ์ต์ ํํด์ผ ํด. ๋ฉ์ ์์ถ(Draco), ํ ์ค์ฒ ์์ถ(KTX2), ๋ถํ์ํ ๋ ธ๋ ์ ๊ฑฐ ๋ฑ์ ์๋์ผ๋ก ์ฒ๋ฆฌํด์ค.
์นด๋ฉ๋ผ ์์ผ ๋ฐ์ ์๋ ์ค๋ธ์ ํธ๋ ๋ ๋๋งํ์ง ์๋ ๊ธฐ๋ฒ์ด์ผ. Three.js๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ด๊ฑธ ์ง์ํ์ง๋ง, ์ปค์คํ ์ฌ์์๋ ๋ช ์์ ์ผ๋ก ์ค์ ํด์ค์ผ ํ ๋๋ ์์ด.
navigator.getBattery()๋ก ๋ฐฐํฐ๋ฆฌ ์ํ๋ฅผ ํ์ธํ๊ณ , ๋ฐฐํฐ๋ฆฌ๊ฐ ๋ฎ์ ๋๋ 3D ํจ๊ณผ๋ฅผ ์ค์ด๊ฑฐ๋ ๋๋ ๋ฐฐ๋ ค๊ฐ ํ์ํด. ํนํ ๋ชจ๋ฐ์ผ ์ฌ์ฉ์๋ฅผ ์ํ UX ๋ฐฐ๋ ค์ผ.
๋ชจ๋ฐ์ผ์์ ํญ์ ์ ํํ๊ฑฐ๋ ๋ฉ๋ชจ๋ฆฌ๊ฐ ๋ถ์กฑํ๋ฉด WebGL ์ปจํ ์คํธ๊ฐ ์์ค๋ ์ ์์ด.
canvas.addEventListener('webglcontextlost', handler)์ webglcontextrestored ์ด๋ฒคํธ๋ฅผ ๋ฐ๋์ ์ฒ๋ฆฌํด์ผ ํด. ์ ํ๋ฉด ์ฌ์ฉ์๊ฐ ํญ ์ ํ ํ ๋์์์ ๋ ๊ฒ์ ํ๋ฉด์ ๋ณด๊ฒ ๋ผ.
๐จ ์์ธํ์ด์ง์ 3D ๋ น์ด๊ธฐ โ ์ค์ ๋์์ธ ๊ฐ์ด๋
์, ์ด์ ์ง์ง ์ค์ ์ด์ผ. ์น๋์์ธ, ํนํ ์์ธํ์ด์ง์ 3D๋ฅผ ์ด๋ป๊ฒ ์์ฐ์ค๋ฝ๊ฒ ๋ น์ผ ์ ์๋์ง ๊ตฌ์ฒด์ ์ธ ๊ฐ์ด๋๋ฅผ ์ค๊ฒ.
๐ 3D ์์ ๋ฐฐ์น ์์น
โข ํ์ด์ง ์ ์ฒด๋ฅผ 3D๋ก ๋๋ฐฐ
โข ๋ก๋ฉ ์์ด ๋ฌด๊ฑฐ์ด 3D ๋ชจ๋ธ ๋ฐ๋ก ํ์
โข ๋ชจ๋ฐ์ผ์์๋ ๋ฐ์คํฌํ๊ณผ ๋์ผํ 3D
โข 3D๊ฐ ์ฝํ ์ธ ๋ฅผ ๊ฐ๋ฆฌ๋ ๋ ์ด์์
โข ์ฌ์ฉ์๊ฐ ์ ์ดํ ์ ์๋ ์๋ ํ์
โข ํต์ฌ ํฌ์ธํธ์๋ง 3D ์์ ๋ฐฐ์น
โข ๋ก๋ฉ ์ค์ผ๋ ํค + ์ ์ง์ ๋ก๋ฉ
โข ๋ชจ๋ฐ์ผ์ 2D ํด๋ฐฑ ๋๋ ๊ฒฝ๋ ๋ฒ์
โข 3D๋ ๋ณด์กฐ ์ญํ , ํ ์คํธ๊ฐ ์ฃผ์ธ๊ณต
โข ์ฌ์ฉ์๊ฐ ์ง์ ์กฐ์ํ๋ ์ธํฐ๋์
๐ ์ด์ปค๋จธ์ค ์์ธํ์ด์ง 3D ๊ตฌํ ํ๋ก์ฐ
์ ํ 3D ๋ชจ๋ธ์ ์ค๋นํด. ์ง์ Blender๋ก ๋ง๋ค๊ฑฐ๋, ์ ์กฐ์ฌ์์ CAD ํ์ผ์ ๋ฐ์์ ๋ณํํด. ์น์ฉ์ผ๋ก๋ ํด๋ฆฌ๊ณค ์๋ฅผ ์ต๋ํ ์ค์ด๊ณ (๋ณดํต 10,000~50,000 ํด๋ฆฌ๊ณค), GLTF ํฌ๋งท์ผ๋ก ๋ด๋ณด๋ด์ผ ํด.
gltf-transform์ด๋ glTF Pipeline์ผ๋ก Draco ์์ถ ์ ์ฉ. ํ ์ค์ฒ๋ WebP ๋๋ KTX2๋ก ๋ณํ. ๋ชฉํ ํ์ผ ํฌ๊ธฐ๋ ๋ชจ๋ฐ์ผ ๊ธฐ์ค 2MB ์ดํ๊ฐ ์ด์์ ์ด์ผ.
์ ํ ๋ทฐ์ด์ ๋ง๋ ์นด๋ฉ๋ผ, ์กฐ๋ช , ํ๊ฒฝ ๋งต ์ค์ . ์คํ๋์ค ์กฐ๋ช ๋๋์ ์ํด HDR ํ๊ฒฝ ๋งต + ์ํํธ ๋ฐ์ค ์กฐ๋ช ์กฐํฉ์ ๋ง์ด ์จ. OrbitControls๋ก ํ์ /์ค ์ธํฐ๋์ ์ถ๊ฐ.
์ ํ ์์ ์ ํ UI์ ์ฐ๋ํด์ Three.js ๋จธํฐ๋ฆฌ์ผ ์์์ ์ค์๊ฐ์ผ๋ก ๋ณ๊ฒฝ.
material.color.set(hexColor)๋ก ๊ฐ๋จํ๊ฒ ๊ตฌํ ๊ฐ๋ฅ. ์ด๊ฒ ์ฌ์ฉ์ ๊ฒฝํ์ ์์ฒญ ๋์ฌ์ค.
WebXR AR ์ธ์ ๋๋ iOS์ AR Quick Look์ ์ฐ๊ฒฐํด์ "๋ด ๋ฐฉ์์ ๋ณด๊ธฐ" ๋ฒํผ ์ถ๊ฐ. ์ด๊ฒ ๊ตฌ๋งค ๊ฒฐ์ ์ ์์ฒญ๋ ์ํฅ์ ์ค.
WebGL์ ์ง์ํ์ง ์๋ ํ๊ฒฝ(๊ตฌํ ๋ธ๋ผ์ฐ์ , ์ผ๋ถ ๋ชจ๋ฐ์ผ)์ ์ํ ํด๋ฐฑ ์ด๋ฏธ์ง ์ค๋น.
WebGLRenderer ์์ฑ ์ try-catch๋ก ์๋ฌ ์ฒ๋ฆฌํ๊ณ ํด๋ฐฑ UI ํ์.
๐ฏ ํฌํธํด๋ฆฌ์ค ์ฌ์ดํธ์์์ ํ์ฉ
๋์์ด๋๋ ๊ฐ๋ฐ์ ํฌํธํด๋ฆฌ์ค์์ WebGL์ ์ ๋ง ๊ฐ๋ ฅํ ์ฐจ๋ณํ ํฌ์ธํธ์ผ. ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ์์ ์ ์ฌ๋ฅ์ ์ดํํ ๋, ํฌํธํด๋ฆฌ์ค ์ฌ์ดํธ์ WebGL ์์๊ฐ ์์ผ๋ฉด ์ฒซ์ธ์์ด ์์ ํ ๋ฌ๋ผ์ง๊ฑฐ๋ .
โข ์คํฌ ์ฐจํธ๋ฅผ 3D ์ธํฐ๋ํฐ๋ธ ๊ทธ๋ํ๋ก ์๊ฐํ
โข ์์ ๋ฌผ ๊ฐค๋ฌ๋ฆฌ๋ฅผ 3D ๊ณต๊ฐ์ ๋ฐฐ์น (๊ฐ์ ๊ฐค๋ฌ๋ฆฌ)
โข ๋ง์ฐ์ค ์์ง์์ ๋ฐ์ํ๋ ํํฐํด ๋ฐฐ๊ฒฝ
โข ์คํฌ๋กคํ๋ฉด 3D ์ค๋ธ์ ํธ๊ฐ ์กฐ๋ฆฝ๋๋ ํจ๊ณผ
๐ฎ WebXR ์ค์ ๊ตฌํ โ AR ์ ํ ๋ฐฐ์น ๋ง๋ค๊ธฐ
์ด์ WebXR์ ์ค์ ๊ตฌํ์ ์ดํด๋ณด์. AR ์ ํ ๋ฐฐ์น ๊ธฐ๋ฅ์ ์ด์ปค๋จธ์ค์์ ๊ฐ์ฅ ์์๊ฐ ๋์ WebXR ํ์ฉ ์ฌ๋ก์ผ.
๐ฑ WebXR AR ์ธ์ ์์ํ๊ธฐ
// WebXR AR ์ธ์
์์ ์์
async function startAR() {
// AR ์ง์ ์ฌ๋ถ ํ์ธ
if (!navigator.xr) {
console.log('WebXR ๋ฏธ์ง์ ๋ธ๋ผ์ฐ์ ');
return;
}
const supported = await navigator.xr.isSessionSupported('immersive-ar');
if (!supported) {
console.log('AR ๋ฏธ์ง์ ๊ธฐ๊ธฐ');
return;
}
// AR ์ธ์
์์ฒญ
const session = await navigator.xr.requestSession('immersive-ar', {
requiredFeatures: ['hit-test'], // ํํธ ํ
์คํธ ํ์
optionalFeatures: ['dom-overlay', 'anchors'] // ์ ํ์ ๊ธฐ๋ฅ
});
// WebGL ์ปจํ
์คํธ์ XR ์ธ์
์ฐ๊ฒฐ
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl', { xrCompatible: true });
await gl.makeXRCompatible();
session.updateRenderState({
baseLayer: new XRWebGLLayer(session, gl)
});
// ํํธ ํ
์คํธ ์์ค ์์ฑ (๋ฐ๋ฅ ๊ฐ์ง)
const hitTestSource = await session.requestHitTestSource({
space: await session.requestReferenceSpace('viewer')
});
// ๋ ๋ ๋ฃจํ ์์
session.requestAnimationFrame(onXRFrame);
}
// XR ๋ ๋ ๋ฃจํ
function onXRFrame(time, frame) {
const session = frame.session;
session.requestAnimationFrame(onXRFrame);
// ํํธ ํ
์คํธ ๊ฒฐ๊ณผ ๊ฐ์ ธ์ค๊ธฐ
const hitTestResults = frame.getHitTestResults(hitTestSource);
if (hitTestResults.length > 0) {
const hit = hitTestResults[0];
const pose = hit.getPose(referenceSpace);
// ๊ฐ์ง๋ ํ๋ฉด ์์น์ 3D ์ค๋ธ์ ํธ ๋ฐฐ์น
if (pose) {
reticle.visible = true;
reticle.matrix.fromArray(pose.transform.matrix);
}
}
}
๐ A-Frame์ผ๋ก ๋น ๋ฅด๊ฒ AR ๊ตฌํํ๊ธฐ
์ฝ๋๊ฐ ๋ณต์กํ๋ค๋ฉด A-Frame์ ์จ๋ด. HTML ํ๊ทธ ๋ช ์ค๋ก AR ๊ฒฝํ์ ๋ง๋ค ์ ์์ด.
<!-- A-Frame WebXR AR ์์ -->
<script src="https://aframe.io/releases/1.4.0/aframe.min.js"></script>
<script src="https://raw.githack.com/AR-js-org/AR.js/master/aframe/build/aframe-ar.js"></script>
<a-scene embedded arjs="sourceType: webcam; debugUIEnabled: false;">
<!-- ๋ง์ปค ๊ธฐ๋ฐ AR -->
<a-marker preset="hiro">
<a-box position="0 0.5 0" material="color: #7ab8d4;" rotation="0 45 0"></a-box>
</a-marker>
<a-entity camera></a-entity>
</a-scene>
<a rel="ar" href="model.usdz"> ๋งํฌ๋ฅผ ๋ง๋ค๋ฉด iOS์์ AR ๋ณด๊ธฐ๊ฐ ๊ฐ๋ฅํด. Android๋ WebXR, iOS๋ AR Quick Look โ ๋ ๊ฐ์ง๋ฅผ ๋ชจ๋ ์ง์ํ๋ ๊ฒ ํ์ค์ ์ธ ์ ๊ทผ์ด์ผ.
๐ ๋ฏธ๋ ํธ๋ ๋ โ ์น๋์์ธ์ ๋ค์ ๋จ๊ณ
WebGL๊ณผ WebXR์ ๊ณ์ ์งํํ๊ณ ์์ด. ์์ผ๋ก ์ด๋ค ๋ฐฉํฅ์ผ๋ก ๋ฐ์ ํ ์ง ์ดํด๋ณด์.
โก WebGPU โ WebGL์ ํ๊ณ์
WebGPU๋ WebGL์ ๋์ฒดํ ์ฐจ์ธ๋ ์น ๊ทธ๋ํฝ API์ผ. 2023๋ Chrome 113์์ ์ ์ ์ง์์ ์์ํ์ด. WebGL์ด OpenGL ES ๊ธฐ๋ฐ์ด๋ผ๋ฉด, WebGPU๋ Vulkan, Metal, Direct3D 12 ๊ฐ์ ์ต์ ๊ทธ๋ํฝ API๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํด.
โข ๋ ๋ฎ์ ์ค๋ฒํค๋: CPU-GPU ํต์ ์ด ๋ ํจ์จ์
โข ๋ฉํฐ์ค๋ ๋ฉ: Web Worker์์ GPU ์์ ๊ฐ๋ฅ
โข AI/ML ๊ฐ์: ๋ธ๋ผ์ฐ์ ์์ ๋ฅ๋ฌ๋ ๋ชจ๋ธ ์คํ ๊ฐ๋ฅ
โข ๋ ๋์ ์๋ฌ ์ฒ๋ฆฌ: ๋๋ฒ๊น ์ด ํจ์ฌ ์ฌ์์ง
๐ค AI + WebGL์ ๋ง๋จ
AI๊ฐ ์น 3D ๋ถ์ผ์๋ ํ๊ณ ๋ค๊ณ ์์ด. ๋ช ๊ฐ์ง ํฅ๋ฏธ๋ก์ด ํธ๋ ๋๋ฅผ ์๊ฐํ ๊ฒ:
๐ ๊ณต๊ฐ ์ปดํจํ ๊ณผ ์น
Apple Vision Pro์ ๋ฑ์ฅ์ผ๋ก ๊ณต๊ฐ ์ปดํจํ ์ด ํ์ค์ด ๋์ด. visionOS์ Safari๋ WebXR์ ์ง์ํ๊ณ , ์น์ฌ์ดํธ๊ฐ 3D ๊ณต๊ฐ์ ์ฐฝ์ผ๋ก ๋ ์๋ ๊ฒฝํ์ ์ ๊ณตํด. ์์ผ๋ก ์น๋์์ธ์ 2D ํ๋ฉด๋ฟ ์๋๋ผ 3D ๊ณต๊ฐ๋ ๊ณ ๋ คํด์ผ ํ๋ ์๋๊ฐ ์ค๊ณ ์์ด.
๐ ์์ํ๋ ๋ฐฉ๋ฒ โ ํ์ต ๋ก๋๋งต
์, ์ด์ "๋๋ ํด๋ณด๊ณ ์ถ๋ค!"๋ ์๊ฐ์ด ๋ค์ง? ์ด๋์๋ถํฐ ์์ํด์ผ ํ ์ง ๋ช ํํ ๋ก๋๋งต์ ์ค๊ฒ.
๐บ๏ธ WebGL/WebXR ํ์ต ๋ก๋๋งต
WebGL ์ ์ JavaScript๋ฅผ ์ ์์์ผ ํด. ํนํ ๋ฐฐ์ด, ํ์ ๋ ์ด๋ ์ด(Float32Array), ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ์ดํดํด์ผ ํด. Canvas 2D API๋ ๋จผ์ ์ตํ๋๋ฉด ์ข์. ๊ธฐ๊ฐ: 1~2๊ฐ์
์์ WebGL๋ณด๋ค Three.js๋ถํฐ ์์ํ๋ ๊ฑธ ๊ฐ๋ ฅ ์ถ์ฒํด. Three.js Journey(Bruno Simon์ ๊ฐ์)๊ฐ ์ต๊ณ ์ผ. ์ฌ, ์นด๋ฉ๋ผ, ๋จธํฐ๋ฆฌ์ผ, ์กฐ๋ช ์ ๊ธฐ๋ณธ์ ์ตํ. ๊ธฐ๊ฐ: 1~2๊ฐ์
The Book of Shaders(thebookofshaders.com)๊ฐ ์ต๊ณ ์ ๋ฌด๋ฃ ๋ฆฌ์์ค์ผ. ์ํ ๊ฐ๋ (๋ฒกํฐ, ํ๋ ฌ, ์ผ๊ฐํจ์)๋ ํจ๊ป ๊ณต๋ถํด์ผ ํด. Shadertoy์์ ๋ค๋ฅธ ์ฌ๋๋ค์ ์ ฐ์ด๋๋ฅผ ๋ถ์ํ๋ ๊ฒ๋ ์ข์. ๊ธฐ๊ฐ: 2~3๊ฐ์
A-Frame์ผ๋ก ๊ฐ๋จํ VR/AR ์ฌ์ ๋ง๋ค์ด๋ด. ๊ทธ ๋ค์ WebXR Device API๋ฅผ ์ง์ ๋ค๋ค๋ด. Immersive Web Weekly ๋ด์ค๋ ํฐ๋ฅผ ๊ตฌ๋ ํ๋ฉด ์ต์ ํธ๋ ๋๋ฅผ ํ์ ํ ์ ์์ด. ๊ธฐ๊ฐ: 1~2๊ฐ์
๋ฐฐ์ด ๊ฑธ ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํด๋ด. ์ ํ ๋ทฐ์ด, ์ธํฐ๋ํฐ๋ธ ํฌํธํด๋ฆฌ์ค, ๋ฐ์ดํฐ ์๊ฐํ ์ค ํ๋๋ฅผ ๊ณจ๋ผ์ ์์ฑ๋ ์๊ฒ ๋ง๋ค์ด๋ด. ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์ ์ฌ๋ ค์ ์ค์ ํด๋ผ์ด์ธํธ์ ์ผํด๋ณด๋ ๊ฒ๋ ์ข์ ๊ฒฝํ์ด์ผ.
๐ ์ถ์ฒ ํ์ต ๋ฆฌ์์ค
| ๋ฆฌ์์ค | ์ข ๋ฅ | ํน์ง |
|---|---|---|
| Three.js Journey | ์ ๋ฃ ๊ฐ์ | ๊ฐ์ฅ ์ฒด๊ณ์ ์ธ Three.js ๊ฐ์, Bruno Simon ์ ์ |
| The Book of Shaders | ๋ฌด๋ฃ ์ฑ | GLSL ์ ฐ์ด๋ ์ ๋ฌธ์ ๋ฐ์ด๋ธ |
| WebGL Fundamentals | ๋ฌด๋ฃ ํํ ๋ฆฌ์ผ | ์์ WebGL ๊ธฐ์ด๋ฅผ ๊ฐ์ฅ ์ ์ค๋ช ํ ์ฌ์ดํธ |
| Shadertoy | ์ปค๋ฎค๋ํฐ | ์ ์ธ๊ณ ์ ฐ์ด๋ ์ํฐ์คํธ๋ค์ ์ํ ๊ณต์ |
| Sketchfab | 3D ๋ชจ๋ธ ๋ง์ผ | ๋ฌด๋ฃ/์ ๋ฃ 3D ๋ชจ๋ธ ๋ค์ด๋ก๋ |
| Immersive Web | ๊ณต์ ๋ฌธ์ | W3C WebXR ๊ณต์ ์์ ๋ฐ ๋ฌธ์ |
๐ก ์ค์ ํ ๋ชจ์ โ ํ์ ์์ ๋ฐ๋ก ์ฐ๋ ๊ฒ๋ค
๋ง์ง๋ง์ผ๋ก ์ค์ ํ๋ก์ ํธ์์ ๋ฐ๋ก ์จ๋จน์ ์ ์๋ ํ๋ค์ ๋ชจ์๋ดค์ด.
๐ง Three.js ์ค์ ํ
renderer.outputColorSpace = THREE.SRGBColorSpace๋ฅผ ์ค์ ํด์ผ ์์์ด ์ ํํ๊ฒ ๋์. ์ ํ๋ฉด ์์ด ์ด๋ก๊ฑฐ๋ ์ด์ํ๊ฒ ๋ณด์ฌ. ๋ renderer.toneMapping = THREE.ACESFilmicToneMapping์ ์ฐ๋ฉด ์ํ ๊ฐ์ ์๊ฐ์ด ๋์.
geometry.dispose(), material.dispose(), texture.dispose()๋ฅผ ๋ฐ๋์ ํธ์ถํด์ผ ํด. ์ ํ๋ฉด ๋ฉ๋ชจ๋ฆฌ ๋์๊ฐ ์๊ฒจ.
castShadow์ receiveShadow๋ฅผ ์ค์ ํด. ๊ทธ๋ฆผ์ ๋งต ํฌ๊ธฐ๋ light.shadow.mapSize.set(1024, 1024)๋ก ์ ์ ํ ์กฐ์ ํด.
์ฐฝ ํฌ๊ธฐ๊ฐ ๋ฐ๋ ๋ ์นด๋ฉ๋ผ ์ข ํก๋น์ ๋ ๋๋ฌ ํฌ๊ธฐ๋ฅผ ์ ๋ฐ์ดํธํด์ผ ํด:
camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);
THREE.LoadingManager๋ฅผ ์ฌ์ฉํ๋ฉด ๋ชจ๋ ์์
์ ๋ก๋ฉ ์งํ๋๋ฅผ ํ ๊ณณ์์ ๊ด๋ฆฌํ ์ ์์ด. ๋ก๋ฉ ํ๋ฉด UI๋ฅผ ๋ง๋ค ๋ ํ์์ผ.
๐จ ๋์์ธ ๊ด์ ์์์ ํ
prefers-reduced-motion ๋ฏธ๋์ด ์ฟผ๋ฆฌ๋ฅผ ํ์ธํ๊ณ , ์ ๋๋ฉ์ด์
์ ์ค์ด๊ฑฐ๋ ๋๋ ์ต์
์ ์ ๊ณตํด. WebGL์ ์ง์ํ์ง ์๋ ํ๊ฒฝ์ ์ํ ํด๋ฐฑ๋ ํ์์ผ.
new THREE.Color().setStyle('#7ab8d4')์ฒ๋ผ CSS ์์ ๋ฌธ์์ด์ ์ง์ ์ฌ์ฉํ๋ฉด ์์ ๊ณต๊ฐ ๋ณํ์ด ์๋์ผ๋ก ์ฒ๋ฆฌ๋ผ.
controls.enablePan = false๋ก ํจ๋์ ๋นํ์ฑํํ๊ฑฐ๋, ํน์ ์์ญ์์๋ง 3D ์ธํฐ๋์
์ด ํ์ฑํ๋๋๋ก ์ค๊ณํด.
๐ฏ ๋ง๋ฌด๋ฆฌ โ ์น๋์์ธ์ ์ ์งํ
์, ์ฌ๊ธฐ๊น์ง WebGL๊ณผ WebXR์ ์ธ๊ณ๋ฅผ ํจ๊ป ํํํ์ด! ๐
์ ๋ฆฌํ์๋ฉด, WebGL์ ๋ธ๋ผ์ฐ์ ์์ GPU๋ฅผ ์ง์ ํ์ฉํด 3D ๊ทธ๋ํฝ์ ๋ ๋๋งํ๋ ๊ธฐ์ ์ด๊ณ , WebXR์ VR/AR/MR ๊ฒฝํ์ ์น์์ ๊ตฌํํ๋ ํ์ค API์ผ. ์ด ๋ ๊ธฐ์ ์ด ๊ฒฐํฉ๋๋ฉด์ ์น๋์์ธ์ ๊ฒฝ๊ณ๊ฐ ์์ ํ ์๋ก ๊ทธ๋ ค์ง๊ณ ์์ด.
Three.js, React Three Fiber, A-Frame ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ด ์ง์ ์ฅ๋ฒฝ์ ๋ฎ์ถฐ์ฃผ๊ณ ์๊ณ , Spline ๊ฐ์ ๋ ธ์ฝ๋ ๋๊ตฌ๋ค๋ ๋ฑ์ฅํ๋ฉด์ ๋์์ด๋๋ 3D ์น ๊ฒฝํ์ ๋ง๋ค ์ ์๋ ์๋๊ฐ ๋์ด.
์์ธํ์ด์ง์์ 3D ์ ํ ๋ทฐ์ด, ํฌํธํด๋ฆฌ์ค์ ์ธํฐ๋ํฐ๋ธ 3D ์์, AR ์ ํ ๋ฐฐ์น ๊ธฐ๋ฅ โ ์ด๋ฐ ๊ฒ๋ค์ด ์ด์ "ํน๋ณํ ๊ฒ"์ด ์๋๋ผ "๊ธฐ๋ณธ ๊ธฐ๋์น"๊ฐ ๋์ด๊ฐ๊ณ ์์ด. ์ง๊ธ ์์ํ๋ฉด ์ถฉ๋ถํ ์์๊ฐ ์ ์์ด.
2. Three.js ๊ณต์ ์ฌ์ดํธ(threejs.org)์ ์์ ๋ค ์ดํด๋ณด๊ธฐ
3. Shadertoy(shadertoy.com)์์ ์ ฐ์ด๋ ์ํธ ๊ฐ์ํ๊ธฐ
4. A-Frame(aframe.io)์ผ๋ก ๊ฐ๋จํ VR ์ฌ 5๋ถ ๋ง์ ๋ง๋ค์ด๋ณด๊ธฐ
5. ์ฌ๋ฅ๋ท์์ WebGL/Three.js ์ ๋ฌธ๊ฐ๋ฅผ ์ฐพ์ ํ์ ํ๊ฑฐ๋, ๋ณธ์ธ์ 3D ์น ์คํฌ์ ์๋น์ค๋ก ๋ฑ๋กํด๋ณด๊ธฐ!
์น๋์์ธ์ ๋ฏธ๋๋ 2D ํ๋ฉด์ ๋์ด 3D ๊ณต๊ฐ์ผ๋ก ํ์ฅ๋๊ณ ์์ด. WebGL๊ณผ WebXR์ ๊ทธ ๋ณํ์ ์ต์ ์ ์ ์๋ ๊ธฐ์ ์ด์ผ. ์ด ํ๋ฆ์ ์ฌ๋ผํ๋ ๊ฑด ์ ํ์ด ์๋๋ผ ํ์๊ฐ ๋์ด๊ฐ๊ณ ์์ด. ์ง๊ธ ๋ฐฐ์ฐ๊ธฐ ์์ํ๋ฉด, ๋ช ๋ ํ์ "๊ทธ๋ ์์ํ๊ธธ ์ํ๋ค"๋ ์๊ฐ์ด ๋ค ๊ฑฐ์ผ. ๐ช
์, ์ด์ ๋ธ๋ผ์ฐ์ ๋ฅผ ์ด๊ณ ์ฝ๋๋ฅผ ์ณ๋ด. 3D ์ธ๊ณ๊ฐ ๊ธฐ๋ค๋ฆฌ๊ณ ์์ด! ๐โจ
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.