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를 할당하면 쓰기가 금지된다.
* 후면 제거는 은면 제거보다 작업 속도가 매우 빠르다. 따라서 후면 제거를 미리 가하여 비교 대상이 되는 면의 수를 줄이면, 은면제거 속도를 높일 수 있다.
'영상 처리 프로그래밍' 카테고리의 다른 글
[컴퓨터 그래픽스] 래스터 변환(2) (0) | 2017.11.27 |
---|---|
[컴퓨터 그래픽스] 래스터 변환(1) (0) | 2017.11.27 |
[컴퓨터 그래픽스] 뷰 포트 변환 (0) | 2017.11.23 |
[컴퓨터 그래픽스] 투상 (0) | 2017.11.23 |
[컴퓨터 그래픽스] 모델변환, 시점변환 (0) | 2017.11.23 |