WebGL context와 첫 triangle
Canvas 2D에서는 fillRect 같은 명령으로 바로 픽셀을 그렸습니다. WebGL에서는 조금 더 돌아갑니다. 먼저 canvas에서 WebGL context를 얻고, GPU가 실행할 shader를 준비하고, vertex 데이터를 보내고, draw call을 호출합니다.
처음에는 번거로워 보입니다. 하지만 이 번거로움이 나중에 수천 개의 도형을 빠르게 그릴 수 있는 길이 됩니다.
실제로 돌아가는 최소 코드
아래 코드는 위 demo의 핵심과 같은 구조입니다. WebGL은 “shader 준비 -> buffer 준비 -> attribute 연결 -> draw” 순서로 첫 픽셀을 만듭니다.
const canvas = document.querySelector("canvas");
const gl = canvas.getContext("webgl2");
const vertexSource = `#version 300 es
in vec2 a_position;
void main() {
gl_Position = vec4(a_position, 0.0, 1.0);
}
`;
const fragmentSource = `#version 300 es
precision highp float;
out vec4 outColor;
void main() {
outColor = vec4(0.22, 0.55, 1.0, 1.0);
}
`;
function compileShader(type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
throw new Error(gl.getShaderInfoLog(shader));
}
return shader;
}
const program = gl.createProgram();
gl.attachShader(program, compileShader(gl.VERTEX_SHADER, vertexSource));
gl.attachShader(program, compileShader(gl.FRAGMENT_SHADER, fragmentSource));
gl.linkProgram(program);
const positions = new Float32Array([
-0.72, -0.58,
0.72, -0.58,
0.00, 0.72
]);
const vao = gl.createVertexArray();
const buffer = gl.createBuffer();
const location = gl.getAttribLocation(program, "a_position");
gl.bindVertexArray(vao);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
gl.enableVertexAttribArray(location);
gl.vertexAttribPointer(location, 2, gl.FLOAT, false, 0, 0);
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clearColor(0.08, 0.09, 0.1, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(program);
gl.drawArrays(gl.TRIANGLES, 0, 3);
이 코드에서 positions는 CPU 쪽 숫자 배열이고, gl.bufferData 이후에는 GPU가 읽을 수 있는 buffer가 됩니다. gl.vertexAttribPointer는 “buffer의 숫자 두 개를 a_position 하나로 읽어라”라는 연결 규칙입니다.
context는 GPU 렌더러의 입구다
WebGL 렌더러는 canvas에서 시작합니다.
const canvas = document.querySelector("canvas");
const gl = canvas.getContext("webgl2");
gl은 DOM 요소가 아닙니다. GPU에 명령을 보내기 위한 상태ful API입니다. shader program, buffer, texture, viewport, blending 같은 렌더링 상태를 이 context를 통해 설정합니다.
첫 목표는 triangle이다
WebGL에서 모든 2D 도형은 결국 triangle로 내려갑니다. 사각형도 triangle 두 개고, 복잡한 path도 잘게 쪼개면 triangle 목록입니다.
그래서 첫 렌더러의 목표는 거창하지 않습니다.
vertex 3개를 보낸다.
GPU가 triangle 하나를 그린다.
이 작은 성공이 WebGL renderer의 최소 회로입니다.
지금은 편집기와 분리해서 본다
첫 triangle을 그릴 때는 scene graph, selection, camera를 잠시 내려놓습니다. 먼저 GPU에 데이터를 보내고 화면에 색이 나오는지 확인합니다.
하지만 목적지는 잊지 않습니다.
triangle
-> rectangle
-> many rectangles
-> scene nodes
-> editor renderer
오늘의 triangle은 API 튜토리얼이 아니라 Figma-like editor renderer의 첫 픽셀입니다.
오늘의 핵심
WebGL context는 Canvas를 GPU 렌더링 표면으로 바꾸는 입구입니다. 첫 triangle은 renderer가 살아 있는지 확인하는 가장 작은 단위입니다.
canvas -> WebGL context -> shader -> buffer -> draw
이 흐름을 잡으면 다음 레슨에서 WebGL의 낯선 좌표계인 clip space를 다룰 준비가 됩니다.