layer order와 z sorting
2D design editor에서 앞뒤 순서는 문서 의미의 일부입니다. 사용자가 Bring to front, Send backward 같은 명령을 쓰는 이유입니다.
WebGL renderer로 넘어가도 이 순서는 사라지지 않습니다.
children order로 draw list를 만드는 코드
scene graph를 renderer가 그릴 flat draw list로 펼칠 때 children 배열 순서를 그대로 보존합니다.
function buildDrawList(node, scene, drawList = []) {
if (node.visible === false) return drawList;
if (node.type !== "page" && node.type !== "group") {
drawList.push(node);
}
for (const childId of node.children ?? []) {
buildDrawList(scene.nodesById.get(childId), scene, drawList);
}
return drawList;
}
const drawList = buildDrawList(scene.root, scene);
for (const node of drawList) {
renderer.drawNode(node);
}
layer panel에서 순서를 바꾸는 명령은 결국 parent의 children 배열을 바꾸는 일입니다.
function reorderChild(document, parentId, childId, toIndex) {
const parent = document.nodesById.get(parentId);
const children = parent.children.filter((id) => id !== childId);
children.splice(toIndex, 0, childId);
return updateNode(document, parentId, { children });
}
hit test는 같은 순서를 거꾸로 봅니다. 마지막에 그린 node가 화면에서 가장 위에 있기 때문입니다.
for (let i = drawList.length - 1; i >= 0; i -= 1) {
if (pointInNode(worldPoint, drawList[i])) {
return drawList[i];
}
}
children 배열이 layer order다
가장 단순한 모델에서는 parent의 children 배열 순서가 곧 layer order입니다.
children: ["background", "card", "button", "label"]
앞에 있는 항목을 먼저 그리고, 뒤에 있는 항목을 나중에 그리면 나중 항목이 위에 보입니다.
z-index처럼 따로 둘 수도 있다
CSS에 익숙하면 z-index를 떠올릴 수 있습니다. editor model에서도 z 값을 둘 수 있지만, design tool에서는 명시적인 layer order 배열이 더 직관적인 경우가 많습니다.
layer panel order <-> children order
사용자가 layer panel에서 항목을 드래그하면 children 배열이 바뀝니다.
batching과 충돌할 수 있다
renderer는 같은 shader나 texture를 쓰는 node를 묶고 싶어합니다. 하지만 layer order가 먼저입니다.
정확한 순서로 그린다.
그 안에서 가능한 것만 batch한다.
특히 alpha blending이 있는 2D 문서에서는 순서가 시각 결과를 바꿉니다.
오늘의 핵심
layer order는 renderer 최적화가 아니라 문서 의미입니다.
children order
-> draw order
-> hit test priority
-> layer panel order
이 네 가지가 같은 모델을 바라봐야 에디터가 예측 가능해집니다.