OpenGL Shader – 3

Vertex Processor

버텍스 프로세서는 버텍스 쉐이더를 실행한다. 버텍스 쉐이더의 입력은 버텍스 데이터로써, 주로 버텍스의 위치, 색, 법선벡터 등으로써 OpenGL 어플리케이션이 보내준다.

다음 OpenGL 코드는 버텍스 프로세서에게 각 버텍스에 대한 색상, 위치 값을 전달해준다.

glBegin(...);
    glColor3f(0.2,0.4,0.6);
    glVertex3f(-1.0,1.0,2.0);

    glColor3f(0.2,0.4,0.8);
    glVertex3f(1.0,-1.0,2.0);
glEnd();

버텍스 쉐이더에서 다음과 같은 일을 할 수 있다.

  • 모델뷰 행렬과 프로젝션 행렬을 이용해서 버텍스의 위치를 이동
  • 노말 벡터의 변환, 그리고 필요하다면 노말 벡터의 정규화
  • 텍스쳐 좌표 생성과 변환
  • 버텍스에 대한 빛 또는 픽셀에 대한 빛 계산
  • 색상 계산

위의 모든 연산을 수행할 필요는 없다. 그러나 일단 버텍스 쉐이더를 만들었다면 버텍스 프로세서의 전체 기능을 버텍스 쉐이더가 대신하며, 따라서 법선 변환을 수행할 수 없고 고정 기능이 텍스쳐 좌표 생성을 수행하도록 할 수 없다. 버텍스 쉐이더가 사용될때 파이프 라인의 이 단계의 필요한 모든 기능을 버텍스 쉐이더가 대신하게 된다.

이전에서 설명했던 것처럼, 버텍스 쉐이더는 버텍스간 연결성 고려에 대한 정보를 가지고 있지 않다. 그러므로 위상 데이터를 필요로 하는 연산은 버텍스 쉐이더에서 수행할 수 없다. 예를들어서, 버텍스 쉐이더가 뒷면 제외를 할 수 없는데, 버텍스 쉐이더는 버텍스 단위 연산이지 버텍스가 이루고 있는 면에 대한 연산을 할 수 없기 때문이다. 면을 알려면 버텍스간의 연결 정보를 알아야 한다. 이러한 연결정보가 바로 위상 데이터의 하나이다. 버텍스 프로세서는 개별적인 버텍스 하나 하나를 처리 하는 순간에 그 하나 이외의 나머지 버텍스를 알지 못한다.

버텍스 쉐이더는 최소한 gl_Position 이라는 변수를 변경해야만 한는데, 이 변수는 보통 모델뷰 행렬과 프로젝션 행렬을 이용한 변환이다.

버텍스 프로세서는 OpenGL 상태에 접근할 수 있어서, 예를 들어 빛을 포함하고 재질을 사용하는 연산을 수행할 수 있다. 또한 텍스쳐에 대한 정보도 접근할 수 있는데 이것은 최신 그래픽 카드에서만 허용한다. 하지만 프레임 버퍼에는 접근할 수 없다.

OpenGL Shader – 2

다음 그림은 매우 단순화된 파이프라인 다이어그램이며 데이터가 어떤 식으로 파이프 라인을 타는지를 보여준다. 매우 단순화시켰지만 쉐이더 프로그래밍에 대한 중요한 개념을 제공하고 있다. 이 부분에서는 파이프라인의 고정 기능을 제공한다. 파이프라인은 추상적이며 모든 각 단계에서 어떤 특정한 구현과 만날 필요가 없다는 점을 주의하기 바란다.
Vertex Transformation

여기에서 버텍스는 공간상의 위치, 색상, 노말벡터, 텍스쳐 좌표 등과 같은 속성들의 집합이다. 이 단계에서의 입력값들은 하나 하나의 버텍스 속성들이다. 이 고정 기능에 의해 수행되는 연산은 다음과 같다.

  • 버텍스의 위치 변환
  • 버텍스에 대한 광원 계산
  • 텍스쳐 좌표의 생성 및 변환

Primitive Assembly and Rasterization

이 단계에서의 입력값은 변환된 버텍스와 연결 정보이다. 연결정보라는 것은 버텍스들이 Primitive(삼각형 등과같은 기본 요소)를 이루기 위해 어떻게 연결되느냐이다. 이 단계에서 Primitive가 생성된다. 또한 이 단계는 뒷면제거나 뷰 절두체에 대한 클리핑 연산을 담당할 수 있다.

라스터라이징은 Fragment를 결정하고 Primitive의 픽셀 위치를 결정한다. 이 문맥에서 Fragment는 특정한 위치에서 프레임 버퍼의 픽셀을 변경하기 위해 사용될 데이터의 조각이다. 쉽게 말하자면 Primitive가 화면에 실제 렌더링될때 그려질 Pixel 값이다. Fragment는 색 뿐만이 아니라 수직벡터값과 텍스쳐 좌표 등과 같은 값들인데, 이러한 값들을 이용해서 실제로 화면상에 찍힐 픽셀의 새로운 색상을 계산하는데 쓰인다.

이 단계에서의 출력값은 다음과 같다.

  • 프레임버퍼 안의 Fragment들의 위치
  • 버텍스 변환 단계에서 계산된 속성에 대한 각 Framgment에 대한 보간된 값

버텍스 변환 단계에서 계산된 값은 버텍스 연결정보와 함께 조합되어 Fragment를 위한 알맞은 속성을 계산하는데 사용된다. 예를들어서 각 버텍스는 변환된 위치를 가지고 있다. 버텍스가 Primitive로 만들어지는데 사용될때 Primitive의 Fragment의 위치를 계산하는것이 가능하다. 또 다른 예는 색상의 사용이다. 만약 삼각형을 구성하는 각 버텍스가 각기 다른 색상을 가지고 있다면, 삼각형 안의 Fragment 색상은 각 버텍스의 상대적인 거리에의한 가중치를 받아 색상값들이 보간되어져 얻어진다.

Fragment Texturing and Coloring

이 단계에서의 입력값은 보간되어진 Fragment의 정보이다. 색상은 이미 이전 단계에서 보간을 통해 계산되어졌고, 이 단계에서는 텍셀(Texel, Texture element)값 결합과 같은 연산이 수행된다. 텍스쳐 좌표는 이전단계에서 보건되어진다. 안개도 역시 이 단계에서 적용된다. 고통적인 최종 출력값은 Fragment의 색상값과 깊이 값이다.

Raster Operations

이 단계에서의 입력값은 다음과 같다.

  • 픽셀의 위치
  • Fragment의 색상값과 깊이값

Fragment에 대해 수행되는 파이프라인의 마지막 연속 단계는 주로 다음과 같다.

  • Scissor Test
  • Alpha Test
  • Stencil Test
  • Depth Test

만약 테스트가 성공한다면, Fragment 정보는 현재의 블렌딩 모드에 따라 픽셀의 값을 변경하는데 사용된다.  블렌딩은 이 단계에서만 수행되는데, Fragment 텍스쳐링과 컬링 단계에서는 프레임 버퍼에 접근하지 못하기 때문이다. 프레임 버퍼는 오직 이 단계에서만 접근할 수 있다.

고정 기능에 대한 간단한 그림 설명

다음 그림은 위에서 설명한 각 단계에 대한 설명을 그림으로 다시 풀어높은 것이다.


Replacing Fixed Functionality

현재의 그래픽 카드는 프로그래머가 위에서 기술된 단계 중에 2개를 새롭게 정의할 수 있게 해준다.

  • 버텍스 쉐이더는 Vertex Transformation 단계를 작성하는데 사용된다.
  • 프레그먼트(Fragment) 쉐이더는 프레그먼트 텍스쳐링과 컬러링 단계의 고정 기능을 교체하는데 사용된다.

다음 섹션에서는 프로그래밍 가능한 단계, 즉 버텍스 처리기와 프레그먼트 처리기에 대해 설명할 것이다.

OpenGL Shader – 1

GLSL을 사용하는 쉐이더 프로그래밍을 알아보자. 쉐이더는 최신 기술이며 3D 게임에서 경이로운 효과를 내는데 사용된다. 이 글을 통해 쉐이더의 세계에 발을 들여놓자.

이 글과 앞으로 진행될 내용을 이해하기 위해서는 OpenGL 프로그래밍에 익숙해야 한다. 또한 쉐이더를 위한 스펙에 대해 알고 싶다면, OpenGL 2.0과 GLSL에 대한 공식문서를 반드시 읽어보길 바란다.

GLSL은 OpenGL 쉐이딩 언어를 의미하며 종종 “glslang”으로 불리며 ARB(Architectural Review Board) OpenGL에 의해 정의된다.

OpenGL과 경쟁하는 Cg, 즉 Nvidia가 나름대로 만든 또 다른 쉐이더 언어와 GLSL을 비교하거나 누가 더 우수한지 따지지는 않겠다. 그 이유는 지금 여기서는 Cg가 아닌 GLSL에 대해서만 살펴볼 것이기 때문이다.

어떤 언어로든 쉐이더를 작성하기 전에, 그래픽 파이프라인에 대한 기본을 이해하는 것이 매우 중요하다. 그래픽 파이프라인은 쉐이더가 무엇을 하는지, 쉐이더의 종류에는 뭐가 있는지와 같은 쉐이더의 핵심을 제공한다. 또한 쉐이더로 할 수 없는것이 무엇인지에 대한 힌트도 제공한다.

파이프라인에 대한 소개 다음에 GLSL을 위한 OpenGL의 설정에 대해서 알아보겠다. OpenGL 어플리케이션에서 쉐이더를 사용하데 필요한 단계에 대해 자세하게 살펴볼 것이다. 최종적으로 OpenGL 어플리케이션이 매우 유연하고 강력한 효과를 낼 수 있는 방법에 대해 살펴볼 것이다.

몇가지 기본적인 개념으로 데이터 타입(data types), 변수(variables), 문장(statements), 함수(function)의 정의에 대해서 알아본다.

이 글은 ARB 확장과 OpenGL 2.0 버전에 대해 다룬다. 후자는 이미 표준이지만 전자는 향후 표준으로 자리잡을 가능성이 있는 것이다. 차이는 작지만 함수의 이름이나 상수 이름에 약간의 차이를 가지고 있다. 이것들을 구분을 돕기위해 코드에 색상을 넣었다. ARB는 회색으로 OpenGL 2.0은 오렌지 색으로 말이다.

이 적용범위는 실수하기 쉽다. 그러니 혹시 이런 표기에 문제가 있다면 언제든지 내게 알려주길 바란다.

이 글은 점진적으로 작성될 것이므로 글과 데모 프로그램에서 문제가 있을 수 있는데, 이것에 대해서 좀 참아주길 바란다. 비록 사소할지라도 문제를 발견하면 알려주길 바란다. 또한 많은 제안을 해주길 바란다. 부디 이 글을 즐겁게 읽어주길 희망하며 첫번째 GLSL, OpenGL Shader의 첫글을 마치도록 하겠다.