1. 화소 연산

기하 기본 요소가 처리되는 파이프라인과는 별도로 화소 파이프라인이 존재한다.
메인 메모리로부터 읽혀진 영상의 화소는 GL의 처리 형식에 부합하도록 변환된다. 이후 이 화소는 래스터 변환, 프래그먼트 연산 등을 거쳐 프레임 버퍼로 입력된다. 역으로 프레임 버퍼에 있는 화소를 화소 파이프라인을 거쳐 메인 메모리로 보낼 수도 있다.
위 그림은 이러한 과정을 나타낸 것이다. 그림에서 화소 파이프라인이 행하는 연산을 화소 연산(Pixel Operation) 이라 한다.

메인 메모리에 화소 데이터를 저장하는데는 다양한 형식이 사용된다. 윈도우의 경우 압축된 형식을 사용한다. 따라서 이러한 메인 메모리 저장 형식을 프레임 버퍼 저장 형식으로 바꾸려면 타입 변환이 필요하다.

1) 화소 저장 모드(Pixel Storage Mode)
: 영상을 효율적으로 저장하고 검색하기 위해 데이터 경계선을 몇 바이트로 할 것인지(Memory Alignment), 몇 행을 띄고 읽을 것인지(Row Skip), 주소를 어떻게 지정할 것인지(Addressing Mode)등을 말한다. 
이는 다음 함수로 제어할 수 잇다.

void glPixelStore{if} (GLenum pname, TYPEparam);

2) 화소 변환 연산(Pixel Transfer Operations)
: 색 값을 어떻게 변경할 것인지에 관한 것이다.
일반적으로 GL의 컬러는 [0.0, 1.0] 범위의 값으로 표현된다. 다른 그래픽 시스템에서 읽은 컬러 값이 다른 범위에 있다면 이를 이 범위로 사상(Color Map)시킬 필요가 있다. 
이는 다음 함수로 제어할 수 있다.

void glPixelTransfer{if} (GLenum pname, TYPEparam);

화소 기본 요소는 화소별 색 정보로 이루어진 기본 요소다. 이는 비트맵과 화소맵(Pixmap, Image)라는 2가지 타입으로 나뉜다. 다시말해 GL은 비트맵과 화소맵을 구분하여 별도로 정의한다. 


위 그림은 화소 파이프라인에서의 읽기와 쓰기 명령별로 거치는 파이프라인을 나타낸다.

1- 비트맵
: 화소당 1비트만 할애한 것으로, 어떤 화소를 변경할 것인지를 결정하는 일종의 마스크에 해당한다.
비트맵이나 화소맵을 프레임 버퍼에 쓰기 위해서는 래스터 위치(Raster Position)을 설정해야 한다. 여기서 래스터 위치는 화면 좌표계 기준의 좌표를 말한다. 
현 위치를 검색하려면 glGetFloatv(GL_CURRENT_RASTER_POSITION)을 호출하면 된다.

void glRasterPos{234}{sifd}v (TYPE x, TYPE y, TYPE z, TYPE w);

위 함수로 래스터 현재 위치를 설정한다. 

voidglBitmap(GLsizei width, GLsizei height, GLint xo, GLint yo, GLint xi, GLint yi, GLubyte *bitmap);

위 함수로 비트맵을 그린다.

2- 화소맵
: 화소당 여러 비트를 할애한 것으로서 실제 영상의 색에 해당한다. 화면의 사각형 블록의 폭을 Width, 높이를 Height라 할 때 이 블록은 pixels[height][width][3] 이라는 배열로 선언할 수 있다.
GL은  이 블록을 대상으로 읽기, 쓰기, 복사라는 3가지 작업을 허용한다. 

void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels);

읽기는 프레임 버퍼의 내용을 메인 메모리에 쓰는 작업이다. 위 함수에 의해 호출된다. 

void glDrawPixels(GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels);

쓰기는 메인 메모리에 있는 내용을 프레임 버퍼에 기록하는 작업으로, 위 함수에 의해 호출된다. 

void glCopyPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type);

복사는 프레임 버퍼 내부의 한 블록을 다른 블록으로 복사하는 작업으로, 위 함수에 의해 호출된다. 

* GL의 영상 처리 기능은 이 밖에도 특정 색이 화면에 나타나는 빈도(Frequency)를 추적하는 히스토그램(Histogram) 기능, 영상 필터로서의 컨벌루션(Convolution) 기능, 영상 확대(Pixel Zoom) 기능, 행렬을 사용한 컬러 값의 선형 변환(Color Matrix) 기능 등 다양한 기능을 포함한다. 


2. A-버퍼
:  A-버퍼의 일반적 정의는 프레임 버퍼, 깊이 버퍼, 알파 채널, 앤티 에일리어싱 등의 기능을 통합한 하드웨어를 A-버퍼라 부른다. 
: GL의 A-버퍼는 일반 정의와는 의미가 약간 다르다. 이는 단일 버퍼로, 복수 개의 영상을 누적하여 저장하는 버퍼를 의미 한다. 
아래 함수로 A-버퍼를 제어한다.

void glAccum(GLenum op, GLfloat value);

밑에 표는 파라미터 op에 대한 설명이다.

glAccum(GL_RETURN, 1.0) 함수를 호출하면 A-버퍼의 내용이 프레임 버퍼로 보내진다.

1) 화면 앤티 에일리어싱

위 그림처럼 시점 위치가 약간 바뀌면 영상의 위치도 약간 이동된다. 이는 영상의 떨림과도 유사한데, 이를 이용해서 화면 앤티 에일리어싱(Full Screen Anti-aliasing)을 구현할 수 있다.
임의로 하나의 영상을 약간씩 변위시켜서 여러 영상을 만들어 낸후 평균을 내어 그리면 에일리어싱이 약화된다. 

2) 필드 깊이 조절
: GL 프로그램에 의해 성성된 영상은 완전히 초점이 잡힌 영상이다. 그러나 일반 카메라에 의한 영상은 초점 거리로부터 물체가 멀어질수록 흐려보인다. 이러한 효과를 구현하기 위해서 초점 평면(Focal Plane)과 함께 필드 깊이(Depth of Field)에 따른 변화를 주어야 한다.

3) 모션 블러

위 그림과 같은 효과도 A-버퍼를 통해 영상을 중첩시키면서 구현 가능하다.

4) 소프트 섀도(Soft Shadow)
: A-버퍼는 여러 개의 광원에 의한 소프트 섀도 효과를 내는 데 이용할 수 있다. 한번에 하나의 광원만 활성화하여 그림을 그려낸 후, 이들 각각을 누적하여 그려내면 소프트 섀도가 드려진다.


 

Posted by 홍성곤
,

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 홍성곤
,

1. 앤티 에일리어싱
: 평면 사각형 텍셀을 곡면으로 사상한 결과는 더 이상 평면 사각형이 아니다. 곡면으로 바뀔 뿐만 아니라 면적도 확대되거나 축소된다. 고로, 앤티 에일리어싱이 나타날 수 있다.

정사각형에 원근 투상을 가한결과 위 그림 (b)처럼 될 수도 있다. (b)의 각 화소 중앙점이 텍스처에서 (a)의 위치에 해당하면 점 샘플링 결과는 (c)처럼 나타난다. 

- 축소 관계와 확장 관계

확장 관계(Magnification)는 위 그림에서 텍셀보다 작은 크기의 텍스처가 한 화소로 매핑되는 것이고, 축소 관계(Minification)는 여러 텍셀에 걸친 텍스처가 한 화소로 매핑되는 것이다.

두가지 모두 에일리어싱이 발생할 수 있다. 확장 관계에서는 주로 양방향 선형보간을 사용해서 앤티 에일리어싱 한다.

양방향 선현보간은 위 그림처럼 텍셀 정 중앙에 있는 점을 대표 점으로 간주하고 어떤 화소의 중앙점이 (b)의 p로 매핑되었다고 하면 양반향 선형 보간을 이용해 색의 값을 결정하는것을 말한다. 
양방향 선형필터는 대부분 그래픽 카드의 드라이버가 표준으로 채택하는 방법이다. 

그러나 양방향 선형 필터는 단점이 있다. 실제 텍스처는 (a)에서 보듯이 텍셀 경계선을 기준으로 색 값이 급변한다. 그런데 양방향 선형 필터를 사용하면 보간으로 인해 경계션이 흐려지는 현상, 즉 블러링(Blurring)이 발생한다. 

축소 관계에서는 텍스처 사각형 내부의 텍셀을 가중 평균하여 해당 화소의 색을 결정할 수 있다. 이를 계산할 때 각 텍셀의 색값의 평균을 구해서 해당 화소의 값을 결정한다. 이에 대한 계산을 미리수행해 저장함으로써 처리 속도를 높인 것이 밉 매핑(MIP Mapping, Multum in Parvo, Many Things in s Small Place)이다. 

이 방법은 R, G, B별로 텍스처를 미리 저장하되, 해상도별로 평균치를 계산하여 하나의 텍스처 맵에 저장한다. 이 방법은 미리 텍스처를 저장한다는 의미에서 사전 필터링(Pre-Filtering) 이라고도 부르며, 해상도별로 여러 텍스처를 저장한다는 의미에서 다해상도 텍스처(Multi-Resolution Texture) 라고도 한다.

위 그림은 밉맵의 구성 방법을 나타낸다. 지도의 가운데를 원점으로 볼때, 오른쪽 위를 Green, 왼쪽 위를 Red, 오른쪽 아래를 Blue로 분리하여 텍스처를 그린다. 지도의 왼쪽 아래는 다시 그 자체를 4개의 상한으로 나누어 R, G, B 값을 담고 있다.
이처럼 밉맵을 저장해 높으면 바르게 앤티 에일리어싱을 할 수 있다. 경우에 따라서는 해상도에 해당하는 밉맵이 저장되어 있지 않을 수도 있다. 그럴 경우 다른 해상도의 밉맵을 보간하여 새로운 밉맵을 계산하여 사용한다.
이 경우는 새로운 밉맵을 만들기 위해 이미 1번 보간을 했고 이를 다시 양방향 선형 보간을 이용해서 픽셀 평균값을 계산해야 되기 때문에 3방향 선형 보간(Tri-Linear interpolation)이라 한다. 

2. GL의 텍스터 매핑
: GL의 텍스처를 가하려면 5가지의 작업이 필요하다. 이들 각가에 대해 알아보자>

1) 텍스처 기능 활성화(Enable Texturing)
: GL의 텍스처 기능을 활성화 하려면 glEnable(GL_TEXTURE_2D) 함수를 호출하고, 비활성화 하려면 glDisable(GL_TEXTURE_2D)를 호출한다. 

void glEnable(GLenum mode);
void glDisable(GLenum mode);


GL은 1, 2, 3 차원 텍스처를 모두 지원한다. 1차원 텍스처는 선으로 등고선을 그려내는 데 사용할 수 있으며, 3차원 텍스처는 이후 설명할 부피 가시화(Volume Rendering)에 주로 사용된다. 

2) 텍스처 영상 명시(Specify Texture Images)
: 텍스처 영상은 배열 형태로 메인 메모리에 로드해야 한다.

Glubyte MyTexture[width][height][3];

이처럼 배열로 표현된 영상을 텍스처 영상으로 사용하기 위해서는 다음 함수를 호출해야 한다.

void glTexImage2d(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *texture);

 여기서 파라미터 target은 GL_TEXTURE_1D, GL_TEXTURE_2D 등의 텍스처 타입을 의미한다. 
파라미터 level은 텍스처의 밉맵 레벨로, 사용하지 않을 경우에는 0으로 설정한다.
파라미터 width, height는 텍셀 단위로 텍스처 폭과 높이를 나타낸다. 
파라미터 border는 윤곽선 두께를 몇 텍셀로 할 것인지를 의미한다. 이 윤곽선은 주어진 텍스처를 둘러싸는 선이다. 
파라미터 internalformat은 텍셀을 기술하는 데 사용된 요소를 의미하는 것으로, 아래 표와 같다.

위에 나열한 내부 형식을 포함하여 GL이 지원하는 텍셀 형식은 모두 38개다.
예를들어 GL_R3_G3_B2는 Red, Green에 3, Blue에 2 비트를 할애한 텍셀을 말한다. 

파라미터 format, type은 텍스처 영상의 데이터 포맷, 타입을 나타낸다.

특수한 경우를 제외하고는 format은 internalformat과 동일한 것을 사용하면 된다. 
함수 호출 예는 다음과 같다. 

glTexImage(GL_TEXTURE_2D, 0, GL_RGB, 512, 512, 0, GL_RGB, GL_UNSiGNED_BYTE, MyTexture); 

위 그림은  래스터 변환을 전후한 GL 파이프라인을 나타낸 것이다.
프로세서 메모리에 있는 영상은 필요한 용량을 줄이기 위해 압축되어 저장된다. 따라서 이 영상에 압축풀기가 가해지고, 이어서 필요한 화소 연산이 가해진다. 이처럼 변환된 영상은 래스터 변환 프로세서로 직접 입력되기도 하지만 텍스처 메모리에 저장되었다가 래스터 변환 프로세서에 입력되기도 한다. 

glTextImage2D() 함수는 위 그림 청색 화살표로 표시된다. 프로세서 메모리에 저장되어 있던 텍스처를 텍스처 메모리(Texture Memory, Texture Buffer)로 이동시키는 함수다. 텍스처 메모리 역시 프레임 버퍼처럼 별도로 할당된 메모리 공간으로, GL_READ_BUFFER 라는 상수로 표시된다. 

현재의 텍스처 모두 또는 일부를 변경하려면 glTexSubImage2D() 함수를 호출하는 것이 유리하다. 이 함수에 의해 기존에 할당된 텍스처 메모리 내용을 변경할 수 있고, 이렇게 함으로써 glTexImage2D()가 새로운 텍스처 메모리를 할당하는 데 소요되는 시간을 줄일 수 있다. 
이 함수는 주로 텍스처 무비를 만드는 데 사용된다. 이는 물체는 그대로 있고, 물체 표면의 텍스처만 계속 바꿈으로써 애니메이션 효과를 주기 위한 것이다. 

GL의 파이프라인에서는 위 그림의 적색 화살표를 따라 프레임 버퍼 내용을 텍스처 메모리로 전달할 수도 있다. 즉, 현재 화면에 보이는 그림을 텍스처로 사용할 수도 있다. 
이를 위해 다음 함수를 사용한다.

void glCopyTexImage2D(GLenum targe, GLint level, GLint format, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);

이 함수에서 x, y, width, height를 제외한 나머지 파라미터는 glTexImage2D() 함수의 정의와 동일하다. 만약 일부의 화소 사각형(Pixel Rectangle)만을 텍스처로 사용하려면 (x, y)로 사각형 좌하단의 좌표를 잡고 width, height로 크기를 잡으면 된다, 반드시 2^n 형태로 표시되어야 한다. 

*2차원 그림 파일을 텍스처로 사용하기 위해서는 해당 파일을 배열 형태로 표시해야 한다. 그러나 대부분의 그림 파일이 다양한 형태로 표시되고, 또 압축된 형태이기 때문에 해당 파일로부터 화소 값만 직접 읽어들이기가 쉽지 않다. 이 경우 유틸리티 프로그램을 사용하여 GL의 배열 형태로 변환해야 한다.

3) 텍스처 매핑 방법 할당(Assign Texture Mapping)
: GL의 텍스처 매핑에는 자동 매핑과 수동 매핑(Manual Texture Generation)이 있다. 

1- 수동 매핑
: 물체 다각형 정점마다 이에 상응하는 텍스처 좌표를 프로그래머가 직접 입력해야 한다. 

glBegin(GL_POLYGON);
    glNormal3f(0.0, 0.0, 1.0);
    glTexCoord2f(0.2, 0.8);  // 텍스처 정점 a를
    glVertex3f(7.5, 10.5, 0.0); // 물체 정점 A에 할당
    glNormal3f(0.0, 0.0, 1.0);
    glTexCoord2f(0.4, 0.2);  // 텍스처 정점 b를 
    glVertex3f(0.0, 3.8, 0.0); // 물체 정점 B에 할당
    glNormal3f(0.0, 0.0, 1.0);
    glTexCoord2f(0.8, 0.4);  // 텍스처 정점 C를
    glVertex3f(12.0, 0.0, 0.0); // 물체 정점 C에 할당
glEnd();

glNormal3f()는 정점 수직 벡터를 의미한다. 이는 조명에 필요한데, 조명에 의한 물체색과 텍스처에 의한 물체색을 혼합할 필요가 있을 때를 대비한다. 만약 온전히 텍스처만 입히려면 조명이 필요 없으므로 생략할 수 있다. 

2- 자동 텍스처 매핑
: GL의 텍스처 좌표는 동차 좌표(s, t, r, q)로 표시된다. 동차 좌표의 정의애 의해 만약 3차원 텍스처 좌표라면 이 값은 (s/q, t/q, r/q)를 의미한다. 기본값은 q = 1.0이다 .만약 2차원 텍스처 좌표라면 r=0.0이고 r값은 사용되지 않음을 의미한다. 
따라서 glTexCoord1() 함수를 호출하면 (s, 0.0, 0.0, 1.0), glTexCoord2() 함수를 호출하면 (s, t, 0.0, 1.0), glTexCoord3() 함수를 호출하면 (s, t, r, 1.0), glTexCoord4() 함수를 호출하면 (s, t, r, q) 형식으로 텍스처 좌표가 표시된다. 

여기서 4차원 텍스처는 q 값의 변화에 의해 s, t, r값을 조절할 수 있음을 의미한다. 예를들어, 맨 위에[그림 11-37] 처럼 텍스처 자체에 원근변환을 가해야 할 경우 q값을 조절함으로써 가능하다. 
원근에 따른 텍스처 보정(Perspective Correction)을 가하려면 다음 함수를 호출해야 한다. 

void glHint(GLenum target, GLenum hint);

두 번째 파라미터인 hint는 GL_DONT_CARE, GL_NICEST, GL_FASTEST 등의 값이 들어갈 수 있다. 일반적으로 3차원 깊이 정보를 감안하여 원근에 따를 선형보간을 가하려면 GL_NICEST를 선택하고, 2차원 화면 좌표계에서 선형보간을 가하려면 GL_FASTEST를 선택한다.
GL_DONT_CARE는 두 가지 방법 모두 좋다는 뜻이다. 이러한 hint는 그래픽 드라이버에 따라 서로 다르게 해석되며, 경우에 따라서는 전혀 무시되기도 한다. 

자동 매핑(Automatic Texture Generation)을 사용하면 물체 내부의 모든 정점마다 텍스처 좌표가 자동으로 할당된다. 이후 다각형 내부의 텍스처 좌표는 보간에 의해 할당된다. 
자동 매핑을 위해서는 다음 함수를 호출해야 한다. 

void glTexGen{ifd}[v](GLenum coord, GLenum pname, TYPE param); 


위 표에서 텍스처 생성 모드(GL_TEXTURE_GEN_MODE)를 보면 GL의 텍스처 매핑 방법은 3가지다. 

자동 매핑방법에서 GL의 텍스처 좌표는 기준 평면(Reference Plane)과 물체면 정점 간의 거리에 의해 할당된다. 



위 코드에서 기준평면은 [1, 0, 0, 0] 이다.
이 말은 평면의 식 Ax + By + Cz + D에서 A, B, C, D의 값이 [1, 0, 0, 0] 이라는 뜻이다. 
이 경우 평면에 수직인 법선 벡터는 (A, B, C) 이기 때문에 [1, 0, 0]이 평면에 수직인 벡터가 되므로 평면은 y축과 z축이 이루는 평면에 나란한 평면이라는 얘기다.
D는 원점과 평면 간의 거리와 관련된 계수로, (A, B, C)가 단위 벡터라면 D는 원점과 평면 간의 거리다. 이 경우 (1, 0, 0)이 X축의 단위 벡터이기 때문에 D는 원점과 평명간의 거리이고, 이 값이 0이기 때문에 이 평면은 원점을 통과하는 평면이다. 

평면과 점사이의 거리는 평면의 법선벡터가 단위 벡터이고 원점과 평면의 거리를 나타내는 D값이 0이니까 법선벡터를 N이라고 놓고 점이 P면 N 내적 P의 값으로 구할 수 있다. (N이 단위벡터일때 N 내적 P는 P벡터의 N방향 크기를 나타낸다. D값이 있으면 D를 단순히 더해주면 된다.)

* 내적의 활용
http://ifyouwanna.tistory.com/entry/%EB%82%B4%EC%A0%81%EC%9D%98-%ED%99%9C%EC%9A%A9

GL_OBJECT_LINEAR
: 물체 정점과 기준 평면 모두 모델 좌표계를 기준으로 정의된다. 이 경우 평면과 물체간의 상대적인 위치가 고정되기 때문에 시점을 바꿔도 평면과 물체가 모두 동일한 모델 뷰 변환을 겪게 되고, 물체면에 매핑된 텍스처는 변하지 않는다. 즉, 텍스처를 물체면 위에 씌워 고정시킨 것과 같은 효과를 보인다. 

GL_EYE_LINEAR
: 물체 정점과 기준 평면 모두 시점 좌표계를 기준으로 정의된다. 그러나 이 방법에서는 기준 평면을 다시 원래의 모델 좌표로 되돌린다. 즉, 물체에 모델 뷰 변환이 가해진 이후의 좌표다. 모델 변환에 의해 물체에 기하변환을 가하거나 뷰 변환에 의해 시점을 움직이면 모델 뷰 행렬이 바뀌고, 물체 정점의 위치도 바뀐다. 그러나 기준 평면에는 이러한 변환이 적용되지 않는다. 모델 뷰 행렬의 역 행렬을 곱해줌으로써 기준 평면을 이전의 모델 좌표계로 되돌려 버렸기 때문이다.

결과적으로 물체를 움직이면 기준 평면은 고정되고, 물체만 움직이게 된다. 이 경우 물체가 움직일 때마자 기준 평면과 물체와의 거리가 달라지므로 텍스처 패턴이 달라진다. 

텍스처 행렬 스택(Texture Matrix Stack)을 사용할 수 있다. 이 스택은 최소 2개 이상의 행렬로 구성되며, 모델 뷰 행렬과 마찬가지로 스택 톱에 있는 행렬이 최종적으로 곱해지는 행렬이다.

* 텍스처 패턴의 기하 변환

glMatrixMode(GL_TEXTURE);
    glLoadIdentity();
    glRotatef(45, 0.0, 0.0, 1.0);
    glTranslatef(0.5, 0.0, 0.0);
glMatrixMode(GL_ModelView);

glMatrixMode는 행렬 모드는 텍스트 행렬 모드로 바꾼다.

GL_SPHERE_MAP
:  주면 매핑을 위한 것이다.

glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);

위 코드는 물체 주변을 둘러싼 원구의 모습이 물체 표면에 맺히도록 텍스처 좌표 (s, t)를 할당한 것이다. 
이 경우 텍스처 매핑은 GL에 의해 자동으로 이루어진다. 따라서 프로그래머는 주변 매핑을 위한 텍스처만 제시하면 된다. 

4) 텍스처 파라미터 명시(Specify Texture Parameters)
: GL의 텍스처 파라미터는 크게 주어진 텍스처를 어떻게 확장할 것인지를 결정하는 파라미터와, 주어진 화소의 텍스처를 어떻게 계산할 것인지를 결정하는 파라미터로 구분할 수 있다. 
파라미터를 할당하기 위한 GL 함수는 다음과 같다.

void glTexParameter{if} (GLenum target, GLenum pname, TYPE param);

아래 표는 이 함수의 파라미터를 나타낸 것이다.

target은 텍스처가 2D인지 3D인지 나타낸다.
pname은 2종류로 대별된다. GL_TEXTURE_WRAP_S,  GL_TEXTURE_WRAP_T는 텍스처를 s, t 방향으로 어떻게 확장할 것인지 결정한다. 일명 래핑(wrapping)이라고 한다. 

1- 래핑(wrapping)
: 텍스처를 s, t방향으로 어떻게 확장할 것인지 결정(GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T)

GL_REPEAT

(a)의를 3배로 반복확장하여 (b)로 만듦. [0, 1]에서 정의된 텍스처를 [0. 3] 으로 만들어 준것이다.
GL_REPEAT가 명시되면 GL은 glTexCoordf2() 함수의 파라미터를 인식하는 방법을 달리한다. 예를들어, glTexCoordf2(2.5, 2.5)를 glTexCoordf2(0.5, 0.5)로 해석한다.

GL_CLAMP

확장 영역이 경계선의 색으로 고정(clamping) 된다.  

2- 해당 화소의 텍스처 선택 방법 결정

GL_TEXTURE_MAG_FILTER
: 확장관계를 말한다. 확장관계에 사용할 수 있는 필터는 2가지다. 
 - GL_NEAREST : 점 샘플링에 해당.
 - GL_LINEAR : 양방향 선형 보간으로써, 인접한 4개의 텍셀을 보간.

GL_TEXTURE_MIN_FILTER
: 축소관계를 말한다. 축소관계에 사용할 수 있는 위 두가지 방법 이외에 4가지 필터가 더 있다.
 - GL_LINEAR_MIPMAP_NEAREST : 주어진 해상도에 가장 가까운 밉맵(MIPMAP_NEAREST)을 선택한 후 양방향 선형보간을 가하는 방법.
 - GL_NEAREST_MIPMAP_NEAREST : MIPMAP_NEAREST 에서 다시 점 샘플링을 가하는 방법.
 - GL_LINEAR_MIPMAP_LINEAR: 밉맵 사이를 보간하여 새로운 밉맵(MIPMAP_LINEAR)을 만든 후 양방향 선형보간을 가하는 방법, 이는 3방향 선형보간에 해당함.
 - GL_NEAREST_MIPMAP_LINEAR: 밉맵 사이를 보간하여 새로운 밉맵을 만든 후 점 샘플링을 가하는 방법.

* 밉맵을 생성하기 위해서는 GLU 함수 또는 GL함수를 사용해야 한다. GLU를 사용해야 할 경우에는 다음과 같이 GLU의 밉맵 생성기 함수를 호출해야 한다. 

GLint gluBuild2DMipmas(GLenum target, GLint internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *texture);

5) 텍스처 환경 명시(Specify Texture Environment)
: 텍스처를 물체면에 그대로 입힐 수도 있지만 물체색과 조합할 수도 있다. GL에서 이러한 기능을 수행하는 함수가 glTexEnv*()로 텍스처 환경을 명시하는 함수라고 불린다.

void glTexEnv{if} (GLenum target, GLenum pname, GLenum TYPEparm)

첫 파라미터 target은 반드시 GL_TEXTURE_ENV로 해야한다.
파라미터 pname을 GL_TEXTURE_ENV_MODE로 하면 텍스처의 색과 물체면의 색을 어떻게 조합할 것인지를 명시할 수 있다.
GL_TEXURE_ENV_COLOR는 GL_TEXTURE_ENV_MODE의 TYPEparm이 GL_BLEND 일때만 사용할 수 있다. 즉, GL_TEXTURE_ENV_MODE에서 GL_BLEND를 호출했을 때는 이후 glTexEnv*() 함수에서 pname을 GL_TEXTURE_ENV_COLOR로 놓고 TYPEparm에 RGBA 값을 지니 매열명을 명시해야 한다. 

텍스처 표현 방식은 glTexImage*D() 함수의 두번째 파라미터인 internalformat에 의해 설정되는데 이 값이 GL_RGB, GL_RGBA 경우일 때에 따라 각각의 계산방식을 표현한것이 위 표이다.

기본적으로 GL_REPLACE는 기존 물체면의 색을 텍스처의 색으로 대체하는 것이고, GL_MODULATE는 물체면의 색과 텍스처의 색을 곱하는 것이다. GL_DECAL, GL_BLEND는 물체면 위에 텍스처 색을 덧칠하는 효과를 주기 위한 것이다.

위 표에서 C(t)는 텍스처의 색이고, C(f)는 프레그먼트의 색, 즉 물체면의 색이다. 같은 규칙으로 A는 각각의 알파값을 나타낸다. 

GL_BLEND에서 C(c)는 GL_TEXTURE_ENV_COLOR에 의해 명시된 색을 의미한다. 

6) 텍스처 객체
: 텍스처를 적용하려면 CPU 메모리로부터 텍스처 메모리로 텍스처를 로드해야 한다. 텍스처 역시 하나의 상태 변수이고 GL 버전 1.0 에서는 이 상태 변수 하나를 공유해서 사용했기 때문에 텍스처 대상을 바꿀때 마다 텍스처 상태 변수 내용을 변경하고 텍스처를 텍스처 메모리로 올리는 작업을 매번 실행해야 했다. 

이러한 단점을 개선하기 위해서 GL 1.1 부터는 텍스터 객체(Texture Object) 개념이 도입되었다. 
즉, 텍스처와 해당 텍스처에 적용되는 파라미터 값을 묶어서 하나의 텍스처 객체를 정의할 수 있다. 
여기서 파라미터는 해당 텍스처와 관련된 함수 호출에 의해 설정된 값을 의미한다. 
또한 GL 1.1 부터는 물체마다 개별적인 텍스처 객체를 정의할 수 있으먀, 이를 위해 텍스처 메모리에는 동시에 여러 개의 텍스처를 저장할 수 있게 되었다.

GLubyte MyTextureA[8][8][3]; // 텍스처 영상 A
GLubyte MyTextureB[64][64][3]; // 텍스처 영상 B
GLuint ObjectId[2]; 2개의 텍스처 객체명을 저장하기 위한 공간, glGenTextures()에 의해 할당 됨.

glGenTextures(2, ObjectId)
glBindTexture(GL_TEXTURE_2D, ObjectId[0]); // 실제 텍스처를 표현할 객체를 생성하고 여기에 객체명을 할당한다. 

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 8, 8, 0, GL_RGB, GL_FLOAT, MyTextureA);
glTexParameteri(.....);
glTexEnvf(....);

glBindTexture(GL_TEXTURE_2D, ObjectId[1]); Object[1]을 위한 텍스처 객체 생성 및 객체명 할당.

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 8, 8, 0, GL_RGB, GL_FLOAT, MyTextureB);
glTexParameteri(.....);
glTexEnvf(....);

....
glBindTexture(GL_TEXTURE_2D, ObjectId[0]); // ObjectId[0]이 활성화되고, 그 아래 원구를 그리는데 활용된다.
glutSolidSphere(1.0, 16, 16);

glBindTexture(GL_TEXTURE_2D, ObjectId[1]); // ObjectId[1]이 활성화되고, 그 아래 주전자를 그리는데 활용된다.
glutSolidTeapot(1.0);





Posted by 홍성곤
,

1. 텍스처 매핑
: 래스터 변환 후 은면제거, 음영 후에 모델에 옷을 입히는 거라고 생각하면 된다. 그 옷을 텍스처 또는 텍스터 맵이라고 한다. 
: 일반적으로 2차원 영상을 텍스처로 사용하지만, 1, 2, 3 차원 영상 모두를 텍스처로 사용할 수 있다.



위 [그림 11-7] 원구에 [그림 11-6] 에 텍스처를 매핑한 결과가 [그림 11-8] 이다.

위 그림은 라이트 매핑이다. (a)는 평면에 벽돌 영상을 매핑시킨 것이다. (b)는 미리 계산된 조명 결과를 영상으로 표현한 것이다. (a)와 (b)를 서로 곱해서 혼합하면 (c)처럼 벽면에 조명을 가한 모습이 된다. 

원구를 둘러싼 주변 모습을 텍스처로 사용하여 [그림 11-10] 원구 표면에 입힐 수 있다. 그 결과 [그림 11- 11] 처럼 하늘, 건물, 호수 모습이 원구 표면에 비쳐 보인다. 이것을 주변 매핑(Environmental Mapping) 이라 불린다. 주변 매핑은 주로 반짝이는 물체면을 표현할 때 사용되는 기법이다. 

2. 텍스처 매핑 기법



위 그림은 텍스처 매핑이 가해지는 시기를 나타낸다. 텍스처 매핑은 래스터 변환 과정에서 실행된다. 


위 그림에서 보듯이 화소 연산(Pixel Operation) 이라는 별도의 파이프라인을 거쳐서 진행하며 이것이 정점 처리를 위한 기하 파이프라인과 만나는 곳이 바로 래스터 변환이다.

텍스처도 영상의 일종이므로 배열을 사용해 저장한다. 텍스처를 구성하는 배열 요소 각각을 텍셀(Texel: Texture Element)이라 한다. 픽셀과 마찬가지로 텍셀도 (R, G, B, A) 정보가 저장된다.
텍셀 크기는 주어진 영상을 얼마나 잘게 잘라 배열로 저장하느냐에 따라 달라진다.

위 그림 (a)에 표시된 텍셀은 6*5 크가의 2차원 배열로 저장할 때의 크기다. 일반적으로 텍스처 좌표는 정규화 형태로 표현한다. 즉, (a)의 텍스처 좌하단 좌표는 (0, 0) 으로, 우상단 좌표는 (1, 1)로 표현한다.

또한 위 그림은 텍스처 매핑을 개념적으로 설명한 것이다. (s, t) 좌표로 표현된 텍스처 영상을 (x, y, z) 좌표로 표현된 물체면으로 사상하는 것이 텍스처 매핑이다. 그림 (b)는 2차원 화면 좌표로 표현하였지만 물체 정점이 아직 깊이 정보인 z값을 지니고 있다는 점에서 3차원으로 간주해야 한다.
텍스처 매핑에서 가장 큰 어려운 부분은 2차원 영상과 3차원 물체와의 관계다. 즉, "텍스처로 사용된 2차원 영상의 어느 부분이 3차원 물체의 어느 부분으로 사상시킬 것인가?" 하는 문제다.
이를 경우별로 나눠 알아보자.

1) 평면 다각형의 텍스처 매핑
: 텍스처가 3차원 평면 다각형으로 사상될 경우는 상대적으로 수월하다. 가장 간단한 방법으로는 프로그래머가 물체 다각형 정점을 대상으로 해당 정점의 텍스처 좌표 (s, t)값을 직접 할당하는 것이다. 



(a)는 텍스처를 (b)는 텍스처가 사상될 평면 다각형의 정점을 예시한다. (b)의 네 정점에 (0, 0), (1, 0), (1, 1), (0, 1)을 할당하면 결과는 (c)처럼 된다. 



위 그림은 (b)처럼 다각형 우상단에 텍스처 좌표 (0.5, 0.5)를 할당한 것이다. 

텍스처 매핑의 방향은 텍스처 -> 다각형 보다 다각형 -> 텍스처로 가는것이 편리하다. 무슨말이냐 하면, 다각형 내부화소 마다 해당 화소로 사상될 텍셀을 텍스처로 부터 찾아내는것이 반대의 경우 보다 상대적으로 간단하기 때문이다. 삼각형을 예로 들어보자.

(a) 삼각형의 화소 V의 텍스처를 결정하기 위해서는 먼저 해당 화소의 무게중심 좌표(Barycentric Coordinates)를 구해야 한다. 무게 중심 좌표를 구해서 (b)에 대입한후 해당 텍셀값을 뽑아와야 한다.

텍스처 매핑에서도 에일리어싱이 발생한다. (a)를 (b)텍스처에 매핑하면 (a)의 ABCD 화소는 모두 백색으로 표현된다. 이를 에일리어싱이라고 한다. 

한가지 더 문제가 되는것은 원근 변환이다. 원근 변환은 특성상 원래의 물체 길이를 보존하지 않는다. 그림 (b)의 원 물체를 아래쪽에서 바라보면 그림 (a) 처럼 된다. 이 경우 텍스처 매핑의 정확성이 감소한다. 즉, 투상 이후의 정점을 기준으로 구해진 무게 중심 과표는 원래 정점을 기준으로 구해진 무게중심 좌표와는 서로 다를 수 있다. 
이 문제는 기본적으로 원래 물체에 텍스처를 입힌 다음에 투상해야 함에도 불구하고, 파이프라인 처리 속도를 높이기 위해 투상 후에 텍스처를 가하기 때문에 발생한다. 

2) 곡면의 텍스처 매핑
: 텍스처 매핑의 대상이 곡면일 경우에는 좀 더 복잡하다. 이는 평면 지도를 가지고 원구형의 지구본을 완전히 감싸라는 주문과도 같다.
그래서 컴퓨터 그래픽에서는 이러한 왜곡을 최소화하는 여러가지 방법이 있다.

1- 파라미터 곡면
: 컴퓨터 그래픽에서는 곡면을 매개 변수로 표현하기도 한다. 예를 들어 밑에 그림에서 반지름이 r인 원구 표면에서 점 P 좌표는 직교 좌표계에서는 (x, y, z) 이지만 원구 좌표계에서는 (u, v)라는 두 개의 파라미터로 표현한다.



즉, 밑에 그림에서 보듯이 직교 좌표계의 s, t를 u와 v로 매핑시키는 것이다. s, t가 0에서 1로 갈때 u, v는 0에서 2파이로 간다. 

2- 다각형 곡면
: 곡면을 매개 변수로 표시할 수 없을 때, 즉 곡면이 수학적으로 정의될 수 없는 경우에는 2단계 매핑(2-Stage Mapping)이 사용된다. 

2단계 매핑의 첫 단계는 S 매핑으로서, 텍스처를 중개면(Intermediate Surface)에 입힌다. 중개면으로는 원기둘, 육면체, 원구 등 다양한 입체 표면이 사용된다. 물론 중개면에 따라 최종 결과가 달라진다.

중개면은 파라미터 u, v로 표현할 수 있기 때문에 쉽게 매핑이 가능하다.

2단계는 O 매핑으로서, 중개면 내부에 실제로 텍스처를 가할 물체를 넣고 텍스처를 입히는 작업이다.

[그림 11-26] 처럼 중개면 뚜껑(cap)에 텍스처를 입히기도 하는데, 뚜껑의 텍스처는 그림처럼 아래쪽 옆면의 텍스처를 연장하여 입힐수도 있고, 필요에 따라서는 텍스처를 직접 입힐 수도 있다.

O 매핑을 할때의 고민점은 어떻게 텍스처 안에있는 물체에 좌표를 사상시킬 것인가 인데, 4가지 방법이 있다. 

(a) 물체면 법선 벡터
: (a)처럼 물체면의 법선 벡터가 중개면과 만나는 점을 구한 뒤, 그 점의 텍스처 값으로 사상시키는 방법이다.

(b) 물체 중심(Object Centroid)
: 물체 중심으로부터 물체면을 향한 직선과 교차하는 중개면의 텍스처를 해당 물체면의 텍스처로 사용하는 방법이다.

(c) 중개면 법선 벡터
: 중개면 법선 벡터의 연장선과 물체면이 만나는 곳을 구해 해당 면에 중개면의 텍스처를 뿌리는 방법이다.

(d) 시점 반사 벡터
: 시점에서 물체면으로 향한 직선이 표면에 반사되어 중개면과 만나는 곳의 텍스처를 물체면의 텍스처로 사용하는 방법이다. 

3) 주변 매핑
: 물체를 에워싼 주변 환경을 물체면에 매핑시키는 것을 말한다.
주변 매핑이 가해지는 물체는 주로 표면이 매끄러운 물체다. 조명 모델에서 보면 경면 반사를 위주로 표현할 수 있는 물체를 말한다. 이러한 의미에서 주변 매핑을 반사 매핑(Reflective Mapping) 이라고도 부른다. 

주변 매핑 역시 2단계 매핑 방식으로 중개면 이 필요하다. O 매핑에서 항상 위 그림 (d)의 시점 반사 벡터 방식으로 구현된다. 
육면체 중개면을 이용한 주변 매핑은 주로 실내 환경 묘사에 자주 사용된다. 방 한가운데에서 보이는 것은 바닥, 천정과 4개의 벽이기 때문이다. 


Posted by 홍성곤
,

1. 보간법
1) 무게중심 좌표
: 벡터 외적에 의해 무게중심 좌표를 구해서 보간 하는 방법이다. 상대적으로 많은 시간이 걸린다.
자세한 내용은 설명하지 않겠다.

2) 양방향 선형보간
: 이 방식을 적용한 결과는 벡터 외적에 의한 무게 중심 좌표를 적용한 결과와 완전히 동일하다. 그러나 이 방법은 상대적으로 빠르다는 장점이 있다. 

V의 색을 구한다고 가정했을 경우, 우선 R과 P의 색을 보간하여 S의 색을 구한다. 그리고 R과 Q의 색을 보간해 T의 색을 구한다음, S와 T의 색을 보간해 V의 색을 구하는 방법이다.


2. GL의 그래픽 기본요소 
: GL의 그래피기 기본요소는 점(GL_POINTS), 선(GL_LINES), 도형(GL_POLYGON) 등으로 구성된다. 기본 선분에 해당하는 GL_LINE 외에도 GL_LINE_STRIP, GL_LINE_LOOP 등의 선이 있으며, GL_POLYGON 외에도 GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_QUAD_STRIP 등의 도형이 있다.

V1 -> V8 순서대로 Vertex를 그리면 Type에 따라 표현되어 지는게 달라진다.
GL_LINES는 정점 쌍을 잇는 선분을 그려낸다. 만약 홀수개의 정점이 선언되면 마지막 정점은 무시된다. 
GL_LINE_STRIP은 꺾은선(Polyline)을 그리기 위한것이다. n개의 정점이 선언되면 (n-1)개의 선분이 이어져 그려진다. 
GL_LINE_LOOP은 GL_LINE_STRIP가 동일하지만 마지막 정점을 첫 정점과 이음으로써 하나의 루프를 형성한다. 

GL_TRIANGLES는 삼각형을 그리기 위한 것으로 정점 수가 3의 배수가 아니라면 마지막 하나 또는 두 개의 정점은 무시된다. 
GL_QUADS는 사각형을 그리기 위한 것으로 정점 수가 4의 배수가 아니라면 마지막 1~3개의 정점은 무시된다. 
GL_POLYGON은 다각형을 그리기 위한 것으로 정점은 3개 이상이어야하고 3개 미만이면 아무것도 그리지 않는다. 

GL은 다각형의 경우 세 가지 규칙이 필요하다.
1) 단순 다각형(Simple Polygon) 이어야 한다. 이는 명시된 순서대로 정점을 따라갔을 때 변끼리 교차하지 않아야 한다는 것이다. 

(b)는 변끼리 교차하므로 비단순 다각형(Non-Simple Polygon) 이다.

2) 볼록 다각형(Convex Polygon) 이어야 한다.
3) 평면 다각형(Flat Polygon) 이어야 한다. 이는 3차원 공간에서 다각형을 구성하는 모든 정점이 동일 평면 위에 존재해야 한다는 것이다. 

GL에서 제시된 정점이 이러한 3가지 조건 중 하나라도 만족시키지 못하면 프로그램 실행 결과는 예측할 수 없다. 

GL_TRIANGLE_STRIP은 일련의 삼각형을 그리기 위한 것이다. 명시된 정점 순서에 오른손 법칙을 가하면 법선 벡터의 방향이 결정된다. 순서대로 정점을 명시하면 V1-V2-V3, V3-V2-V4, V3-V4-V5등의 정점 순서에 의해 삼각형이 정의 된다.
GL_TRIANGLE_FAN은 첫 정점을 중심으로 나머지 삼각형이 이 중심을 공유하는 형태다.
GL_QUAD_STRIP은 사각형을 그리기 위한것으로 사각형의 정점 순서는 V1-V2-V4-V3, V3-V4-V6-V5 등의 순서로 정의된다. 

void glPointSize(GLfloat size);
void glLineWidth(GLfloat width);
void glLineStipple(GLint factor, GLushort pattern);
void glPolygonStipple(const GLubyte *mask);
void glShadeModel(mode);


위 함수들은 기본 요소를 그릴 때 필요한 속성들을 지정하는 함수이다. 즉 크기, 색, 두께, 패턴(Stipple Pattern) 등 이다. 
glLineStipple() 함수는 점선이나 쇄선을 그리기 위한 것이다. 파라미터 패턴은 16비트로 나타낸 것으로, 비트값 1은 해당 화소를 그림을, 비트값 0은 그리지 않음을 의미한다. factor는 이 패턴을 축소 또는 확대하기 위한 배율을 말한다.

다각형 내부를 패턴으로 채우기 위해서는 glPolygonStipple() 함수를 사용할 수 있다. 이 함수의 패턴 역시 glLineStipple() 함수와 유사하게 정의할 수 있지만 이 경우에는 2차원 패턴을 정의해야 한다. 


glShadeModel()은 다각형의 내부 채움 모드를 설정하는 함수다. 이 모드는 GL_FLAT 또는 GL_SMOOTH로 설정된다. GL_FLAT은 다각형 내부가 모두 동일한 색으로 채워지고, GL_SMOOTH는 (b) 처럼 정점의 색이 보간되어 칠해진다. 


3. 에일리어싱과 앤티 에일리어싱
: 에일리어싱은 래스터 그래픽 장비에서는 회피할 수 없는 문제로, 래스터 변환 단계에서 필연적으로 발생한다. 이는 무한히 섬세한 모양을 가진 실제 사물을 일정 화소 단위로 화면에 나타내야 하기 때문이다. 이러한 문제를 해소하기 위한 방법이 안티 에일리어싱 이다. 

1) 슈퍼 샘플링
: 화소를 더 작은 단위로 분할해서 분할한 부분의 색깔을 더해서 평균값을 구하는 것이다. 그 평균값을 해당 화소값으로 정한다. 2*2 슈퍼 샘플링은 포인트 샘플링에 비해 4배의 계산 시간이 필요하다.

1- 포인트 샘플링
: 분할한 부분의 색깔을 뽑아 낼때 중심점을 기준으로 뽑아 내는것이 포인트 샘플링이다. 

2- 지터에 의한 슈퍼 샘플링
: 분할한 부분의 색깔을 뽑아낼 위치를 랜덤으로 지정하는것이 지터에 의한 슈퍼 샘플링이다.

3- 영역 샘플링 
: 화소 밝기를 면적에 비례하게 계산해서 결정하는 방법이다. 





Posted by 홍성곤
,

1. 래스터 변환
: 래스터(Raster)라는 말은 화소(Pixel)을 의미한다. 따라서 래스터 변환(Rasterization) 또는 스캔 변환(Scan Conversion)이라는 말은 화소로의 사상을 의미한다. 

뷰 포트 변환의 결과 화면으로 사상되는 것은 다각형 또는 선분의 정점이다. (위 그림 (a) -> (b))


실수로 표시되던 좌표가 픽셀단위로 사상되는 과정에서 위 그림처럼 여러 선분이 하나로 합쳐지는 경우가 생긴다. (b)의  A, B 두 선분의 양 끝 vertex가 같은 픽셀로 사상되면서 두 선분은 완전히 똑같은 선분으로 취급된다. 

뷰 포트 변환과 달리 래스터 변환은 정점 뿐 아니라 점, 선, 면이 모두 화소 공간으로 사상되어야 한다. 정점 이외에 부분에 픽셀값을 정할때는 보간법을 사용한다. 예를 들어 첫번째 그림에서 1에 픽셀 값을 정할때는 D, E사이를 보간해서 픽셀값을 정하는 것이다. 

GL의 래스터 변환 시기는 G-버퍼 알고리즘에 의한 은면 제거 작업이 래스터 변환과 나란히 진행된다는 것이다. 래스터 변환이 끝나 모든 화소가 칠해진 다음에는 어느 화소가 어느 정점으로부터 온 것인지 구체적으로 알 수가 없어 정점으로부터 화소의 깊이나 색을 보간할 수 없기 때문이다.


2. 선분의 래스터 변환

선분의 화소 위치를 결정할 때는 기울기에 따라 계산하는 방법이 달라진다. 선분의 기울기가 1 보다 작으면 x값을 1씩 증가시키면서 거기에 해당하는 y값을 찾으면 되고, 기울기가 1 보다 크면 반대다. 
기울기가 음수면 절대값을 취해서 계산하면 된다. 

사실, 위의 화소 위치 결정방법은 "교차점 계산" 이라고 하는데 이 방법은 잘 쓰이지 않는다. 대부분의 그래픽 프로세서에서 쓰이는 방법은 "브레스넘 알고리즘" 이다.

1) 브레스넘 알고리즘(Bresenham Algorithm)

선분의 기울기가 0~1 사이라고 가정하고 생각해보자. 그러면 x의 좌표는 1씩 증가할테고, y의 좌표만 결정해 주면 된다. 왼쪽 그림을 보면 A의 오른쪽 좌표는 B, C 둘중의 하나이다. 이를 판별하는 방법은 오른쪽 그림처럼 B, C 중심에 각각 점을 찍고 그 점과 선분과의 거리를 계산해서 가까운 픽셀로 사상시키는 방법이다. 이를 실수 계산 대신 정수 계산으로 하기위해 B, C의 중심점을 정하고 선분과의 거리가 이보다 크냐 작냐만 판단해서 픽셀을 정하면 된다.

장점은 연산 속도가 정수 연산이기 때문에 상당히 빠르다는 것에 있다. 아울러, 부동 소수 연산에서 발행하는 오류 현상도 없어진다. 

원을 그릴때도 마찬가지로 동일한 방법으로 사용가능 하다. 

2) 화소 좌표 

좌표 표현방법은 두가지가 있다. 화소 좌표의 중심을 (a)처럼 가운데에 둘것이냐, (b)처럼 좌 하단에 둘것이냐다.
두 방식의 차이는 밑에 그림을 보면 더 잘 나타난다. 

(b)에서는 선분을 그려보니 (3, 3) 좌표에 좌 하단 부분까지 포함되었다고 가정해서 (3, 3)좌표까지 색을 칠한 것이다. 이렇게 그릴 경우 두 계산 방식모두 실제 선분보다 1 정도 길이가 길어지게 된다. 
이를 보완하는 방법은 (b)에서 (3, 3)좌표 좌 하단을 포함하더라도 그 값을 넘지 않으면 마지막 픽셀은 제외 시킨다. 그런식으로 (b) 방법을 적용하면 실제 선분길이와 거의 같다. 

사각형에 적용해도 두 방식의 차이가 확연히 드러난다. (b) 방법이 실제 사각형의 크기와 거의 동일한 모습으로 화소에 사상시킬 수 있다. 그리하여 GL을 포함한 대부분의 소프트웨어 에서는 (b) 방법을 사용한다. 

3. 다각형의 래스터 변환
1) 삼각형의 래스터 변환



선분의 식은 다음과 같다.

f(x, y) = (y1 - y2)x + (x2 - x1)y + x1y2 - x2y1 = 0

위 그림의 P(0, 0)와 Q(8, -4)를 연결하는 직선의 식은 다음과 같이 계산된다.

f(x, y) = 4x+ 8y = 0

이 선분 위쪽에 있는 점, 예를 들어 (5, 0)을 대입하면 20이 나옴으로써 0보다 크다. 즉, 선분 위쪽에 있는 모든 점은 f(x, y) > 0 을 만족한다. 
그러나 선분의 식이 x=2 처럼 수직으로 서 있으면 어디가 위고 어디가 아래인가? 엄밀한 의미에서 위, 아래가 아니라 왼쪽, 오른쪽의 판단이다. 여기서 왼쪽이라는 것은 위 그림의 점 P에서 Q를 향해 갈 때 진행 반향의 왼쪽을 말한다. 
따라서 진행 방향의 왼쪽에 있는 모든 점은 f(x, y) > 0을, 오른쪽에 있는 모든 점은 f(x, y) < 0을 만족한다. 
만약 Q에서 P로 향하는 반대 방향이라면 식은 다음과 같다.

f(x, y) = (-4-0)x + (0-8)y + 0 + 0 =0
f(x, y) = -4x - 8y = 0

여기에 (5, 0)을 대입하면 -20이 나온다. 
이를 일관성 있게 처리하려면 다음과 같은 규칙을 세우면 된다.

- P, Q, R 등 다각형의 모든 정점을 항상 반시계 방향으로 정의한다.
- 선분 f(x, y)를 구하기 위해 선분 식을 전용할 때는 항상 먼저 정의된 정점을 (x1, y1)으로, 나중에 정의된 정점을 (x2, y2)로 대입한다. 

위 삼각형의 방향은 P->Q->R이다. 각 선분의 왼쪽에 해당하는 f(x, y) > 0에 해당하는 픽셀들을 삼각형 안쪽이라고 보고 해당 픽셀에 값을 할당한다.

* 오목 다각형인 경우에는? 
: 실제 삼각형 같은 볼록 삼각형인 경우에는 한 점을 정해놓고 이 점이 3개의 선 왼쪽에 있는 형태이면 삼각형 안쪽이라고 판단한다.

단, 위 그림 (b)와 같이 검정색 점은 다각형 내부 인데도 선분 1 기준으로 보면 오른쪽에 존재한다.
이런 오류를 방지하기 위해 먼저 다각형이 오목 다각형인지 볼록 다각형인지 판정을 하고 오목 다각형일 경우에는 다각형을 삼각형으로 분할하여 각 픽셀의 내, 외부 판정을 한다.

오목, 볼록 다각형을 판정하는 방법은 두가지가 있다.
첫째, 모든 정점을 대상으로 그 정점이 어떤 선분의 오른쪽에 있는지 확인하는 방법이다. 하나라도 어떤 선분 오른쪽에 있다면 그것은 오목 다각형이다. 위 (a) 그림에서 A 정점은 1 선분 오른쪽에 있기 때문에 오목 다각형이다.
둘째, 이어지는 선분끼리 벡터 외적을 구하는 방법이다. 벡터 외적에는 두 선분이 이루는 각의 sin값이 곱해지므로 그 각이 180도 보다 크면 벡터 외적 값이 -부호 값을 가지게 된다. 최소 하나 이상의 모서리 각이 180보다 크면 오목 다각형이다. 위 (b) 그림에서 1, 2번 선분의 각이 180도 이상이기 때문에 오목 다각형인 것이다. 

2) 주사선 채움 알고리즘
: 위 삼각형의 래스터 변환에서 사용한것 보다 조금 더 일반적인 방법을 소개하겠다.  
컴퓨터 모니터의 주사선은 노트 필기하듯이 위에서 아래로, 그리고 왼쪽에서 오른쪽으로 화면을 훑어 가면서 개별 화소를 밝히게 된다. 그러나 지엘은 화면 좌하단에 좌표축 원점을 설정하기에 주사선이 화면 아래에서 위쪽으로 올라간다고 가정해보자.

위 그림의 오목 다각형을 예로들면, 주사선 마다 선분과의 교차점을 가지고 있을텐데 홀수 규칙이라고 해서 현재 교차점 번호가 홀수이면 짝수가 되기 직전까지 화소를 칠해가는 방법이다. 그러면 다각형 안쪽의 화소들만 색이 칠해진다. 

3) 씨앗 채움 알고리즘
: 그림판 같은데서 다각형 안에 색깔을 채우려고 다각형 안에 한 픽셀을 클릭하면 그 픽셀을 중심으로 색이 인근방향으로 계속 퍼지는 형태로해서 픽셀들에 색깔을 칠하는 것이다. 단, 경계선을 만나면 그만해야 한다. 

Posted by 홍성곤
,

1. 벡터
1) 정규화 벡터 
: 원래 벡터와 방향은 동일하지만 크기가 1이 되도록 변형한 것.

삼각형 밑변4, 높이 3 -> 빗변 = (4^2 + 3^2)^1/2

 
위는 3차원 벡터의 크기

각 성분을 벡터의 크기로 나눠주면 됨


2) 벡터의 내적과 외적
: 두 벡터의 내적은 두 벡터의 크기를 곱한것과 두각이 이루는 각 ∂에 COS을 곱한 값이다. 각 벡터를 성분별로 표시할 수 있다면, 두 벡터의 성분별 곱의 합이다.


벡터 t의 끝점에서 벡터 s의 수직으로 내린 선을 긋고 얻어진 삼각형의 밑변의 길이가 |t|*COS∂ 이다. |t|*COS∂는 벡터 t의 s방향 크기다. 비유적으로 말하면 벡터 t가 벡터 s방향에 끼치는 영향도이다. 예를들어 벡터t 위에서 빛을 내리쬐면 s방향으로는 |t|*COS∂ 만큼의 그림자가 발생한다. 
만약 두 벡터가 서로 수직이면 COS90 = 0 이므로, 내적이 0이 된다. 서로 끼치는 영향도가 전혀 없다는 것이다. 

벡터 내적이 스칼라인데 반해 벡터의 외적은 벡터다.

법선 벡터(Normal Vector, Surface Normal Vector)는 평면에 수직인 벡터이다. 따라서 위 식의 n은 s벡터와 t벡터가 이루는 평면에 수직이고 길이가 1인 단위 법선벡터이다.
즉, 벡터 외적은 두 벡터의 크기의 곱에 두 벡터가 이루는 각의 Sin을 취한 값을 곱한 크기의 벡터로, 방향은 두 벡터가 이루는 평면에 수직이다. 

위 그림은 벡터 s와 벡터 t의 외적을 나타낸다. 외적벡터의 크기는 벡터 t의 끝점을 벡터 s 방향으로 수직으로 그었을 때 형성되는 삼각형의 높이인 |t|*sin∂에 벡터 s의 크기를 곱한 양이다. 그림에 나타난것과 같이 두 벡터를 양변으로 하는 평행사변형의 넓이에 해당한다. 

외적벡터의 방향은 두 벡터가 이루는 평면에 수직인 방향으로, 오른속 법칙에 따른다. 즉, 첫 벤터인 s 로부터 둘째 벡터인 t를 향해 오른손 주먹을 감싸쥐었을 때 엄지 손가락의 방향이 외적 벡터의 방향이다.

s X t = - t X s 

위 식은 벡터 외적이 곱하는 순서에 따라 달라짐을 의미한다. 두 벡터외적의 크기는 같지만 방향이 정반대다.

3) 평면 표현


위 그림에서 P, Q는 평면위의 점이고 N은 점Q에서 출발하는 법선벡터 이다. P-Q는 어파인 공간에서 점Q에서 P로 향하는 벡터이기 때문에, 아래와 같은식이 성립한다.

그림에서 점 P가 어디에 있든지 위 식은 성립한다. 그러므로 점 P를 일반화 시켜서 (x, y, z)로 표시하고 법선 벡터 N을 (A, B, C)로 표시하면 아래와 같은 식을 구할 수 있다.



이 벡터와 법선 벡터의 내적값 Q*N을 D로 치환하면 아래의 식이 나온다. 

이 식이 바로 평면의 식이다. 기억할 점은 이 식으로 표현되는 평면의 법선 벡터는 (A, B, C)라는 것이다. 예를 들어 어떤 평면이 2x+3y+4z+5=0 이라는 수식으로 표현된다면, 이 평면에 수직인 벡터는 (2, 3, 4)이다. 

4) 지엘의 법선 벡터 
: 3차원 그래픽에서 법선 벡터는 면이 향해있는 방향을 가리킨다. 
법선 벡터는 매우 간단한 방법으로 구할 수 있다. 



위 그림에서 정점 P, Q, R 순서로 선언되면 아래와 같은 관계식이 성립한다.

이처럼 법선 벡터를 구할 수 있다면, 정점 P, Q, R로 구성되는 평면의 식도 쉽게 구할 수 있다. 위의 식으로 계산된 법선 벡터가 N = (A, B, C) 라면, 해당 평면의 식은 Ax+By+Cz+D=0 이다. x, y, z에 위 그림의 세 점중 하나만 집어 넣어서 D를 구하면 된다. 

만약 정점을 선언할때 위 순서의 반대로 R, Q, P로 선언했다면 법선 벡터의 방향이 반대가 된다. 법선 벡터는 오른손 법칙을 따르기 때문이다. 

2. 후면 제거(Backface Culling, Backface Removal)



위 그림은 원구 표면을 나타낸 것이다. 화살표 들은 원구 표면을 이루는 평면 다각형의 법선벡터를 나타낸다. (b)는 법선 벡터들이 시점방향인 것들만 나타낸 것이다. 
즉, 법선 벡터가 시점방향이 면을 전면, 반대인 경우를 후면이라 한다. 후면은 컴퓨터 그래픽상에서 필요 없는 부분이기 때문에 제거를 해줘야 한다. 

물체면으로부터 시점을 향한 벡터를 시점벡터라고 한다. 면 A로부터의 시점벡터를 V1, 면 B로 부터의 시점벡터를 V2라 하고, A의 법선벡터를 N1, B의 법선벡터를 N2라 하자.

위 벡터들 가지고 A면과 B면이 시점에서 바라봤을때 보이는지 안보이는지를 알 수 있다.
A가 보이는지 알려면 V1*N1의 내적값을 구해서 양수이면 보이는거고 음수이면 안보이는 것이다. B도 같은 방법을 적용할 수 있다.

Backface = (N * V < 0) = (|N| |V| cos∂ < 0) 

위의 식을 보면 ∂가 90도 이상이면 - 값이고 90도 이하면 + 값이기 때문이다. 그도 그럴것이 시선과 90도 이상 차이가 나면 당연히 보이지 않는다.

보이지 않는 면 중에서 가장 처리가 간단한 것이 후면이다. 후면 여부는 시점 위치와 해당 면만으로 판별이 가능하다. 따라서 후면 판정을 빠르게 해 미리 제거해 놓는편이 성능에 좋다.

void glEnable(GL_CULL_FACE); // 후면 제거 모드 활성화 함수
void glCullFace(GLenum mode); // 실제 후면 제거 작업을 하는 함수, 파라미터에는 GL_FRONT, GL_BACK, GL_FRONT_AND_BACK 등이 있다. 
void glDisable(GL_CULL_FACE); // 후면 제거 모드 비활성화 함수

3. 절단
: GL에서는 전 후방 절단면 외에도 최소 6개의 절단면을 추가로 정의하여 사용할 수 있다. 드라이버에 따라서는 더 많은 수의 절단면을 지원하기도 한다. 

void glGetIntegerv(GLenum pname, GLint *params);
이 함수는 현재의 그래픽 카드 드라이버가 몇개의 절단면을 지원하는지 알아내기 위한 함수다. 


위의 원구는 완전히 가시 부피 안에 있음에도 불구하고, 그림과 같이 추가의 절단 평면에 의해 원구 일부를 잘라낼 수 있다. 따라서 추가 절단면은 물체의 단면을 촬영하는데 유용하다. 
추가 절단면을 할당하기 위한 함수는 다음과 같다.

glClipPlane(GLenum plane, GLdouble *equation);
glEnable(GLenum plane);
glDisable(GLenum plane);


glClipPlane() 함수는 절단면을 선언하기 위한 것이다. plane 파라미터는 절단면의 식별자로서 GL_CLIP_PLANE0부터 GL_CLIP_PLANE5 까지 6개의 절단면을 명시할 수 있다. equation 파라미터는 평면을 나타내는 수식의 계수를 집어넣은 배열이다. 예를들어 Ax+By+Cz+D=0의 평면이면 A, B, C, D를 배열 equation[0], equation[1], equation[2], equation[3]에 차례로 넣으면 된다. 
이렇게 선언된 절단면은 glEnable(), glDisable() 함수에 의해 개별적으로 활성화되거나 비활성화 된다. 

4. 은면 제거
: 보이지 않는 면을 제거하기 위한 작업은 순서대로 진행된다. 후면 제거에 이어서 절단 좌표계에서의 절단이 일어난다. 절단이 끝나면 이제 동차 좌표에 원근 분할을 가하여 3차원 공간 좌표로 되돌린다. 이어서 이를 2차면 평면으로 투상해야 하지만 투상 이전에 은면 제거를 한다. 이 작업을 반드시 2차원 투상 이전에 해야 하는 이유는 작업의 특성상 물체 정점의 깊이, 즉 z 좌표를 필요로 하기 때문이다. 

1) 지-버퍼 알고리즘(Z-Buffer Algorithm)

: 이 알고리즘은 화소 단위로 은면을 판단한다. 
: 지-버퍼 알고리즘은 픽셀마다 하나의 광선을 발사하여 물체마다 픽셀의 z값을 계산해서 z값이 가까운 물체의 픽셀으로 색을 채워 넣는다. 
화면 해상도를 1024*768이라 하면 알고리즘의 복잡도는 1024*768*N 이다. N은 물체의 개수를 뜻한다. 
:지-버퍼(깊이 버퍼)는 은면 제거의 속도를 높이기 위해 사용되는 하드웨어 메모리를 말한다. 지-버퍼의 용량이 클수록 정밀도는 커진다. 예를들어, 화소당 8비트를 할애한 경우보다 16비트를 할애한 경우가 아주 작은 거리의 차이도 정확히 계산해서 앞뒤를 분간할 수 있다.

2) GL의 지-버퍼
void glGetIntegerV(GLenum pname, GLint *params);
: 그래픽 카드의 깊이 버퍼가 화소당 몇비트를 할애할 것인지 검색하려면 위 함수를 사용하면 된다.
: glGetIntegerv(GL_DEPTH_BITS, &Numberbits); 라고 하면 정수 변수 Numberbits에 화소당 비트수가 할당 된다. 위 함수는 일반적인 검색 함수로써, 프레임 버퍼의 비트수를 확인하는 데도 사용된다. 

void glutInitDisplayMode(unsigned int mode);
void glEnable(GL_DEPTH_TEST);
void glClear(GL_DEPTH_BUFFER_BIT);
void glDisable(GL_DEPTH_TEST);
: 먼저 glutInitDisplayMode(GLUT_DEPTH) 함수를 호출하여 새로 만들어지는 윈도우에서 깊이 버퍼 모드를 사용할 수 있도록 초기화해야 한다. 대부분의 경우 이 함수는 컬러 모드와 결합하여 glutInitDisplayMode(GLUT_DEPTH | GLUT_RGBA) 형태의 함수로 호출된다.
이어서 깊이 버퍼를 사용하려면 일단 glEnable() 함수에 의해 깊이를 비교하는 모드를 활성하해야 한다. 깊이 버퍼를 비활성화 하려면 glDisable()함수를 호출하면 된다. 
glClear() 함수는 깊이 버퍼의 내용을 초기화 하는 함수로, 기본값인 z=1.0(깊이 갚의 최대값)으로 초기화 한다. 초기화 z값을 직접 주려면 glClearDepth() 함수를 사용하면 된다. 일단 버퍼가 초기화 되면 이전에 보이던 물체의 깊이 정보는 모두 날아가 버린다. 따라서 이 함수는 새로은 프레임을 그릴 때만 호출되어야 한다. 새로운 프레임을 그릴때는 프레임 버퍼 역시 초기화 되어야 하므로 보통glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) 로 초기화 한다. 

void glDepthFunc(GLenum func);
: 깊이 테스트에 사용되는 비교 함수를 설정하는 함수다.

void glDepthMask(GLboolean flag);
: glEnable()에 의해 깊이 버퍼 모드가 활성화되면 이후로는 테스트에 의해 깊이 버퍼에 자동으로 새로운 내용이 쓰인다. 만약 프로그램 실행 도중 깊이 버퍼에 쓰기를 금지하려면 위 함수를 호출해야 한다. 파라미터 flag에 GL_TRUE를 할당하면 쓰기가 활성화되고, GL_FALSE를 할당하면 쓰기가 금지된다. 

* 후면 제거는 은면 제거보다 작업 속도가 매우 빠르다. 따라서 후면 제거를 미리 가하여 비교 대상이 되는 면의 수를 줄이면, 은면제거 속도를 높일 수 있다. 













Posted by 홍성곤
,

1. 뷰 포트 변환

위 그림 (a)에서 (b)로 가는 작업, 즉 정규화 장치 좌표계에서 화면 좌표계로 가는 작업이 뷰 포트  변환이다.
화면 좌표계(Screen Coordinate System)을 뷰 포트 좌표계(Viewport Coordinate System), 또는 윈도우 좌표계(Window Coordinate System)라 부르기도 한다.

2. 뷰 포트 설정  
void glViewport(GLint left, GLint bottom, GLsizei width, GLsizei height);

glViewport()는 뷰 포트의 위치 및 크기를 지정하는 함수로, (b)에 표시된 것처럼 left, bottom은 뷰 포트 좌하단의 좌표를 나타낸다. 이를 중심으로 width, height에 의해 뷰 포트 사각형이 정의된다. 

3. 가시 부피와 뷰 포트 
: 물체의 왜곡을 방지하기 위해서는 가시 부피의 종횡비와 뷰 포트의 종횡비를 일치시켜야 한다. 

(a)에서 가시 부피의 종횡비가 1:1 이고, 정규화 가시 부피의 종횡비가 1:1 이기 때문에 왜곡이 일어나지 않지만, 뷰 포트의 종횡비가 2:1이기 때문에 물체 왜곡이 일어난다.

(b)에서는 가시 부피의 종횡비가 2:1이고, 정규화 가시 부피의 종횡비가 1:1이기 때문에 물체 왜곡이 일어나지만, 뷰포트의 종횡비가 2:1이기 떄문에 뷰포트 에서는 물체 왜곡이 일어나지 않는다. 


Posted by 홍성곤
,

투상
: 3차원 물체를 화면으로 사상하기 위한 작업으로, 일명 가시 변환(Viewing Transformation) 이라고도 한다. 즉, 모델 좌표계, 전역 좌표계, 시점 좌표계를 순차적으로 거친 다각형 정점 좌표를 2차원 투상면으로 사상시키는 과정을 말한다. 

1. 평행투상
1) 졍규화 가시 부피에 의한 투상

- 가시부피
: 평행 투상에서, 제한된 크기의 뷰 윈도우에 모든 물체의 영상을 맺히게 할 수 없기 때문에 투상 범위를 항상 제한해야 한다. 이를 가시 부피라고 부른다. 

- void glOrtho(GLdouble left, GLdouble right, ,GLdouble bottom, GLdouble top, GLdouble near, GLdouble far)
: 평행 투상의 가시 부피 설정을 위한 함수다.
: near, far은 시점으로부터 절단면과의 거리다. 시점 좌표계는 오른손 법칙을 쓰기 때문에 near의 실제 z 좌표는 -near, far는 -far 이다.

- 가시 부피의 정규화 변환



: 가시 부피는 가로, 세로, 높이가 2인 정육면체로 투상하는 변환이다. 변환된 가시 부피를 정규화 가시 부피(Canonical View Volume)이라 한다. 
: 위 그림 에서 보듯이 변환 결과 시점 좌표계 원점은 정규화 가시 부피 정중앙에 위치한다. 여기서 유의할 점은 시점 좌표계가 오른손 법칙에서 왼속 법칙으로 바꼈다는 것이다. 
- 정규화 가시 부피를 사용하는 이유
1- 평행 투상, 원근 투상 모두 동일한 모습의 정규화 가시 부피를 사용함으로써 파이프라인 처리 구조가 동일해지기 때문이다.
2- 가시 부피 밖의 물체를 절단하는 데 있어서 정규화 가시 부피인 정육면체를 기준으로 하는 것이 훨씬 단순하기 때문이다.
3- 시점 좌표계 원점을 중심으로 가로, 세로 길이를 1로 정규화 함으로써 화면 좌표계로 변환하기가 수월해지기 때문이다.

- 정규화 변환 행렬
: 가시 부피를 물체로 간주하면, 이 물체의 중심을 위 그림 (b)에 표시된 정규화 가시 부피로 변환하기 위한 행렬은 다음과 같다.



glOrtho(left, right, bottom, top, near, far) 함수에 의해 내부적으로는 위 그림의 행렬이 현 투상 행렬로 올려지며 물체 정점에 곱해지게 된다. 

이 변환에 의해 가시 부피는 물론 가시 부피 내의 물체도 정규화 된다. 다시 말해 물체 정점 좌표가 바뀐다. 이 좌표계를 절단 좌표계(Clip Coordinate System)이라 부른다. 이는 정규화된 물체를 대상으로 이후에 가시 부피 밖의 물체에 대한 절단 작업이 가해지기 때문이다. 

2) GL의 투상
: 가시 부피를 정규화 가시 부피로 변환하고 범위를 벗어나는 물체들에 절단 작업이 가해진다. 그 후 2차원으로의 투상 작업이 가해진다. 2차원으로의 투상은 물체에 식 (7.3)의 행렬을 곱하는 것에 해당하며, 변환 결과 물체는 깊이 d=0인 투상면에 투상된다. 그러나 이 행렬을 곱하며 z값이 날아가 버리므로 이 작업은 깊이 정보를 활용한 이후로 미뤄진다.

* glMatrixMode(GL_PROJECTION); 함수로 투영 변환이라는 것을 명시
* 시스템마다 다르지만 투상 행렬 스택은 최소 2개 이상이다. 
* void glGetFloatv(GLenum pname, GLfloat *params);
: 현 투상 행렬 값 검색 함수.(pname에 "GL_PROJECTION_MATRIX" 넣어야 함, "GL_MODELVIEW_MATRiX" 넣으면 현 모델 뷰 행렬 값이 검색 됨.)

2. 원근 투상
1) 정규화 가시 부피에 의한 투상

평행 투상과 마찬가지로 GL의 원근 투상에서도 항상 투상 범위를 제한해야 한다. 



GL의 원근 투상의 가시 부피도 평행 투상과 동일한 모습의 정규화 가시 부피로 변환한다. 
일단 정규화 가시 부피로 바뀌면 GL은 평행 투상과 원근 투상을 동일한 방식으로 취급한다. 따라서 투상의 최종 단계인 3차원 정규화 가시 부피로부터 2차원 투상면으로의 투상은 단순히 z값을 무시하는 투상, 즉 평행투상이 된다. 

2) GL의 투상 함수 종류
void gluPerspective(GLdouble fov, GLdouble aspect, GLdouble near, GLdouble far);



파라미터 fov는 시야를 의미한다. 위 그림에서 보듯이 상하 y축 방향의 시야각을 나타낸 것으로 0~180도 범위를 갖는다. 
파라미터 aspect는 뷰 윈도우의 종횡비로, width/height 값이다. 물체 모습이 왜곡되는 것을 방지하려면 이 종횡비가 뷰 포트의 종횡비와 같아야 한다. 물론, x축 방향의 시야각을 fov와 종횡비에 의해 결정 된다.
파라미터 near, far는 전방, 후방 절단면의 위치값으로 항상 양수이어야 한다. 

시야각을 넓히는 것은 카메라로 말하자면 광각 렌즈를 끼우는 행위와 같다.



시야각을 좁히는 것은 망원 렌즈를 끼우는 행위와 같다. 

void glFrustum(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far);
: 파라미터 순서대로 가시 부피의 좌, 우, 상, 하, 전, 후가 정의된다.

3) 투상 파이프 라인

GL의 파이프라인 처리 순서를 설명해 보자.
1- 모델 변환
그림의 모델 행렬은 좌표계를 기준으로 하여 glScalef(), glRotatef(), glTranslatef()가 순차적으로 가해진 것이다. 이들 복합 변환을 모델 좌표계 기준의 물체 정점에 곱하면 전역 좌표계로 바뀐다. 

2- 시점 변환
gluLookAt()에 의해 할당된 뷰 행렬 V를 여기에 곱하면 시점 좌표계 기준으로 바뀐다.

3- 투상
이어서 투상 행렬을 적용해 3차원 좌표를 2차원 좌표에 투영 시킨다.

1단계 투상
: 투상 행렬을 시점 좌표에 곱하면 절단 좌표계라는 것으로 변한다. 그 후 가시 부피 바깥에 있는 부분을 절단한다.
* 절단 좌표계는 가시 부피 바깥에 있는 부분을 절단하기 쉽도록 변환된 좌표계 이다. 

2단계 투상 
: 절단 후 실제 정규 가시 부피로 투영된 3차원 좌표로 바꿔야 한다. 이 좌표를 정규화 장치 좌표계(Normalized Device Coordinates System)이라 한다. 
위 그림에서 보듯이 절단 좌표계와 정규화 장치 좌표계는 사실상 동일하다. 모두 정규화 가시 부피 내의 좌표를 정의하기 때문이다. 단지 차이점은 원근 분할을 하기 전인지, 후인지에 있다. 
원근 분할은 절단 좌표계에서 정규화 장치 좌표계로 전환하는 작업을 말한다. 

3단계 투상
: 정규화 가시 부피의 z=0 평면이 화면의 윈도우 또는 뷰 포트로 투상된다. 
3차원 정규화 장치 좌표를 2차원 뷰 포트로 투상하기 위해서는 z 성분만 없애면 되지만, 그 전에 깊이 정보가 활용되어야 한다. 





Posted by 홍성곤
,

1. 좌표계
모델 좌표계(지역 좌표계)
: 모델 좌표계는 모델 마다 존재하는 모델을 기준으로 하는 좌표계이다.

위 그림처럼 모델마다 모델 좌표계 원점이 틀리다. 연필을 표현하려면 지우개 원 중심에 모델 좌표계 원점을 그리는 것이 적합하므로 그림처럼 모델 좌표계가 설정되어 있고, 박스 같은 경우에는 좌 하단에 원점이 그려져 있다.

전역 좌표계
: 모델이 여러개일 경우 모델끼리의 상대적 위치를 결정 지을수 있는 좌표계가 전역좌표계이다. 
 
시점 좌표계(카메라 좌표계)
: 화면에 보이는 것은 결국 시점이 좌우하기 때문에 보는 사람의 위치와 바라보는 방향에 따른 시점좌표계가 존재한다. 
: 시점, 즉 카메라의 위치, 각도 라고 생각하면 편하다.


2. 변환 종류
모델 변환
: 물체에 기하 변환을 가하여 물체 모습을 변환하는 작업
: 모델 변환 행렬을 만들고 이것을 전역 좌표계에 곱해서 전역 좌표계와 모델 좌표계를 일치 시키고 전역 좌표계에서 모델 좌표를 기준으로 해당 모델을 그리는 것이 모델 변환이다. 
: 모델이 아직 그려지지 않았다고 가정하고 모델 좌표계를 변환한 후 모델을 그린다. 

시점 변환(뷰 변환)
: 변환된 물체를 관찰하기 위해 카메라를 이동하고 회전함으로써 카메라 위치와 방향을 설정.
: 전역 좌표 기준으로 그려진 모델을 관찰자의 시점으로 변환하는 것을 말한다.
: 모델 좌표에 모델 변환 행렬을 곱하면 전역 좌표가 생성 된다. 시점 변화도 마찬 가지로 전역 좌표에 시점 변환 행렬을 곱하면 시점 좌표로 변한다.

투상변환
: 카메라의 렌즈를 선택하고 촬영하여 물체의 2차원 영상을 필름에 맺히게 하는 작업.

모델 뷰 변환
: 모델 변환 + 뷰 변환

3. 모델 변환 GL 구현
void glMatrixMode (GLenum mode);
: 변환 종류 선택 함수. 모델 뷰 변환인지, 투상 변환인지, 텍스처 변환인지 등등

glLoadIdentity();
: 상태 변수를 사용하는 지엘에서는 항상 현재의 상태 변수, 즉 현 상태 변수 값이 중요하다. 
위 함수를 호출하면 현 변환 행렬이 I(항등 행렬)로 바뀌는 것이다.
즉, 모델 좌표계, 전역 좌표계, 시점 좌표계가 일치된다.

glTranslatef(dx, dy, dz);
glScalef(sx, sy, sz);
glRotatef(theta, vx, vy, vz);

: 모델 좌표계의 이동, 크기 조절, 회전을 가하는 함수. 

glPushMatrix();
glPopMatrix();

: 변환 행렬스택에 push, pop하는 함수

4. 시점 변환 GL 구현

gluLookAt(eyeX, eyeY, eyeZ, atX, atY, atZ, upX, upY, upZ);

(eyeX, eyeY, eyeZ) 위치의 카메라가 (atX, atY, atZ)를 바라보게 하라는 것이다. (upX, upY, upZ)는 카메라의 상향 벡터이다.

Posted by 홍성곤
,