개요
어디선가 한 번쯤 보셨을 그래픽스 파이프라인에 대한 포스팅입니다. 하지만 조금 더 스토리 및 각 항목마다 유기적으로 작성하려 노력했습니다.
컴퓨터 그래픽스의 목적
컴퓨터 그래픽스의 목적은 요즘에야 당연히 프로젝트마다 미학적이거나 어떤 요인에 의한 목적이 조금 달라질 수는 있겠지만, 컴퓨터 그래픽스의 원초적(?)인 목적은 말 그대로 현실 세계를 정확하게 시뮬레이션하는 것에 있습니다. 이를 위해선 많은 것들이 필요할 겁니다. 실제 혹은 가상 물체의 모델링을 물론, 물리/광학과 인간의 시각과 카메라 시뮬레이션까지 필요합니다. 심지어 속도 또한 매우 빨라야만 하죠. 즉, 이런 엄청난 연산량에 속도까지 챙기기 위해 말 그대로 괴물 하드웨어(monster hardware)를 사용하게 됩니다. 최근에 와서는 이러한 괴물 하드웨어를 계산용으로 사용하는 수준을 넘어, 수백, 수천 개(요즘은 뭐 수 만개..)의 코어를 사용하여 계산하는 대규모 병렬 컴퓨팅(MPC: massively Parallel Computing)을 사용하는 것으로 까지 발전하게 됩니다. 그리고 병렬은 단순히 빠르기만 한 것뿐 아니라, 기가 막힌 안정성까지 제공하니(예를 들어 노드 하나 망가져도 끄떡없으니), 그래픽스 입장에서는 너무나 좋은 조건이 되었죠.
그렇다면 이러한 그래픽적 처리를 효율적으로 하기위해, 이전에 컴퓨터 과학자들은 어떤 방식으로 효율적인 파이프라인을 구성했을까요? 아래 그래픽스 파이프 라인을 보면서 알아봅시다.
그래픽스 파이프라인
3D 그래픽스 파이프라인에서는 매우 많은 데이터를 단계적으로 처리합니다. 단순화하지 않은 그래픽스 처리단계는 다음과 같습니다.
전체 프로세스를 나열하자니 꽤나 복잡합니다. 쉐이더나 쉐이더 그래프를 자주 만지셨다면 각 단계에 나오는 용어에 대해 들어보신 적은 있으실 겁니다. 나름 각 단계 별로 설명했으니 한 번쯤 훑어보셔도 좋을 것 같습니다.
이를 여러분이 이전에 들어 보셨을 법한 단순화한 그래픽스 파이프라인(Simplified Graphics Pipeline)은 다음과 같습니다.
단순화하지 않은 그래픽스 파이프라인에서, Primitive 이후에, Rasterizer ~ Dither 단계를 Fragment Processing 단계로 퉁친 것이 가장 큰 차이점입니다. 또한 Primitive Procesesing, T&L, VBO 등도 Vertex Processing이라는 개념으로 크게 묶여있네요. 그리고 참고로 말씀드리면 사진에도 설명했지만, Blend만 따로 떨어져 있는 것이 의아하실 수 있습니다. 이는 나머지 Fragment Processing에서 처리하는 방식과 Blend의 방식, 역할이 다르기 때문입니다. 짧게 표현하자면, Blend는 Framgment Processing과 다르게 Frame Buffer에 담겨있는 기존에 그려진 프레임과 비교해서 어떤 방식으로 Blending 하여 그릴지 고려합니다. 반면에 Fragment Processing은 이렇게 Frame Buffer와 비교하는 과정을 포함하지 않습니다.
참고) Vertex와 Fragment
기하학 관점의 삼각형을 나타내기 위한 꼭짓점 하나씩을 Vertex라고 합니다. 참고로 그래픽스에서는 4 각형, 5 각형이던 모두 3각형으로 환원되어 표현될 수 있다고 바라보기에, 삼각형을 기본도형(Primitive)으로 사용합니다.
그리고 fragment는 픽셀(Pixel)과 관련된 자료(Associtated Data)를 합쳐놓은 것을 의미합니다. 여기서 말한 관련 자료란 색상, 깊이값을 말합니다.
프로그래머블 그래픽스 파이프라인
기존의 전통 그래픽스 파이프라인에서, 엔진개발자 및 사용자들은, 특정구간에서 작업속도가 매우 느려진다는 것을 발견했습니다. 이 부분이 바로 Vertex Processing과 fragment Processing 부분입니다. 이에 느려지는 이 부분에 대한 연산을 병렬처리방식으로 가속을 할 수 있다는 것이 발견되고, 이 두 프로세스에 병렬처리가 가능한 코어가 집중 저긍로 많이 붙게 됩니다. 여기에 반도체(트랜지스터) 기술의 발달이 한몫했죠. 요구사항에 따라 더욱더 많은 코어를 넣을 수 있었기 때문입니다. 사용자가 Vertex, Fragment렌더링 단계 중에서 일부나 전체를 직접 제어하고 수정할 수 있는 쉐이더가 들어간 프로그래머블 그래픽스 파이프라인이 등장하게 됩니다.
쉐이더
쉐이더가 뭔가요? 혹시 "색이나 형태를 짜주는 알고리즘 같은 건가 보다.." 하고 계셨나요? 쉐이더의 유래와 정의를 한 번 알아봅시다.
쉐이더는 원래 1989년에 픽사에서 만든 RenderMan Software에서 유래하게 됩니다. 회사에서 GPU처리 속도 향상 목적으로 그래픽스 처리 전용의 소형 프로그램을 구동하기 위해서 제작한 것이 시작이었죠. 1995년에는 이 쉐이더 기술을 활용한 토이 스토리가 엄청난 성공을 거두게 되었습니다. 쉐이더를 활용해 괄목할만한 성능 향상을 해낸 것이 큰 각광을 받기 시작합니다.
이러한 그래픽스 연산을 빠르게 하고자 하는 갈망과 하드웨어 발전과도 시기가 잘 맞았습니다. 그래픽스 처리만을 위한 CPU 또한 만들게 되었고, 이는 오늘날 우리가 쓰는 GPU(graphics Processing Unit)의 원신이 되지요.
다시 쉐이더이야기로 돌아와서, 즉 쉐이더란 오리지널 한 의미로 봤을 때는 "특정 목적을 위한 작은 프로그램"을 의미합니다. 즉 그래픽스에서 Shader Program은 GPU에서 돌아가는 small-size 프로그램을 말합니다. 그럼 Shader Language, Shader Compliler라는 개념이 좀 더 와닿지 않으신가요? Shader Language는 쉐이더라는 특정 목적을 위한 작은 프로그램을 제어하기 위한 언어인 것이고, Shader 컴파일러는 이 언어를 컴파일하기 위한 컴파일러겠지요. 다시 반복하면, Shader Langauage는 곧 HLSL, GLSL과 같은 프로그래밍 언어인 것이고, Shader Compiler는 쉐이더 전용 컴파일러를 의미하는 것입니다.
이에 또다시 한번 프로그래머블 그래픽스 파이프라인에 대한 정의로 정리하자면, 예전의 전통적인 그래픽스 파이프라인에서는 모두 Fixed VLSI(Very Large-Scale Integration) 칩으로 구현했다면, 현재는 Vertex processing VLSI와 Fragment Processing VLSI를 Shader로 대체하여 사용자가 직접 조작할 수 있게 뜸한 프로그래머블 그래픽스 파이프라인이 된 것입니다.
쉐이더 프로그래밍
저는 당장은 GLSL에 대해 주로 공부하고 포스팅을 할 생각이지만, 쉐이더 프로그래밍 이야기가 나온 김에, 어떤 쉐이더 프로그래밍 언어가 있는지 간단하게 알아보고 포스팅을 마무리해보려고 합니다.
첫 번째로 OepnGL ARM Assembly Language가 있는데요, 말 그대로 어셈블리.. 언어를 쓰는데, 프로그래머의 선호도가 그다지 높지는 않기에 2000년대 초반에 사용하게 됩니다. 그다음 DIrectX HLSL입니다. C스타일 언어이고, DIrectX8 이후부터 PC 일부와 XBox에서 사용가능합니다. 그리고 똑같이 C스타일 언어로, 엔비디아에서 만든 Cg가 있습니다. HLSL, GLSL 출력을 낼 수 있으나, 엔비디아 그래픽카드가 아니면 사용할 수가 없습니다. 그리고 모바일 시장을 장악하며 사실상 가장 많이 쓰는(제가 주로 포스팅하려는) OpenGL SL(OpenGL Shader Language)가 있죠. 이 또한 C-Style Shader Language이고, 모바일을 포함한 모든 GPU에서 사용할 수 있다는 특징이 있습니다.