Big Project · AI's EYE 19

2.5D Scene 구성 규칙: CCTV 투영의 한계를 넘어 읽을 수 있는 장면 만들기

영상의 x·y 좌표를 비율로 바꿔 평면에 찍으면 자연스러운 Twin이 될 것 같다. 왜 사람 크기와 좌석 고정, 앞뒤 겹침 규칙이 별도로 필요한지 궁금하다.

근거 · 프로젝트 문서 · 코드 · 테스트

1장: 단순 비율 조정의 한계: 왜 2.5D Scene이 필요한가?

솔라는 모니터에 떠 있는 한 장의 다이어그램을 보고 있었다. 왼쪽에는 여러 사람이 오가는 매장 CCTV 영상이, 오른쪽에는 그 영상 속 사람들을 단순화된 아이콘으로 옮겨놓은 2.5D 평면도가 있었다. 두 그림 사이에는 ‘좌표 변환’이라는 글자와 함께 단순한 화살표가 그려져 있었다.

“언니, 이 프로젝트 보고서 봤어? CCTV 영상 분석해서 디지털 트윈 만드는 거.”

솔라가 옆자리의 루나에게 말을 걸었다.

“응, 보고 있었어.”

“그런데 생각보다 간단한 거 아닐까? 영상 속 사람의 x, y 좌표를 가져와서, 저쪽 2.5D 평면에 같은 비율로 옮겨서 찍기만 하면 그럴싸한 장면이 만들어질 것 같은데. 굳이 ‘원근, 좌석, 겹침’ 같은 복잡한 규칙을 따로 만들어야 할까?”

솔라의 말에는 순수한 호기심이 담겨 있었다. 얼핏 보기엔 영상 속 위치를 그대로 옮기면 될 것 같았다. 카메라에 가까우면 화면 아래쪽에 크게, 멀면 화면 위쪽에 작게 잡히는 것이 당연하니, 그 좌표를 그대로 활용하면 자연스럽게 원근감도 살지 않을까 하는 생각이었다.

루나는 솔라의 질문에 고개를 끄덕이며 자신의 화면을 조작했다. 잠시 후, 루나의 모니터가 둘로 나뉘었다. 왼쪽에는 솔라가 봤던 것과 같은 CCTV 영상이 재생되고 있었고, 오른쪽에는 텅 빈 회색의 평면만 덩그러니 놓여 있었다.

“좋은 질문이야, 솔라. 네 가설, 한번 직접 확인해 볼까?”

루나가 키보드를 몇 번 두드리자, 오른쪽 평면 위에 작은 점들이 나타나기 시작했다. 왼쪽 영상 속 사람들이 움직일 때마다, 오른쪽 평면의 점들도 실시간으로 따라 움직였다.

“지금 뭐 하는 거야?”

“네 말대로. 영상 속 모든 사람의 경계 상자(bounding box) 중심 좌표를 그대로 가져와서, 저쪽 평면에 같은 비율로 찍어보는 거야. 다른 규칙은 전부 빼고.”

솔라는 흥미롭게 화면을 지켜봤다. 하지만 잠시 후, 솔라의 미간이 살짝 찌푸려졌다. 화면에 나타난 결과는 솔라의 예상과 전혀 달랐다.

오른쪽 평면 위의 점들은 마치 밤하늘의 별자리처럼 보였다. 실제 매장 바닥에 서 있는 사람들의 배치가 아니었다. 카메라에서 멀리 떨어져 영상 위쪽에 작게 잡힌 사람은 2.5D 씬에서도 위쪽 벽에 붙어 유령처럼 떠다녔다. 반면 카메라 바로 앞에 서서 화면 아래쪽을 가득 채운 사람은 씬의 맨 아래쪽에 거대한 아이콘으로 부자연스럽게 붙박여 있었다.

사람들이 겹쳐 지나갈 때 문제는 더 심각해졌다. 뒤에 있어야 할 사람이 앞에 있는 사람을 가리거나, 두 사람이 비현실적으로 합쳐졌다 사라지기를 반복했다. 바닥이라는 기준면 자체가 존재하지 않는 듯했다. 마치 종이 인형을 아무렇게나 허공에 뿌려놓은 것 같았다. 누가 앞에 있고 누가 뒤에 있는지, 공간감이 전혀 느껴지지 않았다.

“이건… 전혀 아닌데.”

솔라가 나지막이 중얼거렸다. 머릿속의 간단한 아이디어가 눈앞에서 산산조각 나는 순간이었다.

“그냥 2D 좌표를 옮겨 심는다고 되는 게 아니었네. 이건 ‘장면’이 아니라 그냥 ‘점들의 나열’이야. 현실감이 전혀 없어.”

루나는 그제야 키보드에서 손을 떼고 솔라를 바라봤다.

“바로 그거야. 우리가 ‘현실감 있는 장면’이라고 부르는 것을 만들기 위해선, 단순한 좌표 이동 이상의 ‘규칙’이 필요해. 2D 카메라가 찍은 평면적인 이미지에 담긴 3D 공간 정보를 잃어버리지 않고, 우리가 이해할 수 있는 2.5D 씬으로 ‘번역’하는 규칙 말이지.”

루나는 ‘시각적 오류’라는 단어가 적힌 메모장을 솔라 쪽으로 밀어주었다. 방금 솔라가 목격한 모든 어색함이 바로 그 오류였다.

솔라는 고개를 끄덕였다. 이제 왜 별도의 규칙들이 필요한지, 몸으로 이해할 수 있었다. 단순한 비율 조정이라는 ‘쉬운 답’이 왜 틀렸는지 명확해졌다. 하지만 새로운 질문이 곧바로 고개를 들었다.

“알겠어. 규칙이 필요하다는 건. 그럼 어디서부터 시작해야 해? 아까 보니까 사람들이 가만히 서 있는데도 캐릭터가 위아래로 미세하게 흔들리는 것처럼 보였어. 위치를 잡는 기준점부터 잘못된 걸까?”

2장: 발 좌표: 흔들리는 바닥 접점을 고정하는 법

솔라의 질문이 끝나기 무섭게, 루나는 다시 한번 모니터 화면을 바꿨다. 이전의 실험에서 봤던, 허공에 흩뿌려진 듯한 점들은 사라졌다. 대신 CCTV 영상 원본이 화면을 채웠고, 그 위로 영상 속 사람들마다 녹색의 사각형들이 겹쳐 보였다. 인공지능이 사람을 인식해 표시하는 경계 상자, 즉 ‘바운딩 박스(bounding box)’였다.

루나는 아무 말 없이 영상 하나를 전체 화면으로 띄웠다. 한 남자가 매장 선반 앞에서 가만히 서서 물건을 고르는 장면이었다. 하지만 솔라는 곧 무언가 이상한 점을 발견했다. 남자는 분명 제자리에 서 있는데, 그를 감싼 녹색 바운딩 박스는 매 프레임마다 미세하게 크기와 위치가 변했다. 마치 불안하게 숨을 쉬는 것처럼. 루나는 바운딩 박스의 정중앙에 빨간 점을 추가로 표시했다. 박스가 떨릴 때마다, 그 중심점 역시 위아래로, 좌우로 미세하게 흔들렸다.

“아까 내가 말한 게 저거야.”

솔라가 화면을 가리키며 말했다.

“사람은 가만히 있는데, 저 캐릭터는 공중에서 부양하는 것처럼 떨리잖아. 저 빨간 점이 우리가 어제 봤던 그 ‘점들의 나열’의 정체인 거지? 사람의 위치를 저 바운딩 박스의 중심으로 잡은 거고.”

“맞아. 대부분의 객체 탐지 모델에서 가장 기본적으로 제공하는 위치 정보니까. 가장 직관적인 기준점이기도 하고.”

루나의 대답에 솔라는 고개를 갸웃했다.

“직관적이긴 한데… 틀린 것 같아. 저렇게 위치가 흔들리면 디지털 트윈으로서 의미가 없잖아. 모든 사람이 스케이트를 타는 것처럼 보일 텐데.”

솔라의 지적은 정확했다. 바운딩 박스는 모델의 매 프레임 예측에 따라 조금씩 달라진다. 사람이 팔을 뻗거나 자세를 바꾸면 박스의 모양이 크게 변하고, 그에 따라 중심점의 위치도 휙휙 바뀐다. 사람의 ‘위치’를 나타내는 안정적인 기준점으로는 부적합했다.

“그럼 기준을 바꿔야겠네. 저 박스에서 절대 변하지 않는 지점을 찾아야 해. 사람이 서 있든, 앉아있든, 팔을 뻗든… 바닥에 닿아있는 지점은 그대로잖아?”

솔라의 눈이 빛났다. 스스로 문제의 핵심에 다가서고 있었다.

“바로 발이지! 사람의 발이 땅에 닿는 지점을 기준으로 삼으면, 자세가 어떻게 바뀌든 캐릭터가 땅에 붙박여 있을 거 아냐.”

솔라의 가설을 확인시켜주려는 듯, 루나가 키보드를 몇 번 더 두드렸다. 화면에 새로운 점이 나타났다. 이번에는 바운딩 박스의 맨 아래쪽, 그 중앙에 찍힌 파란 점이었다. 앞서 본 빨간 중심점과 달리, 파란 점은 남자가 몸을 조금 뒤척여도 거의 움직이지 않고 바닥 한 지점에 안정적으로 붙어 있었다. 마치 압정으로 고정해 놓은 것처럼.

루나는 다른 영상들을 연달아 보여주었다. 몸을 숙여 물건을 집는 사람, 키가 큰 사람과 작은 사람이 나란히 서 있는 장면. 모든 영상에서 바운딩 박스의 중심을 나타내는 빨간 점은 자세와 키에 따라 위아래로 크게 요동쳤다. 하지만 발 위치를 나타내는 파란 점은 시종일관 바닥에 단단히 고정되어 있었다.

“이거였네. ‘어디에 있는가’를 알려주는 진짜 정보는 박스 전체가 아니라, 박스 밑면에 숨어 있었어.”

솔라는 이제 바운딩 박스의 중심점을 위치의 기준으로 삼는 것이 왜 시각적 오류를 낳는지 명확히 이해했다. 그것은 사람의 ‘무게중심’에 가까울 수는 있어도, 공간 속 ‘좌표’를 보장해주지는 못했다. 2.5D 씬에서 캐릭터를 안정적으로 세우기 위한 첫 번째 규칙은 바로 바닥과의 접점, 즉 ‘발 좌표’를 사용하는 것이었다.

솔라는 만족스럽게 고개를 끄덕였다. 흔들림의 문제가 해결되자, 비로소 씬을 제대로 평가할 수 있게 되었다. 하지만 새로운 규칙으로 안정화된 캐릭터들을 찬찬히 뜯어보던 솔라의 얼굴에 다시 물음표가 떠올랐다.

“그런데 언니, 저기 봐. 맨 앞에 있는 사람하고 저 뒤쪽에 있는 사람이 이제 흔들리지는 않는데… 크기가 거의 똑같아 보여. 이러면 원근감이 전혀 안 살잖아.”

3장: 원근 곡선과 bbox 높이: 실제 같은 크기감을 만드는 방법

루나는 솔라의 이전 질문에 대답하는 대신, 조용히 2.5D 씬 뷰어의 렌더링 설정을 바꿨다. 이전 챕터에서 확인했던, 발 좌표를 기준으로 안정적으로 바닥에 서 있던 캐릭터들의 모습이 화면에서 일제히 변했다. 이제 씬에 등장하는 모든 캐릭터가 키와 위치에 상관없이 완전히 동일한 크기로 표시되었다. 마치 종이 인형극의 배우들처럼.

이 변화는 솔라가 마지막으로 지적했던 문제, 즉 원근감의 부재를 극단적으로 부각시켰다. 매장 안쪽 깊숙이 서 있는 사람의 아바타와 카메라 바로 앞을 지나가는 사람의 아바타가 똑같은 크기로 그려지자, 공간은 다시 한번 납작하게 찌그러져 보였다. 캐릭터들은 더 이상 공중에서 떨지는 않았지만, 대신 비현실적인 거인과 소인이 뒤섞인 혼란스러운 장면을 연출했다.

“이거 봐. 이제 흔들리지는 않지만, 원근감이 없으니까 누가 앞에 있고 누가 뒤에 있는지 전혀 모르겠어.”

솔라가 화면을 보며 말했다. 그녀는 잠시 생각에 잠기더니, 해결책이 떠올랐다는 듯 손가락을 튕겼다.

“간단한 거 아닐까? 우리한테는 영상에서 가져온 바운딩 박스 정보가 있잖아. 거기에 박스 높이값도 포함되어 있고. 카메라에 가까운 사람은 박스가 크게, 먼 사람은 작게 잡힐 테니, 그 높이값을 캐릭터 크기에 그대로 반영하면 되잖아. 그럼 자연스럽게 원근감이 살지 않을까?”

그것은 지극히 합리적인 추론이었다. 영상에 이미 담겨있는 크기 정보를 활용하자는 생각. 솔라의 초기 가설, ‘좌표를 그대로 옮기면 된다’와 비슷한 맥락의 ‘쉬운 답’이었다.

루나는 솔라의 제안에 고개를 끄덕이며, 다시 한번 뷰어의 설정을 변경했다. 이번에는 ‘바운딩 박스 높이 직접 반영’이라는 옵션을 켰다. 그러자 화면 속 캐릭터들의 크기가 즉시 바뀌었다. 확실히 이전보다는 나아 보였다. 카메라에서 멀리 있는 캐릭터는 작게, 가까이 있는 캐릭터는 크게 표시되었다.

“봐, 훨씬 낫네. 이제 좀… 어?”

솔라의 만족감은 길지 않았다. 잠시 영상을 재생하자, 새로운 시각적 오류가 모습을 드러냈다. 매장 안쪽에서 물건을 고르던 한 남자가 팔을 들어 올리는 순간, 그의 캐릭터가 갑자기 부풀어 오르듯 커졌다. 멀리 있는 다른 사람이 몸을 숙이자, 그의 캐릭터는 순식간에 난쟁이처럼 작아졌다. 바운딩 박스의 높이는 사람의 실제 키가 아니라, 그 순간의 자세에 따라 너무나 쉽게 변했다. 위치를 안정시키기 위해 박스 중심점을 버렸던 것처럼, 크기를 안정시키기 위해서는 박스의 높이값을 그대로 쓸 수 없었다.

“안 되겠네. 이것도 흔들리는 건 마찬가지구나. 위치만 흔들리는 게 아니라 크기도 흔들리네.”

솔라가 중얼거렸다. 영상 속 바운딩 박스 높이는 원근 정보와 자세 정보가 뒤섞인, 믿을 수 없는 데이터였다.

루나는 그때까지 준비해 둔 또 다른 창을 화면에 띄웠다. 그것은 하나의 곡선 그래프였다. 가로축은 ‘화면 세로(Y) 좌표’, 세로축은 ‘크기 배율(Scale Factor)’이라고 적혀 있었다. 그래프의 곡선은 화면 아래쪽(Y좌표가 큰 값)에서 높은 배율을 가리키다가, 화면 위쪽(Y좌표가 작은 값)으로 갈수록 점차 낮은 배율을 향해 완만하게 휘어졌다.

“이게 이 프로젝트의 두 번째 규칙, ‘원근 곡선’이야.”

루나가 그래프를 가리키며 설명했다.

“우리는 프레임마다 흔들리는 바운딩 박스 높이를 직접 쓰지 않아. 대신, 우리가 이미 확보한 안정적인 기준점, 즉 ‘발 좌표’를 다시 사용하는 거야. 발 좌표의 Y값(화면상 높이)은 그 사람이 얼마나 멀리 있는지를 가장 안정적으로 알려주는 정보거든. 이 곡선은 ‘발이 이쯤에 있으면, 캐릭터 크기를 이만큼 키워라’라고 알려주는 규칙인 셈이지.”

솔라는 그래프와 2.5D 씬을 번갈아 보며 고개를 끄덕였다. 발 좌표가 위치뿐만 아니라 크기를 결정하는 핵심 열쇠이기도 했다.

“그럼 캐릭터의 기본 키는 어떻게 정해? 모두 똑같은 크기에서 시작해서 이 곡선대로 키우는 거야?”

“비슷해. 모든 사람의 바운딩 박스 높이를 쓰되, 실시간 값이 아니라 여러 프레임에 걸쳐 정규화(normalize)하고 평균을 내서 안정적인 ‘기본 키’를 구해. 그리고 그 기본 키에다, 발 좌표에 따른 이 원근 곡선의 배율을 곱해주는 거지. 그러면 자세 변화 때문에 크기가 널뛰는 일 없이, 원근감만 자연스럽게 표현할 수 있어.”

루나가 최종 설정이 적용된 씬을 보여주었다. 이제 캐릭터들은 바닥에 단단히 발을 딛고 서서, 각자의 위치에 맞는 자연스러운 크기를 유지했다. 팔을 들거나 몸을 숙여도 캐릭터의 크기가 갑자기 변하는 일은 없었다. 거인과 소인이 뒤섞여 있던 비현실적인 공간은 사라지고, 깊이감이 느껴지는 안정적인 2.5D 장면이 눈앞에 펼쳐졌다. 단순한 박스 높이 적용이라는 ‘그럴듯한 답’이 왜 틀렸는지, 그리고 ‘발 위치 원근 곡선’과 ‘정규화된 높이’의 조합이 왜 필요한지 명확해지는 순간이었다.

솔라는 훨씬 더 현실에 가까워진 디지털 트윈을 만족스럽게 바라보았다. 위치와 크기의 문제가 해결되자, 비로소 다른 디테일들이 눈에 들어오기 시작했다. 그녀의 시선이 테이블에 앉아있는 한 캐릭터에 머물렀다.

“언니, 그런데 저기 앉아있는 사람은… 왜 의자 위에서 미세하게 떨리는 것처럼 보이지? 바닥에 서 있는 사람들은 괜찮은데.”

4장: Seat Anchor: 좌석에 앉은 Twin을 고정하는 마법

솔라의 말이 끝나자, 루나는 2.5D 뷰어 창 옆에 있던 필터 옵션을 켰다. ‘상태: 서 있음’ 항목을 체크 해제하자, 화면에서 분주하게 움직이거나 서 있던 캐릭터들이 일제히 사라졌다. 이제 화면에는 테이블 주변 의자에 앉아 있는 캐릭터들만 덩그러니 남았다.

이 변화는 문제를 더욱 선명하게 만들었다. 이전까지는 다른 움직이는 캐릭터들에 섞여 잘 보이지 않았던 현상이 적나라하게 드러났다. 테이블에 앉아 가만히 있는 사람들의 캐릭터들이, 마치 편치 않은 의자에 앉은 듯 계속해서 미세하게 위아래로, 또 좌우로 떨고 있었다. 바닥에 단단히 발을 딛고 서 있던 다른 캐릭터들과는 확연히 다른 모습이었다.

“어? 서 있는 사람들은 괜찮은데, 왜 앉아있는 사람들만 이러지? 발 좌표 규칙이 제대로 적용 안 되는 건가?”

솔라가 물었다. 발 좌표라는 단단한 기준점을 찾아 위치 문제를 해결했다고 생각했는데, 그 규칙이 통하지 않는 예외가 눈앞에 나타난 것이다. 그녀의 생각은 합리적인 방향으로 흘러갔다.

“혹시 앉아있을 때는 ‘발’이 바닥에 제대로 안 닿으니까, 시스템이 엉뚱한 곳을 발 좌표로 인식하는 거 아닐까? 아니면, 이럴 땐 차라리 바운딩 박스 중심점을 쓰는 게 나을 수도 있고.”

솔라는 자신이 막 폐기했던 아이디어를 다시 꺼내 들었다. ‘상황에 따라’ 다른 규칙을 적용해야 할지도 모른다는 생각이었다. 서 있는 사람에게는 발 좌표가 정답이지만, 앉아있는 사람에게는 다른 기준이 필요할 수 있다는 것.

루나는 대답 대신 2.5D 씬을 잠시 끄고, 해당 인물이 나오는 원본 CCTV 영상을 화면에 띄웠다. 그리고 그 사람을 감싸는 바운딩 박스와, 박스 하단 중앙에 찍힌 ‘발 좌표’를 함께 표시했다. 의자에 앉아있는 남자는 동료와 대화하며 몸을 살짝씩 앞뒤로 기울이거나 자세를 고쳐 앉고 있었다.

솔라는 화면을 유심히 들여다보았다. 그리고 곧 자신의 두 번째 가설도 틀렸다는 것을 깨달았다. 문제는 발 좌표를 잘못 찾은 데 있지 않았다. 남자가 상체를 조금만 움직여도, 그를 탐지한 바운딩 박스가 미세하게, 하지만 쉴 새 없이 크기와 형태를 바꾸고 있었다. 그 결과, 솔라가 믿었던 ‘발 좌표’ 기준점조차 함께 흔들렸다. 박스의 중심점은 말할 것도 없이 더 큰 폭으로 요동쳤다.

“아… 서 있을 때랑은 다르구나. 앉아 있으니까, 몸을 조금만 움직여도 박스 전체가 들썩이는구나. 발이 땅에 고정된 게 아니니까.”

솔라가 나지막이 말했다. 서 있는 사람은 땅이라는 절대적인 기준면에 발을 고정하고 있지만, 앉아있는 사람은 의자 위에서 몸 전체가 미세하게 움직이는 하나의 덩어리였다. 바운딩 박스에서 파생된 어떤 좌표도 이 흔들림으로부터 자유로울 수 없었다. 사람의 좌표만으로는 앉은 위치를 고정할 수 없다는 명백한 증거였다.

루나가 그제야 입을 열었다.

“그래서 이런 경우엔, 사람의 좌표를 그대로 믿지 않아. 대신, 이 씬에서 절대로 움직이지 않는 것을 기준으로 삼는 거야.”

루나는 2.5D 씬으로 돌아와 ‘Seat Anchor’라는 이름의 옵션을 활성화했다. 그러자 마법 같은 일이 일어났다. 마치 자석처럼, 떨리던 캐릭터가 의자의 특정 지점에 ‘착’ 하고 달라붙었다. 남자가 영상 속에서 몸을 뒤척여도, 2.5D 씬 속 캐릭터는 더 이상 떨리지 않고 안정적으로 의자에 고정되어 있었다.

“이게… 앵커(anchor), 닻이라는 거구나.”

솔라는 ‘Seat Anchor’라는 단어를 곱씹었다.

“맞아. 우리는 사전에 씬의 배경 요소를 분석해서, 테이블이나 의자처럼 고정된 사물의 위치를 알고 있어. 그리고 특정 사람이 ‘앉아있다’는 상태가 감지되면, 그 사람의 불안정한 bbox 좌표를 쓰는 대신, 가장 가까운 의자의 약속된 지점, 즉 ‘시트 앵커’에 강제로 위치를 고정시키는 거야.”

솔라는 고개를 끄덕였다. 드디어 세 번째 규칙의 의미를 이해했다. 흔들리는 배(사람)를 붙잡아두기 위해 항구(의자)에 내린 닻과 같은 역할이었다.

“그러니까… 서 있는 사람은 자기 발이 기준점이 되지만, 앉아있는 사람은 자기가 앉은 ‘의자’가 기준점이 되는 거구나. 캐릭터의 상태에 따라 기준점을 바꾸는 거네.”

이제 씬은 완벽에 가까워 보였다. 모든 캐릭터는 각자의 위치에서, 흔들림 없이, 올바른 원근감으로 존재했다. 솔라는 만족스러운 눈으로 전체 씬을 다시 한번 훑어보았다. 모든 시각적 오류가 잡힌 것처럼 보였다. 바로 그때, 그녀의 시선이 한곳에 꽂혔다.

방금 전까지 안정적으로 의자에 앉아 있던 캐릭터가, 그 앞을 지나가는 다른 캐릭터에게 잠시 가려졌다가 다시 나타나는 순간이었다. 그런데… 무언가 이상했다.

“언니, 잠깐만. 저 사람… 방금 테이블을 뚫고 나온 것 같지 않았어?”

5장: Z-order: 객체 겹침의 혼란을 정리하는 순서

루나는 솔라가 발견한 그 기묘한 순간을 포착하기 위해 영상을 뒤로 돌렸다. 앉아있던 캐릭터의 앞으로 다른 캐릭터가 지나가던 바로 그 프레임에서 화면은 정지했다. 모든 것이 안정적으로 보였던 2.5D 씬에 나타난 유일한 균열이었다. 루나는 아무 말 없이 화면에 몇 가지 정보를 추가로 겹쳐 띄웠다.

하나는 지나가는 캐릭터(W), 앉아있는 캐릭터(P), 그리고 그들이 앉은 테이블(T)의 바닥 접점 Y 좌표값이었다. 화면 맨 아래에 가까운 W의 발 좌표가 가장 큰 숫자였고, 테이블 다리의 Y 좌표가 중간, 의자 밑에 가려진 P의 발 좌표가 가장 작은 숫자였다. 또 하나는 렌더링 순서를 나타내는 작은 숫자였다. 현재는 W, T, P 순으로 그려지고 있었다. 즉, W가 맨 앞에, P가 맨 뒤에 있었다. 하지만 솔라의 눈에는 P가 T를 뚫고 나온 것처럼 보였다.

“이상하네. 순서상으로는 테이블이 캐릭터를 가리는 게 맞는데… 왜 그렇게 보였을까?”

솔라는 고개를 갸웃거리다, Y 좌표값들을 보고 한 가지 가설을 세웠다.

“혹시 Y 좌표 때문 아닐까? 결국 화면 아래쪽에 있을수록 우리 눈에 가까운 거니까. 그냥 Y 좌표가 큰 순서대로 객체를 그리면 모든 겹침 문제가 해결될 것 같은데. 가장 간단하고 확실한 규칙이잖아.”

그것은 이전의 ‘발 좌표’나 ‘원근 곡선’ 규칙처럼, 화면에 보이는 정보를 가장 직접적으로 활용하자는 직관적인 아이디어였다. 화면 Y 좌표가 곧 깊이 순서를 의미한다는 믿음. 루나는 솔라의 가설을 검증해볼 수 있도록 뷰어의 렌더링 설정을 변경했다. ‘겹침 순서 결정 방식: Y 좌표 우선’.

설정을 바꾸자마자 화면에 즉각적인 변화가 일어났다. 솔라의 예상과는 다른, 더 큰 시각적 오류가 발생했다. 테이블 앞에 제대로 앉아있던 캐릭터 P가 갑자기 테이블을 가리며 앞으로 튀어나왔다. 마치 테이블 위에 올라선 것처럼 부자연스러웠다.

“어? 왜 저래?”

솔라는 당황했다. 그녀의 단순한 규칙이 오히려 상황을 악화시킨 것이다. 루나는 이번에도 말없이, P와 T의 바닥 접점 Y 좌표값을 가리켰다. 앉은 자세 때문에 P의 발 좌표가 T의 기준점보다 화면상 아래쪽에 위치했고, Y 좌표값이 더 컸다. ‘Y 좌표가 큰 객체를 앞에 그린다’는 규칙은 P를 T보다 앞에 그려버리는 결과를 낳았다.

“아… Y 좌표만으로는 안 되는구나. 저 둘은 그냥 나열된 게 아니라, ‘테이블에 앉아있다’는 관계가 있는 건데. 그 관계를 무시하니까 순서가 뒤엉키는구나.”

솔라는 자신의 실수를 인정했다. 화면상의 위치 정보만으로는 객체들이 맺고 있는 공간적, 논리적 관계를 파악할 수 없었다. 뒤에 있는 사람이 앞에 있는 사람을 가리는 일반적인 상황은 Y 좌표로 해결되지만, 테이블과 의자, 그리고 그곳에 앉은 사람처럼 복잡하게 얽힌 경우는 예외였다.

루나는 그제야 프로젝트 문서의 마지막 규칙에 대한 부분을 화면에 띄웠다.

여러 객체가 겹칠 때는 화면 y와 Scene 계층을 이용해 z-order를 계산했다.

“네 말대로, 객체 간의 ‘관계’를 이해하는 추가 규칙이 필요해. 그게 바로 ‘Scene 계층’이야.”

루나는 2.5D 씬 에디터의 사이드바를 열어 객체 목록을 보여주었다. 그곳에는 ‘Table_Group_01’이라는 항목 아래, ‘Table_Top’, ‘Chair_01’, ‘Chair_02’, 그리고 방금 문제가 되었던 캐릭터 ‘Person_P’가 들여쓰기 된 채로 묶여 있었다. 마치 폴더와 파일처럼.

“시스템은 이들이 한 그룹이라는 걸 미리 알고 있어. 그래서 Y 좌표로 기본적인 앞뒤 순서를 정하되, 같은 그룹에 속한 객체끼리는 이 계층 구조에 따라 렌더링 순서(z-order)를 다시 한번 보정해. ‘테이블 그룹에 속한 사람은 테이블보다 앞에 그리지 말라’는 특별 지시가 내려지는 거지. Y 좌표라는 일반 규칙보다 이 계층 규칙이 더 강한 힘을 가져.”

솔라는 비로소 모든 조각이 맞춰지는 것을 느꼈다. Y 좌표는 도로 위의 차들에게 적용되는 일반적인 통행 규칙과 같았다. 하지만 테이블과 의자 같은 특수한 관계는 신호등이 있는 복잡한 교차로와 같아서, 별도의 신호 체계(Scene 계층)가 필요한 것이었다.

모든 시각적 오류를 해결하는 규칙들을 이해하고 나자, 솔라는 처음 이 프로젝트를 봤을 때의 생각이 떠올랐다. ‘좌표를 비율로 바꿔 평면에 찍으면 자연스러운 Twin이 될 것 같다’던 순진한 믿음. 이제는 그것이 얼마나 많은 현실의 복잡성을 무시한 생각이었는지 알 수 있었다.

솔라는 자신의 프로젝트 보고서 초안 파일을 열었다. 제목 아래에는 이렇게 한 줄만 쓰여 있었다. CCTV 영상의 객체 좌표를 2.5D 평면에 비율적으로 투영하여 디지털 트윈을 생성함. 그녀는 망설임 없이 그 문장을 지웠다. 그리고 새로운 소제목을 타이핑하기 시작했다.

<CCTV 투영의 한계를 넘어, 읽을 수 있는 Scene을 만드는 4가지 규칙>

그녀는 자신이 직접 부딪치고 깨달았던 네 가지 시각적 오류와 그 해결책을 하나씩 정리해 나갔다.

  1. 위치 안정화: 상하로 흔들리는 문제를 해결하기 위해, 바운딩 박스 중심이 아닌 발 좌표를 위치의 기준으로 삼는다.
  2. 원근감 표현: 비현실적인 크기 문제를 해결하기 위해, 발 좌표에 기반한 원근 곡선과 정규화된 높이값을 사용한다.
  3. 상호작용 안정화: 앉은 객체의 떨림을 잡기 위해, 사람의 좌표 대신 고정된 시트 앵커에 위치를 연결한다.
  4. 겹침 순서 보정: 객체 간 겹침 오류를 막기 위해, 단순 Y 좌표가 아닌 Scene 계층을 결합한 Z-order를 계산한다.

보고서를 작성하는 솔라의 손가락은 자신감에 차 있었다. 이제 2.5D 씬은 그녀에게 단순한 점들의 나열이 아니었다. 그것은 현실 세계의 공간적 논리를 디지털 환경의 언어로 한 겹 한 겹 섬세하게 ‘번역’해낸, 지적인 결과물이었다.