instanced rectangle renderer
rectangle이 많아지면 매 rectangle마다 여섯 vertex를 복사하는 방식이 아깝게 느껴집니다. 모든 rectangle은 같은 기본 모양을 갖고, 위치와 크기와 색만 다르기 때문입니다.
이때 사용할 수 있는 방식이 instancing입니다.
WebGL2 instancing 코드
unit rectangle vertex buffer는 한 번만 만들고, node별 값은 instance buffer에 넣습니다.
const unitQuad = new Float32Array([
0, 0,
1, 0,
0, 1,
0, 1,
1, 0,
1, 1
]);
const instances = new Float32Array([
// x, y, width, height, r, g, b, a
40, 32, 160, 96, 0.60, 0.78, 0.92, 1,
240, 80, 120, 72, 0.96, 0.78, 0.37, 1
]);
각 instance attribute는 vertexAttribDivisor(location, 1)로 설정합니다. 이 값이 핵심입니다. vertex마다 바뀌는 값이 아니라 instance마다 한 번씩 바뀌는 값이라는 뜻입니다.
gl.bindVertexArray(vao);
gl.bindBuffer(gl.ARRAY_BUFFER, quadBuffer);
gl.bufferData(gl.ARRAY_BUFFER, unitQuad, gl.STATIC_DRAW);
gl.enableVertexAttribArray(unitPositionLocation);
gl.vertexAttribPointer(unitPositionLocation, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, instanceBuffer);
gl.bufferData(gl.ARRAY_BUFFER, instances, gl.DYNAMIC_DRAW);
const stride = 8 * Float32Array.BYTES_PER_ELEMENT;
gl.enableVertexAttribArray(instanceRectLocation);
gl.vertexAttribPointer(instanceRectLocation, 4, gl.FLOAT, false, stride, 0);
gl.vertexAttribDivisor(instanceRectLocation, 1);
gl.enableVertexAttribArray(instanceColorLocation);
gl.vertexAttribPointer(
instanceColorLocation,
4,
gl.FLOAT,
false,
stride,
4 * Float32Array.BYTES_PER_ELEMENT
);
gl.vertexAttribDivisor(instanceColorLocation, 1);
gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, instances.length / 8);
vertex shader에서는 unit 좌표와 instance rectangle을 조합합니다.
in vec2 a_unitPosition;
in vec4 a_rect;
in vec4 a_color;
out vec4 v_color;
void main() {
vec2 world = a_rect.xy + a_unitPosition * a_rect.zw;
vec3 clip = u_matrix * vec3(world, 1.0);
gl_Position = vec4(clip.xy, 0.0, 1.0);
v_color = a_color;
}
unit rectangle은 한 번만 둔다
기본 rectangle geometry는 이렇게 고정할 수 있습니다.
(0,0), (1,0), (0,1)
(0,1), (1,0), (1,1)
그리고 각 instance마다 size, transform, color를 따로 보냅니다.
shared vertices:
unit rectangle
per instance data:
x, y, width, height, color
instance data는 node 목록에 가깝다
instancing은 scene node와 잘 맞습니다. node 하나가 instance 하나가 될 수 있기 때문입니다.
node -> instance row
vertex shader는 shared vertex와 instance data를 조합해서 최종 좌표를 만듭니다.
모든 도형이 instancing에 맞지는 않는다
사각형, 이미지 quad, 간단한 glyph quad는 instancing과 잘 맞습니다. 하지만 복잡한 path나 서로 다른 vertex 수를 가진 shape는 다른 전략이 필요합니다.
그래서 renderer는 여러 path를 가질 수 있습니다.
rect instance renderer
image instance renderer
path mesh renderer
overlay line renderer
오늘의 핵심
instancing은 같은 geometry를 여러 node에 재사용하는 방법입니다.
one unit rectangle
many instance records
one draw call
Figma-like editor에서 rectangle과 image node가 많다면 매우 중요한 렌더링 패턴입니다.