matrix uniform으로 camera 적용하기
앞에서 camera는 world 좌표를 screen 좌표로 바꾸는 장치라고 했습니다. WebGL에서는 이 변환을 vertex shader에 맡길 수 있습니다.
방법은 matrix를 uniform으로 보내는 것입니다.
camera matrix를 uniform으로 보내는 코드
아래는 2D용 3x3 matrix를 만들고 shader의 u_matrix로 보내는 최소 형태입니다.
function projection(width, height) {
return [
2 / width, 0, 0,
0, -2 / height, 0,
-1, 1, 1
];
}
function cameraMatrix(camera) {
const z = camera.zoom;
return [
z, 0, 0,
0, z, 0,
-camera.x * z, -camera.y * z, 1
];
}
function multiply(a, b) {
return [
a[0] * b[0] + a[3] * b[1] + a[6] * b[2],
a[1] * b[0] + a[4] * b[1] + a[7] * b[2],
a[2] * b[0] + a[5] * b[1] + a[8] * b[2],
a[0] * b[3] + a[3] * b[4] + a[6] * b[5],
a[1] * b[3] + a[4] * b[4] + a[7] * b[5],
a[2] * b[3] + a[5] * b[4] + a[8] * b[5],
a[0] * b[6] + a[3] * b[7] + a[6] * b[8],
a[1] * b[6] + a[4] * b[7] + a[7] * b[8],
a[2] * b[6] + a[5] * b[7] + a[8] * b[8]
];
}
const camera = { x: 120, y: 80, zoom: 1.5 };
const uMatrix = multiply(projection(canvas.width, canvas.height), cameraMatrix(camera));
gl.useProgram(program);
gl.uniformMatrix3fv(matrixLocation, false, new Float32Array(uMatrix));
gl.drawArrays(gl.TRIANGLES, 0, vertexCount);
이 구조가 중요한 이유는 node vertex buffer를 그대로 두고 camera만 바꿀 수 있기 때문입니다. pan/zoom 때 geometry를 다시 만들지 않아도 됩니다.
uniform은 draw call 동안 공유되는 값이다
attribute는 vertex마다 달라지는 값입니다. position이 대표적입니다.
uniform은 draw call 동안 동일하게 쓰이는 값입니다. camera matrix처럼 모든 vertex에 같은 방식으로 적용되는 값에 어울립니다.
uniform mat3 u_matrix;
in vec2 a_position;
2D affine transform은 3x3 matrix로 표현하기 좋습니다.
local에서 clip까지 한 번에 보낸다
renderer는 여러 matrix를 곱해서 shader에 보낼 수 있습니다.
clipMatrix * cameraMatrix * nodeMatrix
혹은 일부는 CPU에서 곱하고 일부는 shader에서 곱할 수 있습니다. 중요한 것은 변환 순서가 명확해야 한다는 점입니다.
local -> world -> screen -> clip
CSS transform 순서에서 배운 감각이 그대로 쓰입니다.
camera가 바뀌면 uniform만 바꾼다
pan/zoom을 할 때 모든 vertex를 다시 만들 필요는 없습니다. camera matrix uniform만 바꿔도 됩니다.
same node buffer
new camera uniform
draw again
이것이 GPU renderer의 장점 중 하나입니다. 문서 geometry와 view transform을 분리할 수 있습니다.
오늘의 핵심
matrix uniform은 CSS transform 감각을 shader로 옮기는 다리입니다.
scene geometry는 그대로 둔다.
camera matrix를 바꾼다.
vertex shader가 최종 위치를 계산한다.
이 구조가 잡히면 pan/zoom이 많은 도형에서도 가볍게 동작할 수 있습니다.