WebGL - 캐브 소환

안녕하세요, 미래의 WebGL 마법사 여러분! 오늘 우리는 3D 그래픽의 세계로 흥미로운 여정을 떠납니다. 이 튜토리얼의 끝까지 따라오시면, WebGL을 사용하여 회전하는 캐브를 만들 수 있게 될 것입니다. 멋지지 않나요? 시작해보겠습니다!

WebGL - Cube Rotation

기본 개념 이해

캐브를 DJ처럼 돌리기 전에, 몇 가지 기본 개념을 이해해보겠습니다.

WebGL이란?

WebGL (Web Graphics Library)는 브라우저에서 3D 그래픽을 렌더링할 수 있도록 해주는 JavaScript API입니다. 브라우저에 3D 안경을 씌우는 것과 같습니다!

왜 캐브인가요?

"왜 캐브로 시작하나요?"라는 의문이 드실 수 있습니다. 그러나 캐브는 3D 그래픽의 "Hello World"입니다. 이해하기 쉬우면서도 중요한 개념을 가르쳐줄 만큼 복잡합니다. 그리고, 누가 캐브를 좋아하지 않을까요?

WebGL 환경 설정

HTML 캔버스

먼저, 캐브가 공연할 무대가 필요합니다. WebGL에서 이 무대를 캔버스라고 합니다. 설정해보겠습니다:

<canvas id="glcanvas" width="640" height="480">
Your browser doesn't support HTML5 canvas
</canvas>

이렇게 하면 640x480 픽셀의 캔버스를 만듭니다. 보이지 않는다면 걱정하지 마세요 - 지금은 보이지 않는 춤의 무대입니다.

WebGL 초기화

이제 WebGL을 준비해보겠습니다:

var canvas = document.getElementById('glcanvas');
var gl = canvas.getContext('webgl');

if (!gl) {
console.log('WebGL not supported, falling back on experimental-webgl');
gl = canvas.getContext('experimental-webgl');
}

if (!gl) {
alert('Your browser does not support WebGL');
}

이 코드는 우리의 WebGL 컨텍스트를 가져옵니다. 브라우저가 WebGL을 지원하지 않으면, VHS 플레이어에서 DVD를 재생하려는 것과 같이 작동하지 않습니다!

3D 캐브 생성

꼭짓점 정의

캐브는 8개의 꼭짓점(頂点)을 가지고 있습니다. WebGL에 이 꼭짓점이 어디 있는지 알려줘야 합니다:

var vertices = [
// 앞면
-1.0, -1.0,  1.0,
1.0, -1.0,  1.0,
1.0,  1.0,  1.0,
-1.0,  1.0,  1.0,

// 뒷면
-1.0, -1.0, -1.0,
-1.0,  1.0, -1.0,
1.0,  1.0, -1.0,
1.0, -1.0, -1.0,

// 윗면
-1.0,  1.0, -1.0,
-1.0,  1.0,  1.0,
1.0,  1.0,  1.0,
1.0,  1.0, -1.0,

// 아랫면
-1.0, -1.0, -1.0,
1.0, -1.0, -1.0,
1.0, -1.0,  1.0,
-1.0, -1.0,  1.0,

// 오른쪽면
1.0, -1.0, -1.0,
1.0,  1.0, -1.0,
1.0,  1.0,  1.0,
1.0, -1.0,  1.0,

// 왼쪽면
-1.0, -1.0, -1.0,
-1.0, -1.0,  1.0,
-1.0,  1.0,  1.0,
-1.0,  1.0, -1.0
];

각 세트의 세 개의 숫자는 캐브의 꼭짓점을 3D 공간에서 나타냅니다. WebGL에 캐브의 지도를 주는 것과 같습니다!

버퍼 생성

이제 이 데이터를 GPU로 보내야 합니다:

var vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

이것은 캐브를 가방(버퍼)에 싸서 GPU로 보내는 것과 같습니다.

셰이더 - WebGL의 마법사

顶点 셰이더

顶点 셰이더는顶点의 위치를 설정합니다:

var vertexShaderSource = `
attribute vec3 aVertexPosition;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
void main(void) {
gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aVertexPosition, 1.0);
}
`;

이 셰이더는 각顶点에 우리의 변환 행렬을 적용합니다. 마법사가顶点을 이동시키는 것과 같습니다!

프래그먼트 셰이더

프래그먼트 셰이더는 캐브를 칠합니다:

var fragmentShaderSource = `
void main(void) {
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
`;

이 간단한 셰이더는 모든 것을 흰색으로 칠합니다. 캐브를 칠하는 것과 같습니다!

셰이더 컴파일 및 링크

이제 셰이더를 컴파일하고 프로그램에 링크합니다:

function getShader(gl, source, type) {
var shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);

if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));
return null;
}

return shader;
}

var vertexShader = getShader(gl, vertexShaderSource, gl.VERTEX_SHADER);
var fragmentShader = getShader(gl, fragmentShaderSource, gl.FRAGMENT_SHADER);

var shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);

if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram));
}

gl.useProgram(shaderProgram);

이것은 마법사(셰이더)를 가르치고 무대에 올리는 것과 같습니다!

캐브 회전

회전 설정

캐브를 회전시키기 위해 시간이 지남에 따라 회전 각도를 업데이트해야 합니다:

var cubeRotation = 0.0;

function render(now) {
now *= 0.001;  // convert to seconds
const deltaTime = now - then;
then = now;

cubeRotation += deltaTime;

drawScene();

requestAnimationFrame(render);
}

이 함수는 반복적으로 호출되어 캐브의 회전을 갱신합니다.

그리기 함수

이제 모든 것을 합쳐서 회전하는 캐브를 그리겠습니다:

function drawScene() {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

const projectionMatrix = mat4.create();
mat4.perspective(projectionMatrix, 45 * Math.PI / 180, gl.canvas.clientWidth / gl.canvas.clientHeight, 0.1, 100.0);

const modelViewMatrix = mat4.create();
mat4.translate(modelViewMatrix, modelViewMatrix, [-0.0, 0.0, -6.0]);
mat4.rotate(modelViewMatrix, modelViewMatrix, cubeRotation, [0, 1, 1]);

gl.uniformMatrix4fv(gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'), false, projectionMatrix);
gl.uniformMatrix4fv(gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'), false, modelViewMatrix);

gl.drawArrays(gl.TRIANGLE_STRIP, 0, 36);
}

이 함수는 캔버스를 지우고 시점을 설정하고 회전을 적용한 후 캐브를 그립니다.

결론

그렇습니다, 여러분! 우리는 WebGL을 사용하여 회전하는 3D 캐브를 만들었습니다. WebGL을 마스터하는 것은 저그링하는 것과 같습니다 - 연습이 필요하지만, 한 번 익혀지면 놀라운 것을 할 수 있습니다!

여기 우리가 사용한 주요 메서드를 요약한 표입니다:

메서드 설명
gl.createBuffer() 새로운 버퍼 객체 생성
gl.bindBuffer() 버퍼 객체를 대상에 바인드
gl.bufferData() 버퍼 객체의 데이터 저장소 생성 및 초기화
gl.createShader() 셰이더 객체 생성
gl.shaderSource() 셰이더 객체의 소스 코드 설정
gl.compileShader() 셰이더 객체 컴파일
gl.createProgram() 프로그램 객체 생성
gl.attachShader() 셰이더 객체를 프로그램 객체에 첨부
gl.linkProgram() 프로그램 객체 링크
gl.useProgram() 지정된 프로그램을 현재 렌더링 상태로 설정
gl.clear() 버퍼를 기본값으로 지우기
gl.uniformMatrix4fv() 일관된 4x4 행렬을 일관된.uniform 변수에 지정
gl.drawArrays() 배열 데이터에서 프리미티브 렌더링

계속 연습하고, 계속 코딩하면 곧 브라우저에서 놀라운 3D 그래픽을 만들 수 있을 것입니다. 행복한 코딩을 기원합니다!

Credits: Image by storyset