이 핸드북의 핵심 명제 — "POP은 GPU 그래픽스 파이프라인의 데이터 모델을 노드 그래프로 노출시킨 학습 환경" — 가 이 챕터에서는 "도메인 변환은 공짜가 아니다"로 구체화된다. 4장에서 본 "texture = buffer" 등식은 메모리 차원의 진술이었다. 그러나 같은 메모리를 다른 도메인으로 "보여주는" 행위에는 비용이 따른다. 특히 POP을 화면 픽셀로 만드는 일 — 우리가 "렌더링"이라 부르는 그것 — 은 정점 변환과 래스터화라는 명시적 계산을 수반한다. Render TOP은 마술이 아니라 compile된 draw call의 묶음이다.
이 챕터에서 정복할 CG 개념
- POP attribute(GPU 메모리 위의 1D 배열)가 화면 이미지(2D 픽셀 그리드)로 변환되는 두 가지 경로의 구분.
- "Geometry → Rasterization → Per-pixel work"이라는 raster pipeline의 운영 비용 모델.
- POP attribute를 CHOP 채널로 끌어내는 일이 사실은 GPU→CPU readback 또는 GPU 내 채널화임을 인지.
- Render TOP과 Render Simple TOP의 위치 — 노드는 다르지만 내부 추상화는 같다.
- "Trail" 같은 시간 축 누적은 POP 측에서도 TOP 측에서도 가능하며, 둘은 동일한 ping-pong 패턴의 서로 다른 면이다.
왜 이게 중요한가
학습자는 종종 노드 그래프의 시각적 균질함에 속는다. TOP을 POP에 꽂는 것과 POP을 TOP에 꽂는 것이 한 줄로 그려지면, 두 작업의 비용이 같다고 착각하기 쉽다. 그러나 4장의 TOP→POP은 메모리 reinterpret에 가까운 작업이고, 이 챕터의 POP→TOP은 다르다. POP의 정점이 픽셀로 가려면 vertex shader가 모든 점에 대해 MVP 변환을 수행하고, primitive assembly가 삼각형을 만들고, rasterizer가 픽셀 단위로 fragment를 만들고, fragment shader가 각 fragment의 색을 계산해야 한다. 이것이 raster pipeline 그 자체다. Render TOP은 그 파이프라인을 한 번 cook할 때마다 풀어내는 wrapper다. 같은 사실을 LearnOpenGL의 "Hello Triangle"이 가장 짧은 코드로 보여주고, RTR4 §2가 가장 정식으로 정리한다.
POP에서의 노출 지점
POP을 다른 도메인으로 보내는 경로는 wiki에서 다음과 같이 확인된다.
- Geometry COMP → Render TOP: 정식 렌더링 경로. POP은 직접 화면으로 가지 않는다. POP은 Geometry COMP 안에 위치해야 하고, Geometry COMP가 카메라/라이트와 함께 Render TOP의 입력으로 들어간다. POP 인벤토리 §"Notes" 4번이 이 사실을 명시한다: "No POPs sit between POP and the renderer at the operator level. Rendering is handled by putting the POP inside a Geometry COMP and feeding the COMP to a Render TOP."
- Render Simple TOP: POP 인벤토리에는 "Render TOP only via the POP overview reference"라고 표시되어 있다. 즉 인벤토리 표 자체에는 등재되지 않은 viewer용 단축 노드다. POP 작업 중에 가장 자주 보이지만, Render TOP의 기능 부분집합 — 카메라/라이트/MAT을 자동 구성한 wrapper — 으로 다루는 것이 안전하다. wiki 미확정 — 실험 필요 표시: Render Simple TOP의 정확한 자동 구성 규칙(어떤 기본 카메라, 어떤 기본 라이트, 어떤 기본 MAT)은 inventory 자료에서 verbatim 확인되지 않는다.
- POP attribute → TOP의 픽셀로 직접 dump: Render TOP을 거치지 않는 별도 경로의 존재 여부는 POP 인벤토리에 등재된 노드 표만으로는 확정되지 않는다. 인벤토리의 Bridge POPs 표에는
TOP to POP은 있지만 그 반대 방향("POP to TOP" 같은 노드)은 등장하지 않는다. 즉 POP attribute를 TOP의 픽셀로 직접 쓰는 전용 Bridge POP 노드는 wiki 인벤토리상 확인되지 않는다 — wiki 미확정, 실험 필요. 실무적 대안은 GLSL Advanced POP의 "Extra Outputs" 기능을 통해 또 다른 POP의 attribute를 쓰는 것, 혹은 Render TOP의 "Render Pass / G-buffer" 모드를 활용해 attribute에 해당하는 채널을 픽셀로 받는 것이다. 두 번째 경로의 정확한 구성은 wiki 직접 확인이 필요하다. - POP → CHOP: POP의 attribute를 CHOP 채널로 끌어오는 노드 또한 인벤토리 Bridge POPs 표에 명시적으로 등장하지 않는다.
CHOP to POP(채널 → 어트리뷰트)은 존재하나, 그 역방향 노드는 wiki 미확정 — 실험 필요. 대안으로Analyze POP이 input 전체에 대한 통계를 한 점에 응축해 출력하므로, 그 한 점을 다시 CHOP으로 빼는 표준 통로(Object CHOP / Geometry CHOP 류)가 사용될 수 있다. 정확한 통로의 명칭은 실험으로 확정해야 한다.
여기서 확인 가능한 노드만 정직하게 나열하면:
- Geometry COMP (POP을 담는 컨테이너)
- Render TOP (실제 rasterization)
- Render Simple TOP (viewer용 단축 노드 — POP overview 페이지에서 언급됨)
- Analyze POP (input의 attribute 통계를 한 점으로 응축; 후속 CHOP 변환의 시작점)
- Trail POP (입력의 최근 N프레임 포인트를 누적; 시간 축 자료 보존)
- CHOP to POP (역방향)
이론
POP의 attribute가 화면 픽셀이 되는 경로를 두 가지로 나눠 본다.
경로 (a): POP → Geometry COMP → Render TOP
이것은 raster pipeline의 정식 행로다. 매 cook마다 다음이 일어난다.
[POP attributes on GPU]
P, N, Cd, Tex, primitive index buffer
|
v
Vertex Shader (per point)
- P를 World/View/Clip space로 변환 (MVP)
- Cd, N, Tex 등을 varying으로 통과
|
v
Primitive Assembly
- index buffer로 triangle/line/point primitive 조립
|
v
Rasterization
- 각 primitive를 픽셀로 분할
- varying을 픽셀별로 interpolate
|
v
Fragment Shader (per pixel)
- 보간된 N, Tex 등으로 색 계산
|
v
Framebuffer (TOP pixels)
비용은 두 곳에서 발생한다. Vertex stage는 POP의 포인트 수에 비례. Fragment stage는 화면에 차지하는 픽셀 수(= 커버리지)에 비례. POP을 100만 포인트로 키운다고 fragment 비용이 100만 배 되지 않는다 — 화면 커버리지가 그대로면 fragment 비용은 그대로다. 반대로 화면을 4K로 키우면 fragment 비용은 4배가 되지만 vertex 비용은 동일하다. 이 비대칭은 raster pipeline의 핵심 특성이다.
경로 (b): POP attribute → TOP 픽셀로 직접 (별도 메커니즘)
이 경로는 rasterization을 건너뛴다. 즉 vertex shader도 fragment shader도 호출되지 않는다. 대신 POP의 N개 element를 TOP의 N개 픽셀에 1:1 매핑한다. attribute가 RGBA channel로 그대로 들어간다. 이것은 "POP의 메모리를 다른 도메인으로 보이게만 한다"는 점에서 4장의 TOP→POP과 대칭이다.
여기서 중요한 것: 이 두 번째 경로의 정확한 wiki-confirmed 노드명은 인벤토리에 등재되어 있지 않다. 인벤토리 §"Notes" 4번이 명시하듯 "rendering" 자체는 Geometry COMP → Render TOP 한 경로만 documented되어 있다. POP-as-data-source를 TOP으로 직접 보내는 별도 노드의 존재 여부는 wiki 미확정 — 실험 필요.
Render TOP은 무엇인가
LearnOpenGL "Hello Triangle"의 마지막 코드를 보면, 매 프레임 호출되는 라인은 본질적으로 두 줄이다 — glUseProgram(...)과 glDrawElements(...). Render TOP의 한 번의 cook은 그 두 줄을 자기 입력에 맞춰 컴파일하고 실행한다. 다음을 포함한다:
- 카메라(Camera COMP) 행렬에서 V와 P 행렬을 추출.
- 라이트(Light COMP)의 위치/색/강도를 uniform으로.
- Geometry COMP에 들어있는 POP의 attribute buffer를 VBO로 바인딩.
- MAT(GLSL MAT 또는 Phong MAT 등)의 vertex/fragment 쌍을 사용하는 draw call 발행.
- 결과를 자신의 출력 framebuffer(TOP의 pixel buffer)에 쓴다.
Render TOP은 마술적인 "POP을 픽셀로"가 아니다. 컴파일된 draw call 묶음이다. RTR4 §2가 이 사실을 정식으로 정리한다.
POP→CHOP의 의미
CHOP는 시간 축 또는 1D 인덱스에 따른 채널이다. POP의 attribute를 CHOP 채널로 보내는 행위는 본질적으로 GPU의 SSBO를 channel buffer로 reinterpret하는 것이다. 그러나 실제 구현은 두 가지로 갈린다.
- Element 수가 작은 경우: POP에서 CPU로 readback해서 CHOP에 채운다. GPU→CPU 동기화 비용이 있다.
- Element 수가 큰 경우: GPU 내에서 채널-형 buffer로 reinterpret할 수 있다면 비용이 없으나, CHOP UI는 보통 CPU 측 표시이므로 readback이 불가피하다.
정확한 통로 노드명과 readback 동기화 정책은 wiki 미확정 — 실험 필요. 인벤토리에서 확인된 후보 시작점은 Analyze POP 한 점 출력 → 후속 CHOP 추출 통로다.
손작업 (Hands-on)
시작 파일 A — 정식 렌더링 경로
- Sphere POP1 (Type: Geodesic, Frequency: 4)
- Normal POP1 (Type: Vertex)
- Geometry COMP1 (POPs 모드 — 안에 위 POP 체인 배치)
- Camera COMP1 (기본값, Translate Z=4)
- Light COMP1 (기본값, Translate X=2, Y=2, Z=4)
- Render TOP1 (Geometry: Geometry COMP1, Camera: Camera COMP1, Lights: Light COMP1)
- 비교용 Render Simple TOP1 (Geometry: Geometry COMP1)
연결: Sphere POP1 → Normal POP1 → (Geometry COMP1 내부). Render TOP1과 Render Simple TOP1은 같은 Geometry COMP를 입력으로 가진다.
1단계: 두 노드의 결과 비교
- 보게 될 것: 두 TOP은 거의 동일한 sphere 이미지를 출력한다. 차이는 Render Simple TOP이 기본 카메라/라이트를 내부에서 자동 구성하는 데 있다. Render TOP에서는 우리가 그 자동 구성을 펼쳐 명시적으로 만들었다.
- 직관과 어긋나는 부분: 두 노드는 "다른 종류의 렌더링"이 아니라 "같은 렌더링의 다른 노출 수준"이다. Render Simple TOP은 빠른 viewer, Render TOP은 production setup.
2단계: POP을 키워 vertex 비용을 본다
- Sphere POP1의 Frequency를 8, 16, 32, 64로 키운다. 포인트 수가 기하급수적으로 증가한다.
- 화면 크기는 그대로 두고 Render TOP의 cook time을 본다 (Performance Monitor).
- 보게 될 것: cook time이 늘어나지만 화면 커버리지가 같다면 fragment 비용은 거의 같다. 증가분은 vertex stage 비용이다.
3단계: 화면 크기를 키워 fragment 비용을 본다
- POP의 Frequency를 적당한 값(예: 16)으로 고정. Render TOP1의 Resolution을 512×512, 1024×1024, 2048×2048로 키운다.
- 보게 될 것: vertex 비용은 그대로, fragment 비용이 화면 면적에 비례해 증가. 같은 sphere를 더 비싸게 그리고 있는 것이다.
시작 파일 B — Trail POP으로 시간 축 누적
- Sphere POP1 (위와 동일)
- Transform POP1 (Translate Y를
sin(absTime.seconds) * 1.0같은 식으로 묶음) - Trail POP1 (Trail Length: 60 frames — wiki: "Captures the input's points for the most recent N frames/slices and optionally connects them as primitives.")
- Geometry COMP1 → Render TOP1
1단계: 시간이 누적된다
- 보게 될 것: 매 프레임의 sphere가 trail 형태로 쌓인다. 60 프레임 = 1초간 sphere가 어디 있었는지가 한 POP 안에 들어 있다.
- 본질적으로 이것은 ping-pong이 아니라 ring buffer다. 매 프레임 새 frame이 들어오고 가장 오래된 frame이 밀려난다. 11장의 Feedback POP과 비교하면 차이가 분명해진다 — Feedback POP은 1프레임 history, Trail POP은 N프레임 history.
노드 ↔︎ GLSL 매핑
이 챕터의 주제는 "데이터 이동"이라 GLSL 비중은 작다. 한 가지 짧은 예 — Analyze POP의 sum-style reduction을 GLSL POP 한 번의 dispatch로 흉내내는 idea code — 만 보인다. atomic add 패턴이며, Read-Write 출력 모드가 필수다.
노드 구성
- Point Generator POP1 (input: Sphere POP1, Number: 10000) — 점 만 개를 만든다.
- GLSL POP1 (Attribute Class: Point, Output Attributes: P, Output Access: readwrite, Number of Threads: Auto)
- Create Attributes 페이지:
attr0name = SumOut, type =float, components = 1 - (또는 Temp Buffers 페이지로 atomic 누적용 buffer를 잡는 게 더 정석. wiki에 따르면 Read-Write 출력 SSBO에 atomic 연산이 허용된다.)
- Create Attributes 페이지:
- 결과 확인은 후속 노드에서.
Compute Shader (idea code)
// GLSL POP, Attribute Class = Point, Output Attributes = P (and SumOut as new attribute)
// Output Access = readwrite (atomic을 쓰려면 필수)
// Pass 1에서 SumOut[0]을 0으로 초기화한다고 가정.
layout(std430, binding = 0) buffer SumBuf {
float gSum[]; // SumOut과 매핑되는 SSBO. wiki는 'attribType AttribName[]' 형식을 명시한다.
};
void main() {
const uint id = TDIndex();
if (id >= TDNumElements()) return;
vec3 p = TDIn_P();
// sphere 위 한 점의 z 좌표를 모든 점에 대해 누적.
// atomicAdd는 float이 아닌 uint/int에만 정의되므로,
// 정밀도 손실을 감수하고 fixed-point로 변환하거나
// float atomic 확장을 활성화해야 한다 (벤더/드라이버 의존).
// 여기서는 개념 시연 — 실제 코드는 uint atomic + scale로 작성.
uint scaled = uint(clamp(p.z * 1024.0 + 1024.0, 0.0, 4095.0));
atomicAdd(gSumU[0], scaled); // gSumU는 uint 버전 SumOut.
// P는 그대로 통과시킨다.
P[id] = p;
}
이 코드가 GPU에서 실제로 무엇을 하고 있는가: 만 개의 thread가 동시에 같은 uint 메모리 슬롯에 add를 시도한다. atomic 연산이 그 add를 직렬화한다(드라이버/하드웨어가 lock-free로 직렬 효과를 보장한다). 결과적으로 한 번의 dispatch로 만 개 점의 z 좌표 합이 한 슬롯에 모인다. Analyze POP의 sum 모드가 내부에서 하는 일과 같은 카테고리의 작업이다. 현실적 주의: float atomic은 GLSL 표준이 아니라 확장이므로(NV_shader_atomic_float 등), 표준 GLSL POP에서 동작 보장이 안 된다. uint atomic + scale이 안전한 패턴이며, 정밀도가 중요하면 prefix sum(GPU Gems 3 Ch.39) 같은 reduction 알고리즘으로 다단 작성한다.
wiki 미확정 — 실험 필요: GLSL POP에서 출력 SSBO의 정확한 binding qualifier와 layout이 사용자에게 노출되는 방식(자동 주입 vs 직접 선언)은 wiki에 verbatim 확정 형태로 등장하지 않는다. 위 코드는 일반 compute shader 패턴을 따른 idea code다. 실제 코드는 wiki의 attribType AttribName[] 패턴(verbatim)으로 시작해 atomic 연산을 추가하는 형태가 될 것이다.
확인 질문 (Self-check)
- 같은 sphere를 Render TOP 두 개에 동시에 꽂아 다른 Resolution(512×512와 2048×2048)으로 받으면, vertex 비용과 fragment 비용은 각각 어떻게 변하는가? 왜 비대칭이 발생하는가?
- Render Simple TOP의 내부 자동 구성에서 라이트가 없는 상태로 sphere를 그리면 어떤 결과가 나오는가? 그 결과는 Render TOP에서 라이트를 0개로 두고 그린 결과와 같은가?
- Trail POP은 N프레임을 누적한다. 같은 결과를 Feedback POP과 Cache POP의 조합으로 구현하려면 어떻게 해야 하는가? 세 노드의 책임 분담은 무엇이 다른가?
- POP attribute를 CHOP 채널로 끌어내는 정식 노드명을 wiki에서 찾을 때 어떤 검색어가 유효한가? 인벤토리의 어떤 카테고리를 우선 살피겠는가?
- atomic add 기반의 reduction과 prefix-sum 기반의 reduction은 같은 결과를 만든다. 어느 쪽이 throughput에 유리한가? 어느 쪽이 numerical 정확도에 유리한가?
연결 고리
- LearnOpenGL — "Hello Triangle" (https://learnopengl.com/Getting-started/Hello-Triangle). Render TOP 내부의 minimal 흐름.
glDrawElements한 줄이 Render TOP cook 한 번에 해당한다. - Real-Time Rendering 4ed §2 "The Graphics Rendering Pipeline". 정점에서 픽셀까지의 정식 파이프라인 구조.
- Real-Time Rendering 4ed §23 "Graphics Hardware". Render TOP cook 한 번 동안 GPU가 실제로 무엇을 하는지의 하드웨어 측면.
- TouchDesigner Wiki — POP overview (https://docs.derivative.ca/POP). Render TOP과 Geometry COMP의 관계 ("rendering is handled by putting the POP inside a Geometry COMP and feeding the COMP to a Render TOP").
- GPU Gems 3 Ch.39 "Parallel Prefix Sum (Scan) with CUDA" (https://developer.nvidia.com/gpugems/gpugems3). reduction의 정석. Analyze POP이 더 큰 규모에서 어떤 알고리즘으로 구현되어야 하는지의 표준.
이 챕터의 한 줄 명제
Render TOP은 마술이 아니라 컴파일된 draw call 묶음이다. POP을 다른 도메인(픽셀, CHOP 채널, 다른 POP)으로 보내는 행위는 메모리 reinterpret(거의 공짜)일 수도 있고 raster pipeline 전체 실행(정점/픽셀당 계산 비용)일 수도 있으며, 두 가지를 구분하는 것이 GPU 위 데이터 흐름을 읽는 첫 단계다.