가시성 판단이란?
가시성 판단이란 3차원 상에 존재하는 물체를 카메라로 촬영할 때 카메라로 촬영되는 부분(가시 부피) 외의 물체를 자르거나, 다른 물체에 의해 가려지는 물체를 자르거나, 뒷면을 자르는 등의 판단을 하는 것이다. 즉, 촬영해서는 안될 부분(가시 부피 외의 공간, 다른 물체에 가려진 부분)과 촬영하지 않아도 되는 부분(물체의 뒤쪽 면)을 판단함으로써 성능을 향상시키고 좀 더 명확한 world를 표현할 수 있게된다.
벡터
가시성 판단을 이해하기 위한 벡터의 기본 개념을 훑고 지나간다
유클리드 벡터
- 실수 공간 상에서 존재한다
- 방향과 크기만 존재하며, 시작점은 무시한다
- 그래픽스에서는 2,3차원 벡터를 다룬다
- 방향이 없이 크기만 있는 것이 스칼라(scalar)이다.
평면
- 두개의 벡터가 있으면, 두 벡터를 포함하는 평면을 계산 가능
- 두 벡터의 외적은 항상 평면과 수직임으로, 이를 사용하여 임의의 평면 표현 가능
법선 벡터
- 평면과 수직인 벡터
- 수직인 벡터는 두개 존재
평면 표현
- 임의의 평면에 대한 법선 벡터 방향을 해당 평면의 앞면(Front Face)으로 지정
- 반대 면이 뒷면(Back Face)가 된다.
GL에서 평면 방향 지정
- 법선 벡터를 따로 지정하지는 않는다
- 평면 표현시, 정점의 순서는 앞면에서 바라보는 기준으로 반시계 방향이다
- 함수(glFrontFace)를 사용하여 정점 순서 기준을 변경할 수 있다.(반시계 -> 시계)
glFrontFace(GL_CW); // 시계방향으로 그려진 걸 전면이라 한다
glFrontFace(GL_CCW); // 반시계방향으로 그려진 걸 전면이라 한다
//모델러가 어떤 방향을 기준으로 했느냐에 따라서 앞 뒷면이 정해진다.
//모델러가 시계방향으로 만들었는데, 개발자가 반시계방향을 기준으로 개발했다면 BACK과 FRONT가 반대가 된다.
후면제거
- 평면의 앞면과 뒷면은 서로 다른 존재(색상, 재질 등)
- 대부분 3차원 물체의 경우, 평면의 뒷면은 그릴 필요가 없다
GL의 후면 판단 및 제거
- 법선벡터의 z축 값으로 판단이 가능하다
- 후면제거의 시행 여부는 OpenGL 코드로 지정한다
glEnable(GL_CULL_FACE); 전/후면제거 활성화
glCullFace(GL_BACK); 후면제거
glCullFace(GL_FRONT); 전면제거
glCullFace(GL_FRONT_AND_BACK) 전면, 후면제거
glDisable(GL_CULL_FACE); 전/후면제거 비활성화
- 위 코드는 초기화시에 한 번만 호출
- 후면제거는 시점변환시에 이루어진다
앞면/뒷면을 다른 방법으로 그리기
- GL에서 다각형을 그릴 때, 앞면/뒷면에 따라 그리는 방법을 다르게(채움/선) 설정할 수 있다.
glPolygonMode(GL_FRONT, GL_FILL)
glPolygonMode(GL_BACK, GL_LINE)
절단(클리핑)
- 가시범위 밖에 있는 물체를 통째로 제거한다
- 가시범위에 걸쳐져 있는 물체는 절단하여 보이지 않는 부분은 제거한다
절단 알고리즘
- 코헨-서더런드 알고리즘
- 리앙-바스키 알고리즘
- 서더런드-핫지먼 알고리즘
- 웨일러-애서톤 알고리즘
GL의 절단
- 가시범위 밖 물체에 대해서는 별도의 함수 사용 없이 자동 절단
- 추가 절단면 사용
glClipPlane(GLenum plane, GLdouble* equation);
<인자 설명>
plane : GL_CLIP_PLANE0~5, 절단면을 6개정도 만들어 줄 수 있음
equation : 평면방정식(ax+by+cz+d=0)의 계수 4개를 배열로 보냄,이 평면으로 자름
glEnable(GLenum plane); 특정 절단면 활성화, 비활성화
glDisable(GLenum plane);
- 사용가능 절단면의 개수는 그래픽 카드에 따라 상이하다
int num
glGetIntegerv(GL_MAX_GLIP_PLANES,&num);
은면 제거
- 가려져서 보이지 않는 면 제거
- 가시부피에서의 x,y,z 좌표로 판단 가능
- 다른 물체들과의 비교 필요
페인터 알고리즘(나이브한 방식)
- z축에 따라 물체 단위로 정렬한 후 뒤쪽 물체부터 차례대로 그려나감
- 물체가 겹쳐져 있는 경우에 문제가 발생함(제일 앞의 삼각형이 무엇인지 이해하기 어려움) 그래서 등장한 것이 z버퍼 알고리즘
Z버퍼 알고리즘
- 화소 단위로 숨겨진 여부 판단
- 물체를 하나씩 그리면서 각 픽셀의 깊이를 버퍼에 저장
- 이전에 그려진 픽셀의 z버퍼값보다 작은 경우 픽셀값 변경
- 복잡하게 겹쳐진 물체의 경우에도 쉽게 판단 가능
- 알파값(투명도)이 지정된 경우에는 무시
- z버퍼의 정밀도가 낮은 경우 비슷한 위치의 물체를 정확하게 표현할 수 없음(버퍼가 픽셀당 8비트(2^8)이면 256개의 깊이 표현가능)
- 버퍼 bit 확인 함수
int num
glGetIntegerv(GL_DEPTH_BITS,&num);
- 다른 함수들
glutInitDisplayMode(GLUT_DEPTH); 깊이버퍼 모드 사용
glEnable(GL_DEPTH_TEST); 깊이버퍼 테스트(깊이 비교) 활성화
glDisable(GL_DEPTH_TEST); 깊이버퍼 테스트 비활성화
glClear(GL_DEPTH_BUFFER_BIT); 깊이버퍼 초기화
glDepthFunc(GLenum_func); 깊이 테스트 시 비교방법 설정
<인자 설명>
GL_NEVER : 보이지 않게 한다
GL_ALWAYS : 항상 보이게한다
GL_LESS : 깊이 버퍼 값이 작은걸 보이게한다
GL_EQUAL : 깊이 버퍼 값이 동일한 것을 보이게한다
GL_LEQUAL : 깊이 버퍼 값이 동일하거나 작은 경우 보이게한다
GL_GEQUAL : 깊이 버퍼 값이 동일하거나 클 경우 보이게한다
GL_NOTEQUAL : 깊이 버퍼 값이 동일하지 않은 경우 보이게한다
GL_GREATER : 깊이 버퍼 값이 더 큰걸 보이게한다(멀리 있는걸 그린다)
glDepthMask(GLboolean flag) 깊이버퍼 쓰기 금지 on/off
<인사 설명>
flag : GL_TRUE, GL_FALSE
OpenGL 실습 : Clip Plane, 후면&전면 제거
#include <OpenGL/OpenGL.h>
#include <GLUT/GLUT.h>
#include <math.h>
#include <iostream>
#define _WINDOW_WIDTH 700
#define _WINDOW_HEIGHT 700
GLfloat camPos_x = 0.5, camPos_y = 1, camPos_z = 1; //위치
GLfloat camAt_x = 0, camAt_y = 0, camAt_z = 0; //바라보는 곳
GLfloat camUp_x = 0, camUp_y = 1, camUp_z = 0; //방향
void NumberOfClipPlane(){//절단면 개수
int num;
glGetIntegerv(GL_MAX_CLIP_PLANES, &num);
std::cout << num <<std::endl;
//결과 : 6개
}
void InitLight(){
GLfloat mat_diffuse[] = {0.5, 0.4, 0.3, 1.0};
GLfloat mat_specular[] = {1.0, 1.0, 1.0, 1.0};
GLfloat mat_ambient[] = {0.5, 0.4, 0.3, 1.0};
GLfloat mat_shininess[] = {50.0};
GLfloat light_specular[] = {1.0, 1.0, 1.0, 1.0};
GLfloat light_diffuse[] = {0.8, 0.8, 0.8, 1.0};
GLfloat light_ambient[] = {0.3, 0.3, 0.3, 1.0};
GLfloat light_position[] = {-3, 2, 3.0, 0.0};
glShadeModel(GL_SMOOTH);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
}
void InitVisibility(){
//glEnable(GL_CULL_FACE); //평면
glDisable(GL_CULL_FACE);
glPolygonMode(GL_FRONT, GL_FILL);
glPolygonMode(GL_BACK, GL_LINE);
glFrontFace(GL_CW);
//정점의 순서는 앞면에서 바라보는 기준으로 반시계 방향이다
//함수(glFrontFace)를 사용하여 정점 순서 기준을 변경할 수 있다.(반시계 -> 시계)
//모델링 된 오브젝트가 어떤 방향을 앞면으로 잡았는지에 따라 다름
//glCullFace(GL_BACK);
//뒤쪽 면은 그리지 않겠다.
glEnable(GL_DEPTH_TEST); //테스트 활성화
glEnable(GL_CLIP_PLANE0);//클립 평면 중 0번 enable
}
void MyDisplay(){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(camPos_x, camPos_y, camPos_z, camAt_x, camAt_y, camAt_z, camUp_x,camUp_y, camUp_z);
//InitVisiblity에서 clip enable해주었지만
//초기화한 다음에 잘라야해서 Display로 가져옴
GLdouble eq[4] = {0,0,1,0}; // y = 0인 평면 위쪽만 살리고 아래를 자름
glClipPlane(GL_CLIP_PLANE0, eq);
glutSolidTeapot(0.2);
glutSwapBuffers();
}
void MyReshape(int width, int height){
GLfloat left = 0, bottom = 0;
glViewport(left, bottom, width-left, height-bottom);
glMatrixMode(GL_PROJECTION);//투영변환 모드 초기화
glLoadIdentity();
GLfloat ratio = (float)(width-left)/(height-bottom); //비율 값
gluPerspective(40, ratio, 0.1, 10); //원근 투영
}
void MyTimer(int value){
GLfloat Theta = 0.01;
GLfloat tmp_cam_x = camPos_x;
//모델의 y축 기준으로 카메라 회전시키기 회전공식 참고
camPos_x = camPos_x * cos(Theta) + camPos_z * sin(Theta);
camPos_z = tmp_cam_x * -sin(Theta) + camPos_z * cos(Theta);
glutTimerFunc(20, MyTimer, 1);
glutPostRedisplay();
}
int main(int argc, char** argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
glutInitWindowSize(_WINDOW_WIDTH, _WINDOW_HEIGHT);
glutInitWindowPosition(50, 50);
glutCreateWindow("Visibility Detection");
glClearColor(0,0,0,0.0);
InitLight();
InitVisibility();
glutDisplayFunc(MyDisplay);
glutReshapeFunc(MyReshape);
glutTimerFunc(20, MyTimer, 1);
NumberOfClipPlane(); //쓸 수 있는 절단면 6개
glutMainLoop();
}
'Computer Graphics' 카테고리의 다른 글
Chapter12. 에일리어싱과 안티 에일리어싱 (0) | 2021.06.07 |
---|---|
Chapter11. 래스터 변환 (0) | 2021.06.07 |
Chapter09. 투영변환(gluPerspective) & 뷰포트 변환(glViewport) (2) | 2021.06.05 |
Chapter08. 시점변환(gluLookat) (0) | 2021.06.05 |
Chapter07. GL-Viewing : 행렬&좌표계&모델 변환 (0) | 2021.06.01 |