WebGL/WebGPU 디버그 overlay 만들기
GPU editor에서 가장 먼저 만들어야 할 개발 도구는 debug overlay입니다. 화면에 보이는 픽셀과 editor model 사이를 바로 확인할 수 있어야 합니다.
overlay canvas로 디버그 정보를 그리는 코드
content canvas 위에 debug 전용 canvas를 하나 더 두면 문서 렌더링과 디버그 렌더링을 분리할 수 있습니다.
function drawDebugOverlay(ctx, snapshot) {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.save();
ctx.scale(devicePixelRatio, devicePixelRatio);
drawWorldAxis(ctx, snapshot.camera);
for (const nodeId of snapshot.selection) {
const node = snapshot.scene.nodesById.get(nodeId);
const bounds = worldBoundsToScreen(nodeWorldBounds(node), snapshot.camera);
ctx.strokeStyle = "#0d9488";
ctx.lineWidth = 1;
ctx.strokeRect(bounds.x, bounds.y, bounds.width, bounds.height);
}
if (snapshot.pointerWorld) {
const p = worldToScreen(snapshot.pointerWorld, snapshot.camera);
ctx.fillStyle = "#f6c85f";
ctx.fillRect(p.x - 3, p.y - 3, 6, 6);
}
ctx.restore();
}
무엇을 보여줄까
camera axes
selection bounds
pointer world position
draw region
picking result
content와 같은 canvas에 그릴 수도 있고, 별도 overlay canvas/DOM/SVG layer로 둘 수도 있습니다. 중요한 것은 문서 스타일과 debug UI를 섞지 않는 것입니다.
overlay도 HiDPI canvas로 맞춘다
function resizeOverlayCanvas(canvas) {
const rect = canvas.getBoundingClientRect();
const dpr = window.devicePixelRatio || 1;
canvas.width = Math.round(rect.width * dpr);
canvas.height = Math.round(rect.height * dpr);
const ctx = canvas.getContext("2d");
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
return ctx;
}
function renderDebugLayer(canvas, snapshot) {
const ctx = resizeOverlayCanvas(canvas);
ctx.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight);
drawDebugOverlay(ctx, snapshot);
}
debug overlay가 흐릿하면 bounds와 pointer readout도 신뢰하기 어렵습니다. content canvas와 같은 DPR 정책을 overlay에도 적용해야 합니다.
오늘의 핵심
좋은 GPU editor는 좋은 debug overlay를 갖고 있습니다. 좌표축과 bounds만 보여줘도 버그 탐색 시간이 줄어듭니다.