1. GL 파이프라인

: 정점 데이터나 화소 데이터 모두 컴파일된 형태로 디스플레이 리스트에 저장될 수 있다. 디스플레이 리스트를 실행하면, 이들 데이터가 즉시 파이프라인으로 보내진다.
만약 스플라인 곡선을 사용한다면 Evaluators가 실행된다. 이는 주어진 제어점으로부터 곡선이나 곡면의 정점 좌표, 법선 벡터 등을 계산하는 프로세서다. 
정점 데이터에 대한 모델 변환, 시점 변환, 투상 변환 등의 기하 변환을 가하는 작업은 정점 연산(Per-Vertex Operation) 프로세서에 의해 이루어 진다. 
만약 조명이 사용되면 시점 좌표계에서 정점의 색이 계산된다. 
변환된 정점 위치로부터 기본 요소인 선분 또는 다각형을 구성하는 작업이 기본 요소 조합(Primitive Assembly) 프로세스다. 이 단계에서 절단(Clipping)이 가해진다. 
이후 뷰 포트 변환이 가해져 완전한 기하 기본 요소(Geometric Primitives)가 생성된다. 기하 기본 요소는 변환되고, 절단된 정점 위치와 해당 정점의 색, 깊이, 텍스처 좌표 등의 정보로 구성된다.
그리고 이 정보가 래스터 변환(Rasterization) 또는 스캔 변환(Scan Conversion) 프로세서에 입력된다. 

위 그림은 이 과정을 더욱 상세히 나타낸 것이다. 아래쪽 정점 좌표로부터 투상 변환까지가 정점 연산 프로세스에서 이루어지며, 절단은 기본 요소 조합 프로세스에서 이루어진다. 사용자가 직접 정의하거나 Evaluators로 계산된 법선 벡터는 모델 뷰 변환이 가해진 후, 시점 좌표계 상태에서 정점의 조명 계산에 사용된다.
주어진 정점 좌표로부터 텍스터 좌표를 계산하는 작업이 텍스처 좌표 생성(Texture Coordinates Generation) 프로세스다. 그림에서 보듯이 이 프로세스는 별도의 파이프라인을 따라 진행하기 때문에 래스터 변환 시 텍스처 매핑이 일어나기 전에 완료되기만 하면 된다. 
화면 화소의 색은 프레임 버퍼(Frame Buffer, Color Buffer)에 값이 기록되는 순간 결정된다.
프레임 버퍼에 기록되기 이전 단계의 화소를 프래그먼트(Fragment, Pixel Fragment, Fragment Square)라 부른다. 이는 화소에 대응하는 추상적인 개념으로, 화소 위치를 비롯하여 해당 화소의 색, 깊이, 텍스처 등 하나의 화소를 채우기 위해 필요한 정보를 지닌 기본 단위다.

래스터 변환은 기하 기본 요소와 화소 기본 요소를 프래그먼트로 변환하는 프로세스다. 선분의 경우 래스터 변환 프로세스로 입력되는 것은 선분 양 끝점의 뷰 포트 좌표다. 이 좌표로부터 선의 굵기, 앤티 엘일리어싱을 감안하여 두 점을 연결하는 선분 내부의 프래그먼트 위치와 색이 결정된다. 물론 프래그먼트 위치는 정수 단위의 화면 좌표계로 표시된다. 여기에 프래그먼트 별로 깊이 정보가 추가되고, 필요한 경우 텍스처 정보가 추가된다. 

화소 데이터는 정점 데이터와 다른 파이프라인을 거친다. 화소 연산(Pixel Operation)에서는 메인 메모리에 압축되어 저장된 화소 데이터를 복원(Unpacking) 한 후 크기 조절, 화소 맵 처리, 클리핑 등의 화소 변환(Pixel Transfer) 작업을 거쳐 텍스처 메모리나 래스터 변환 프로세서로 보내진다. 

2. GL의 버퍼

프레임 버퍼
: 프레임 버퍼는 프런트 버퍼, 백 버퍼, 보조 버퍼다. 현재 화면에 보이는 영상은 프런트 버퍼에 기록되고, 더블 버퍼링을 사용하면 다음 화면에 보일영상은 백 버퍼에 기록된다. 
그래픽 카드에 따라 좌측 버퍼(Left Buffer), 우측 버퍼(Right Buffer)를 사용할 수도 있다. 이는 입체 영상을 위한 스테레오 버퍼로, 지원 여부는 glGetBooleanv(GL_STEREO) 호출로 알 수 있다. 
또한 그래픽 카드에 따라 보조 버퍼가 지원되기도 한다. 이 버퍼는 사용자 임의로 사용할 수 이ㅈㅆ지만 화면 디스플레이에 직접 사용할 수는 없다. 따라서 사용자는 임시 저장 장소로 사용하되 화면에 디스플레이 하려면 프런트 버퍼로 옮겨야만 한다. 
glGetIntegerv(GL_AUX_BUsFFERS) 호출로 그래픽 카드가 몇개의 보조 버퍼가 지원되는지 알 수 있다. 

void glutInitDisplayMode(int Modes)
: 버퍼를 활성화 하는 함수다. 

void glClear(GLbitfield mask);
: 초기화 값으로 버퍼를 초기화하는 함수다. 파라미터로 버퍼의 종류를 지정한다. 이 함수는 실행 속도가 매우 느리기 때문에 가급적 프레임당 한 번만 가하는 것이 바람직하다. 
glClearColor(). glClearIndex(), glClearDepth(), glClearStencil(), glClearAccum() 으로 각각 버퍼의 초기화 값을 정의할 수 있다.

void glDrawBuffer(GLenum mode);
void glReadBuffer(GLenum mode);
: 버퍼에 어떤 내용을 쓰거나 읽으려면 위 함수를 호출해야 한다. 
glDrawBuffer() 함수의 기본 모드는 GL_FRONT_LEFT다.  
glReadBuffer() 함수는 현재의 화면 영상을 지정된 버퍼로 읽어 들여 텍스처로 활용하는데 사용한다.

void glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha):
void glDepthMask(GLboolean flag);
void glStencilMask(GLUint mask);
: 호출 결과 필요한 버퍼에 자동으로 쓰기 작업을 가하는 함수들도 있다. 프로그래머가 임의의 쓰기 작업을 배제하려면 위 함수들로 해당 버퍼를 마스킹 해줘야 한다.
예를 들어, glColorMask(false, true, true, true) 함수 호출로 프레임 버퍼의 (R, G, B, A) 중 R 성분은 변하지 않도록 마스크가 씌워진다. 
마스킹은 glClear() 함수를 비롯해 버퍼 내용을 변경하는 모든 명령어에 영향을 준다. 

3. 프래그먼트 연산
: 래스터 변환이 완료된 모든 정보는 프래그먼트 단위로 표시된다. 프래그먼트 단위로 표시된 이후에는 파이프라인의 모든 작업이 프래그먼트 단위로 병렬 처리(Parallel Processing)되어 연산속도가 최대화 된다. 
래스터 변환 결과 프래그먼트는 텍스터 매핑 프로세서, Color Sum 이라는 것을 거치고 안개 효과(Fog Effect) 프로세스가 가해지면 비로소 프래그먼트 테스트가 시작된다. 

프래그먼트가 화소로 변하여 프레임 버퍼에 저장되기 전까지 일련의 프래그먼트 연산이 가해진다.
연산의 전반부는 테스트 작업으로, 주어진 테스트를 가할 것인지 아닌지는 glEnable(), glDisable() 함수에 의해 적용된다. 
어떤 프래그먼트가 테스트를 통과하지 못하고 제외되면 해당 프래그먼트는 이후 테스트로 입력되지 않는다. 모든 테스트를 통과한 프래그먼트만 블렌딩, 디더링, 논리 연산이 실행되고, 최종결과가 프레임 버퍼에 기록된다. 

아래 그림은 프래그먼트 테스트에 관련되 버퍼를 나타낸 것이다.

1) 시저 테스트(Scissor Test)
: GL에서 기본적으로 하나의 장면은 뷰 포트 기준으로 절단되는데, 뷰 포트 안에 시저 박스(Scissor Box)를 추가적으로 설정해서 추가 절단을 하겠다는 것이다.

void glEnable(GL_SCISSOR_TEST);
void GLScissor(GLint x, GLint y, GLsizei width, GLsizei height);
void glDisable(GL_SCISSOR_TEST);

glScissor안에 좌표 x, y로 좌하단을 설정하고 width, height로 크기를 설정해서 시저 박스를 만든다. 

2) 알파 테스트
: 알파값을 기준으로 해당 프래그먼트를 제외시키는 것이다. 

위 그림에서 c, d는 불투명한 프래그먼트 이고 a, b는 일부 투명한 프래그먼트 라고 가정하자.
(a) 순서대로 처리하면 아무 문제가 없다. 불투명한 것을 먼저 그리고 그 위에 투명한 것을 그리니 적절하게 조합이 된다. 
(b) 순서대로 처리하면 먼저 a가 처음 기록되면 지-버퍼에 a의 깊이가 저장된다. 이후 지-버퍼 알고리즘을 적용하면 b, c, d는 더 먼곳에 있으므로 지-버퍼는 물론 프레임 버퍼에 전혀 기록되지 못한다. 
이 경우 투명성 알고리즘(Transparency Algorithm) 다음과 같이 진행한다. 첫째, 지-버퍼를 활성화한 상태에서 완전히 불투명한 것(알파가 1인것)을 먼저 그린다. 그런 다음, 깊이 버퍼를 비활성화하고 알파가 1보다 작은 것을 그린다. 이렇게 되면 적절하게 반영된다. 

3) 스텐실 테스트(Stencil Test)
: 시저 박스가 드로잉을 사각형 내부로 국한하는 것에 반해 스텐실 테스트는 좀 더 자유롭다. 

void glStencilFunc(GLenum func, GLUint ref, GLUint mask);
void glStencilOp(GLenum fail, GLenum zfail, GLenum zpass);

위 두함수를 사용하여 스텐실 테스트를 진행한다. 

참고 링크 
- http://blog.naver.com/PostView.nhn?blogId=kzh8055&logNo=140043022533&redirect=Dlog&widgetTypeCall=true

4) 깊이 버퍼 테스트(Depth Buffer Test)
: 지-버퍼 알고리즘이 가해지는 부분을 말한다. 
화소당 16비트를 할애한 것이 깊이 버퍼라면, 깊이는 2^16 레벨로 표현된다. 즉, 가시 부피의 전면과 후면 사이의 길이가 2^16가지 레벨로 양자화(Quantization)되는 것이다. 

void glDepthFunc(GLenum func);  

위 함수를 사용한다. 파라미터를 GL_LESS로 입력해야 z값이 기존의 깊이 버퍼에 저장된 z값보다 작을때 테스트를 통과하게 된다. 

5) 블렌딩(Blending)
1- 블렌딩 함수
: 입력되는 프레그먼트가 앞에서 설명한 테스트를 모두 통과하면 다양한 방법으로 기존에 프레임 버퍼에 쓰인 내용과 결합할 수 있다. 
새로운 프래그먼트가 투명한 경우와 프레임 버퍼에 앤티에일리어싱을 가하려는 경우에는 프래그먼트와 프레임 버퍼 내용을 혼합(Blending)해야 한다. 

위는 혼합 과정을 예시한 것이다. 입력 프래그먼트를 source라 하고, 현재 프레임 버퍼에 저장되어 있는 화소를 Destination이라 할 때 Source Fragment와 Destination Pixel 각각이 혼합 인수(Blending factor)를 지닌다. 
여기서 혼합 인수는 해당 색의 가중치, 즉 반영 비율이다. 혼합 함수는 혼합 인수를 기준으로 2가지 색을 결합하여 다시 Destination Pixel에 저장한다. 

소스 프래그먼트의 색 (R, G, B, A) 와 프래그먼트의 혼합인수(S(r), S(g), S(b), S(a)) 의 값을 element 곱한것과 대상 화소의 색 (R, G, B, A) 와 대상화소의 혼합인수 (D(r), D(g), D(b), D(a)) 의 값을 element 곱한것을 더하면 최종 (R, G, B, A)가 나온다. 

void glBlendFunc(GLenum sfactor, GLenum dfactor);

혼합을 위해서는 위 함수를 호출해야 한다. sfactor는 소스 프래그먼트의 파라미터를, dfactor는 대상화소의 파라미터를 나타 낸다. 

위 표는 파라미터에 따른 실제 혼합 인수 값을 나타낸다. 

전면 영상(Foreground Image)과 배경 영상(Background Image)을 합성해야 할 때가 있다. 위 그림을 합성해야 하는 경우다. 위 영상 A의 청색 부분을 투명하게 처리해야만 그림과 같은 결과가 나온다. 영상 합성(Image Composition)에 있어서 투명하게 해야 할 부분을 선택하는 작업을 키잉(Keying) 또는 블루 스크리닝(Blue Screening)이라 부른다.
특히 그림처럼 일정한 색을 선택할 경우를 크로마 키잉(Chroma Keying) 이라 한다. 밝기의 임계치를 선택할 경우를 루마 키잉(Luma Keying) 일정한 모양을 선택하여 해당 부분을 투명하게 처리할 경우를 배팅(Matte, Matting) 이라 한다. 



위 그림에서 전면 영상 A의 청색 프래그먼트의 알파 값을 모두 0으로 놓을 수 있다. 이후, 혼합시에 소스 프래그먼트의 혼합 인수를 GL_SRC_ALPHA로 설정하면 전면 영상 중 청색 프래그먼트의 혼합 인수는 (0, 0, 0, 0)이 되므로, 혼합에 반영되지 않아 투명하게 처리된다.

2- 깊이 큐와 안개 효과
: 안개뿐만 아니라 아지랑이, 연기, 공해, 대기 등 공기 상태로 인한 현상을 통틀어 안개 효과(Fog Effect)라 한다. 거리에 따라 물체를 흐리게 만드는 것이다. 즉, 거리가 멀수록 배경색이 더욱 강하게 드러나게 하는 것인데, 이는 물체색과 배경색의 혼합 비율을 조절한 것이다. 
안개 효과를 적용하기 위한 GL 함수는 다음과 같다.

void glFog{if}[v] (GLenum pname, TYPE param);

3- 멀티패스 랜더링(Multipass rendering)  
: 여러 개의 그림을 혼합에 의해 조합하는 것을 말한다. 동일한 모양의 물체에 대해 파라미터를 달리하면서 그려내되, 혼합에 의해 이를 하나로 조합하여 그려진다. 

위 그림의 원기둥 가운데 새겨진 글자는 범프 매핑(Bump Mapping)에 의한 것이다.

6) 디더링(dithering)
: 프레임 버퍼의 용량, 즉 비트 평면 수가 적다면 디더링을 사용하여 다양한 색을 표현할 수 있다. 
하드웨어별로 서로 다른 디더링 방식을 사용하기 때문에 지엘이 할 수 있는 것은 단지 디더링 모드를 활성화 하거나 비활성화 하는 일뿐이다. 디더링을 활성화해도 아무 일도 일어나지 않는 경우가 있는데, 이때는 이미 하드웨어가 충분히 고 해상도 컬러라는 얘기이다.

7) 논리 연산(Logical Operation)
: 프래그먼트에 가해지는 최종 작업이다. 이 연산도 블렌딩과 마찬가지로 입력 프래그먼트와 프레임 버퍼에 대한 연산이다. 즉, 소스 프래그먼트와 대상 화소에 논리 연산이 가해지고, 결과가 대상 화소로 저장된다. 



Posted by 홍성곤
,