테 셀 레 이 션 과 이차 곡 면
테셀레이션과 이차 곡면 “ 폴리곤 테셀레이션 ” “ 이차 곡면 – 구, 원기둥, 원의 렌더링 ” 2010-2학기 가상현실
폴리곤 테셀레이션 OpenGL은 볼록한 폴리곤만 직접 표현 볼록하지 않은 폴리곤들과 구멍이 있는 폴리곤들, 모서리끼리 교차하고 있는 폴리곤들을 표현하는 프로그램을 구현할 경우 이러한 폴리곤들을 간단한 볼록한 폴리곤들로 나누어야 함 나누는 작업을 테셀레이션이라고 함 GLU는 테셀레이션을 수행할 수 있는 루틴들의 모임을 제공 2010-2학기 가상현실
폴리곤 테셀레이션 순서 gluNewTess()로 새로운 테셀레이션 오브젝트를 만든다. gluTessCallback()을 테셀레이션 하는 동안 그 연산을 수행하기 위한 함수의 콜백을 등록하기 위해 여러 번 사용 gluTessProperty()를 호출하여 테셀레이션의 특성을 지정. 가장 중요한 특성은 감기 규칙(winding rule). 하나 이상의 닫힌 폴리곤의 형태를 지정해서 테셀레이트된 폴리곤으로 만든다. 다른 것을 테셀레이트 할 때 이전에 테셀레이트 한 오브젝트를 다시 사용할 수 있다. 작업이 끝난 뒤에는 gluDeleteTess()를 사용하여 삭제. 2010-2학기 가상현실
테셀레이션 오브젝트 만들기 테셀레이션 하기 위해서 gluNewTess()를 이용하여 새로운 테셀레이션 오브젝트를 만든다. GLUtesselator* gluNewTess(void); 새로운 테셀레이션 오브젝트 만들고, 포인터를 오브젝트에 반환 새로운 테셀레이션 오브젝트가 생성되지 않았다면 null값을 반환 2010-2학기 가상현실
테셀레이션 콜백 루틴 테셀레이션 오브젝트를 생성한 후에는 테셀레이션 동안에 적절한 시점마다 호출되는 일련의 콜백 루틴을 만든다. 콜백 지정 후에는 GLU 루틴을 이용, 폴리곤의 윤곽선들을 그린다. 어떤 생략된 함수들은 테셀레이션 동안 호출 안됨. 그 함수들이 가지고 있던 프로그램에 반환되는 데이터들은 삭제 gluTessCallback()으로 지정 void gluTessCallback(GLUtesselator *tessobj, Glenum type, void *(fn)()); 콜백함 수 fn을 테셀레이션 tessobj와 연결, 타입은 다음의 type에 의해 지정: GLU_TESS_BEGIN, GLU_TESS_BEGIN_DATA, GLU_TESS_EDGE_FLAG, GLU_TESS_EDGE_FLAG_DATA, GLU_TESS_VERTEX, GLU_TESS_VERTEX_DATA, GLU_TESS_END, GLU_TESS_END_DATA, GLU_TESS_COMBINE, GLU_TESS_COMBINE_DATA, GLU_TESS_ERROR, GLU_TESS_ERROR_DATA 2010-2학기 가상현실
테셀레이션이 진행되는 동안 콜백 루틴들이 호출된다. OpenGL 커맨드들(glBegin(), glVertex*(), glEdgeFlag*(), glEnd()) 을 사용하는 것과 비슷 이 콜백 조합들은 모서리들의 새로운 교차점을 생성하기 위해 사용된다. 예) tobj = gluNewTess(); gluTessCallback(tobj, GLU_TESS_VERTEX, glVertex3dv); gluTessCallback(tobj, GLU_TESS_BEGIN, beginCallback); gluTessCallback(tobj, GLU_TESS_END, endCallback); gluTessCallback(tobj, GLU_TESS_ERROR, errorCallback); 모든 테셀레이션 오브젝트들이 생성되기 위해 GLU_TESS_BEGIN 콜백이 다음 네 개의 변수 GL_TRIANGLE_FAN, GL_TRIANGLE_STRIP, GL_TRIANGLES, GL_LINE_LOOP 중 하나와 함께 호출된다. GLU_TESS_BOUNDARY_ONLY 특성이 활성화되어 있다면 GL_LINE_LOOP 이 사용되어야 함 GLU_TESS_EDGE_FLAG와 연관된 콜백이 있다면 GLU_TESS_BEGIN 콜백은 오직 GL_TRIANGLES 와 함께 호출된다. 2010-2학기 가상현실
뭔가 잘못되면 에러 콜백은 GLU 에러 숫자로 넘겨짐. GLU_TESS_BEGIN이 호출되고 난 뒤와 GLU_TESS_END_VERTEX와 연관된 콜백이 호출되기 전, GLU_TESS_EDGE_FLAG 와 GLU_TESS_VERTEX 의 콜백 조합들이 호출된다. 뭔가 잘못되면 에러 콜백은 GLU 에러 숫자로 넘겨짐. 2010-2학기 가상현실
매개변수에 대한 콜백함수들의 원형 GLU_TESS_BEGIN void begin(GLenum type); GLU_TESS_BEGIN_DATA void begin(GLenum type, void *user_data); GLU_TESS_EDGE_FLAG void edgeFlag(GLboolean flag); GLU_TESS_EDGE_FLAG_DATA void edgeFlag(GLboolean flag, void *user_data); GLU_TESS_VERTEX void vertex(void *vertex_data); GLU_TESS_VERTEX_DATA void vertex(void *vertex_data, void *user_data); GLU_TESS_END void end(void); 2010-2학기 가상현실
void end(GLenum type); GLU_TESS_END_DATA void end(GLenum type); GLU_TESS_COMBINE void combine(GLdouble coords[3], void *vertex_data[4], GLfloat weight[4], void **outData); GLU_TESS_COMBINE_DATA void combine(GLdouble coords[3], void *vertex_data[4], GLfloat weight[4], void **outData, void *user_data); GLU_TESS_ERROR void error(GLenum errno); GLU_TESS_ERROR_DATA void error(GLenum errno, void *user_data); 2010-2학기 가상현실
테셀레이션 콜백의 등록: tess.c (1) 테셀레이션 오브젝트 생성과 여러 콜백들을 등록 void CALLBACK beginCallback(GLenum which) { glBegin(which); } void CALLBACK errorCallback(GLenum errorCode) const GLubyte *estring; estring = gluErrorString(errorCode); fprintf(stderr, "Tessellation Error: %s\n", estring); exit(0); 2010-2학기 가상현실
테셀레이션 콜백의 등록: tess.c (2) void CALLBACK endCallback(void) { glEnd(); } void CALLBACK vertexCallback(GLvoid *vertex) const GLdouble *pointer; pointer = (GLdouble *) vertex; glColor3dv(pointer+3); glVertex3dv(vertex); 2010-2학기 가상현실
테셀레이션 콜백의 등록: tess.c (3) tobj = gluNewTess(); gluTessCallback(tobj, GLU_TESS_VERTEX, glVertex3dv); gluTessCallback(tobj, GLU_TESS_BEGIN, beginCallback); gluTessCallback(tobj, GLU_TESS_END, endCallback); gluTessCallback(tobj, GLU_TESS_ERROR, errorCallback); Microsoft Windows에서 실행하려면 콜백을 선언하는 프로그램 중에서 함수의 선언부에 CALLBACK 이라는 심볼을 넣어야 한다. #ifdef CALLBACK #define CALLBACK #endif void CALLBACK callbackFunction(…) { … } 2010-2학기 가상현실
정점과 콜백 조합: tess.c (1) /* combineCallback is used to create a new vertex when edges * intersect. coordinate location is trivial to calculate, * but weight[4] may be used to average color, normal, or texture * coordinate data. In this program, color is weighted. */ void CALLBACK combineCallback(GLdouble coords[3], GLdouble *vertex_data[4], GLfloat weight[4], GLdouble **dataOut ) { GLdouble *vertex; int i; vertex = (GLdouble *) malloc(6 * sizeof(GLdouble)); 2010-2학기 가상현실
정점과 콜백 조합: tess.c (2) vertex[0] = coords[0]; vertex[1] = coords[1]; for (i = 3; i < 6; i++) vertex[i] = weight[0] * vertex_data[0][i] + weight[1] * vertex_data[1][i] + weight[2] * vertex_data[2][i] + weight[3] * vertex_data[3][i]; *dataOut = vertex; } 테셀레이션 알고리즘이 입력된 윤곽선을 검사하고, 교차점을 찾고, 새로운 정점을 생성하고자 할 때 마다 GLU_TESS_COMBINE 콜백 호출 테셀레이터가 아주 가까이 위치한 두 정점을 합치려고 할 때도 콜백 호출 새롭게 만들어진 정점은 vertex_data[0..3]으로 참조된 네 개의 존재하는 정점의 선형 조합이다. 선형조합의 상수들은 weight[0..3] 으로 주어지며 이 가중치의 합은 1.0 2010-2학기 가상현실
사용자 지정 데이터 여섯 가지의 콜백들이 등록될 수 있는데, 종류마다 콜백은 두 가지 버전이 있으므로 모두 12개의 콜백이 존재 사용자 지정 데이터는 gluTessBeginPolygon()을 구현하여 구할 수 있으며 *DATA 콜백 루틴에서 통과되며, 불변한다. GLU_TESS_BEGIN_DATA로 사용자 지정 데이터는 각각의 폴리곤 자료로 사용될 수 있다. 만약 사용자가 특정한 콜백 버전을 지정하면 user_data의 콜백은 사용되며, 다른 것은 거부됨 12 개의 콜백이 있지만 사용자는 동시에 최대 6개의 콜백 함수를 사용 할 수 있다. 2010-2학기 가상현실
테셀레이션 특성 테셀레이션과 렌더링에서는 테셀레이션 알고리즘에 영향을 미칠 몇 가지 특성을 설정하기 위해 gluTessProperty() 사용 여기서 가장 중요하고 복잡한 성질은 감기 규칙으로, 내부인지 외부인지 판별한다. void gluTessProperty(GLUtesselator *tessobj, GLenum property, GLdouble value); 테셀레이션 오브젝트인 tessobj에서, 현재의 property값은 value로 설정된다. property: GLU_TESS_BOUNDARY_ONLY, GLU_TESS_TOLERANCE, GLU_TESS_WINDING_RULE property가 GLU_TESS_BOUNDARY_ONLY이면 value는 GL_TRUE 혹은 GL_FALSE GL_TRUE이면 폴리곤들은 더 이상 닫힌 폴리곤으로 테셀레이드 되지 않는다. 폴리곤의 내부와 외부를 분리하는 윤곽선의 바깥쪽에 반복되는 선이 그려진다. 디폴트는 GL_FALSE 2010-2학기 가상현실
property가 GLU_TESS_TOLERANCE이면 value는 GL_TRUE 혹은 GL_FALSE(기본값) value는 두 개의 정점이 GLU_TESS_COMBINE 콜백으로 합병될 수 있는지를 계산할 때 사용되는 거리가 된다. 여기서는 입력된 정점의 최대좌표길이를 한계값에 곱한 값을 특징값으로 이용 property가 GLU_TESS_WINDING 이면 폴리곤의 어느 부분이 내부이고, 채워지면 안되는 외부인지를 판정한다. value의 값은 GLU_TESS_WINDING_ODD(디폴트), GLU_TESS_WINDING_NONZERO, GLU_TESS_WINDING_POSITIVE, GLU_TESS_WINDING_NEGATIVE, GLU_TESS_WINDING_ABS_GEQ_TWO 2010-2학기 가상현실
감기 숫자와 감기 규칙 단일 윤곽선에서 정점의 감기 숫자는 윤곽선을 따라 회전하는 방향에 따라서 매기는 부호 있는 숫자이다. 반시계 방향의 회전은 양이고 시계 방향일 때는 음 윤곽선들이 여러 개 있을 때 각 감기 숫자는 더해진다. 이 과정에서 각기 같은 평면의 점마다 부호 있는 정수값이 매겨진다. 감기 규칙은 감기 숫자가 선택한 분류 안(홀수, 음수, 양수, 0보다 큰 수, 2보다 크거나 같은 절대값)에 포함될 때 영역을 내부로 분류한다. 홀수와 0보다 큰 수의 규칙은 내부의 정의와 동일한 방법이다. 양수, 음수, “절대값 2” 감기 규칙은 폴리곤 CSG(Computational Solid Geometry) 연산의 사용이 제한된다. 2010-2학기 가상현실
샘플 윤곽선들에 대한 감기 숫자: tesswind.c 1 1 1 2 1 1 2 3 -1 1 1 2010-2학기 가상현실
2010-2학기 가상현실
감기 규칙의 효과: tesswind.c /* * tesswind.c * This program demonstrates the winding rule polygon * tessellation property. Four tessellated objects are drawn, * each with very different contours. When the w key is pressed, * the objects are drawn with a different winding rule. */ #include <GL/glut.h> #include <stdlib.h> #include <stdio.h> #ifndef CALLBACK #define CALLBACK #endif GLdouble currentWinding = GLU_TESS_WINDING_ODD; int currentShape = 0; 2010-2학기 가상현실
/* Make four display lists, GLUtesselator *tobj; GLuint list; /* Make four display lists, * each with a different tessellated object. */ void makeNewLists (void) { int i; static GLdouble rects[12][3] = {50.0, 50.0, 0.0, 300.0, 50.0, 0.0, 300.0, 300.0, 0.0, 50.0, 300.0, 0.0, 100.0, 100.0, 0.0, 250.0, 100.0, 0.0, 250.0, 250.0, 0.0, 100.0, 250.0, 0.0, 150.0, 150.0, 0.0, 200.0, 150.0, 0.0, 200.0, 200.0, 0.0, 150.0, 200.0, 0.0}; 2010-2학기 가상현실
static GLdouble spiral[16][3] = {400.0, 250.0, 0.0, 400.0, 50.0, 0.0, 50.0, 50.0, 0.0, 50.0, 400.0, 0.0, 350.0, 400.0, 0.0, 350.0, 100.0, 0.0, 100.0, 100.0, 0.0, 100.0, 350.0, 0.0, 300.0, 350.0, 0.0, 300.0, 150.0, 0.0, 150.0, 150.0, 0.0, 150.0, 300.0, 0.0, 250.0, 300.0, 0.0, 250.0, 200.0, 0.0, 200.0, 200.0, 0.0, 200.0, 250.0, 0.0}; static GLdouble quad1[4][3] = {50.0, 150.0, 0.0, 350.0, 150.0, 0.0, 350.0, 200.0, 0.0, 50.0, 200.0, 0.0}; static GLdouble quad2[4][3] = {100.0, 100.0, 0.0, 300.0, 100.0, 0.0, 300.0, 350.0, 0.0, 100.0, 350.0, 0.0}; static GLdouble tri[3][3] = {200.0, 50.0, 0.0, 250.0, 300.0, 0.0, 150.0, 300.0, 0.0}; 2010-2학기 가상현실
gluTessProperty(tobj, GLU_TESS_WINDING_RULE, currentWinding); glNewList(list, GL_COMPILE); gluTessBeginPolygon(tobj, NULL); gluTessBeginContour(tobj); for (i = 0; i < 4; i++) gluTessVertex(tobj, rects[i], rects[i]); gluTessEndContour(tobj); for (i = 4; i < 8; i++) for (i = 8; i < 12; i++) gluTessEndPolygon(tobj); glEndList(); 2010-2학기 가상현실
glNewList(list+1, GL_COMPILE); gluTessBeginPolygon(tobj, NULL); gluTessBeginContour(tobj); for (i = 0; i < 4; i++) gluTessVertex(tobj, rects[i], rects[i]); gluTessEndContour(tobj); for (i = 7; i >= 4; i--) for (i = 11; i >= 8; i--) gluTessEndPolygon(tobj); glEndList(); 2010-2학기 가상현실
glNewList(list+2, GL_COMPILE); gluTessBeginPolygon(tobj, NULL); gluTessBeginContour(tobj); for (i = 0; i < 16; i++) gluTessVertex(tobj, spiral[i], spiral[i]); gluTessEndContour(tobj); gluTessEndPolygon(tobj); glEndList(); glNewList(list+3, GL_COMPILE); for (i = 0; i < 4; i++) gluTessVertex(tobj, quad1[i], quad1[i]); gluTessVertex(tobj, quad2[i], quad2[i]); 2010-2학기 가상현실
gluTessBeginContour(tobj); for (i = 0; i < 3; i++) gluTessVertex(tobj, tri[i], tri[i]); gluTessEndContour(tobj); gluTessEndPolygon(tobj); glEndList(); } void display (void) { glClear(GL_COLOR_BUFFER_BIT); glColor3f(1.0, 1.0, 1.0); glPushMatrix(); glCallList(list); glTranslatef(0.0, 500.0, 0.0); glCallList(list+1); glTranslatef(500.0, -500.0, 0.0); glCallList(list+2); glCallList(list+3); 2010-2학기 가상현실
void CALLBACK beginCallback(GLenum which) { glBegin(which); glPopMatrix(); glFlush(); } void CALLBACK beginCallback(GLenum which) { glBegin(which); void CALLBACK errorCallback(GLenum errorCode) const GLubyte *estring; estring = gluErrorString(errorCode); fprintf(stderr, "Tessellation Error: %s\n", estring); exit(0); 2010-2학기 가상현실
void CALLBACK endCallback(void) { glEnd(); } /* combineCallback is used to create a new vertex when edges * intersect. coordinate location is trivial to calculate, * but weight[4] may be used to average color, normal, or texture * coordinate data. */ void CALLBACK combineCallback(GLdouble coords[3], GLdouble *data[4], GLfloat weight[4], GLdouble **dataOut ) GLdouble *vertex; vertex = (GLdouble *) malloc(3 * sizeof(GLdouble)); 2010-2학기 가상현실
glShadeModel(GL_FLAT); tobj = gluNewTess(); vertex[0] = coords[0]; vertex[1] = coords[1]; vertex[2] = coords[2]; *dataOut = vertex; } void init(void) { glClearColor(0.0, 0.0, 0.0, 0.0); glShadeModel(GL_FLAT); tobj = gluNewTess(); gluTessCallback(tobj, GLU_TESS_VERTEX, glVertex3dv); gluTessCallback(tobj, GLU_TESS_BEGIN, beginCallback); gluTessCallback(tobj, GLU_TESS_END, endCallback); 2010-2학기 가상현실
gluTessCallback(tobj, GLU_TESS_ERROR, errorCallback); gluTessCallback(tobj, GLU_TESS_COMBINE, combineCallback); list = glGenLists(4); makeNewLists(); } void reshape(int w, int h) { glViewport(0, 0, (GLsizei) w, (GLsizei) h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if (w <= h) gluOrtho2D(0.0, 1000.0, 0.0, 1000.0 * (GLdouble)h/(GLdouble)w); else 2010-2학기 가상현실
gluOrtho2D(0.0, 1000.0 * (GLdouble)w/(GLdouble)h, 0.0, 1000.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void keyboard(unsigned char key, int x, int y) { switch (key) { case 'w': case 'W': if (currentWinding == GLU_TESS_WINDING_ODD) currentWinding = GLU_TESS_WINDING_NONZERO; else if (currentWinding == GLU_TESS_WINDING_NONZERO) currentWinding = GLU_TESS_WINDING_POSITIVE; else if (currentWinding == GLU_TESS_WINDING_POSITIVE) currentWinding = GLU_TESS_WINDING_NEGATIVE; 2010-2학기 가상현실
else if (currentWinding == GLU_TESS_WINDING_NEGATIVE) currentWinding = GLU_TESS_WINDING_ABS_GEQ_TWO; else if (currentWinding == GLU_TESS_WINDING_ABS_GEQ_TWO) currentWinding = GLU_TESS_WINDING_ODD; makeNewLists(); glutPostRedisplay(); break; case 27: exit(0); default: } 2010-2학기 가상현실
int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowSize(500, 500); glutCreateWindow(argv[0]); init(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutKeyboardFunc(keyboard); glutMainLoop(); return 0; } 2010-2학기 가상현실
2010-2학기 가상현실
감기 규칙을 위한 CSG(Constructive Solid Geometry)의 사용 GLU_TESS_WINDING_ODD와 GLU_TESS_WINDING_NONZERO는 가장 일반적으로 사용되는 감기 규칙, 쉐이딩을 가장 전형적으로 작업한다. 감기 규칙은 CSG 연산을 위해 그 연산이 몇 가지의 윤곽선들의 결합, 차이 또는 교차점을 쉽게 찾을 수 있도록 설계되어 있다. 각각의 윤곽선들이 외부 영역에서 0의 감기 숫자를 가지고, 내부에서는 1을 가지도록 정의됨. 반시계 방향의 윤곽선들은 폴리곤의 외부 경계로, 시계 방향의 곡선들은 구멍으로 판별된다. 2010-2학기 가상현실
기존 형태의 두 가지 이상의 폴리곤이 주어졌을 때 CSG연산은 다음과 같은 기능을 가짐 결합 – 몇 가지의 윤곽선들의 결합을 계산하기 위해 모든 입력된 윤곽선들을 하나의 폴리곤인 것처럼 그림 GLU_TESS_WINDING_NONZERO나 GLU_TESS_WINDING_POSITIVE 감기규칙을 이용하여 찾아낼 수 있다. 교차 – 이것은 한번에 두 개의 윤곽선에만 적용 두 윤곽선을 이용하여 하나의 폴리곤을 그린다. GLU_TESS_WINDING_ABS_GEQ_TWO를 사용 차이 – 사용자가 A의 차이(B, C, D의 합집합)를 계산하는 경우 GLU_TESS_WINDING_POSITIVE 감기 규칙 사용 2010-2학기 가상현실
다른 테셀레이션 특성 루틴 gluTessProperty()와 나란히 작동하는 보조 루틴들이 있다. gluGetTessProperty()는 테셀레이터 특성의 현재 값을 검색 gluTessNormal()은 테셀레이트된 폴리곤의 감기 방향을 조절하는 법선 정의 void gluGetTessProperty(GLUtesselator *tessobj, GLenum property, GLdouble *value); void gluTessNormal(GLUtesselator *tessobj, GLdouble x, GLdouble y, GLdouble z); 2010-2학기 가상현실
폴리곤의 정의 모든 테셀레이션의 특성이 정해지고, 콜백 동작들이 등록된 다음에 마지막으로 입력 윤곽선을 구성하고, 폴리곤을 테셀레이트 하는 정점을 정의 void gluTessBeginPolygon(GLUtesselator *tessobj, void *user_data); void gluTessEndPolygon(GLUtessellator *tessobj); 위 두 루틴의 호출은 하나 이상의 윤곽선에 대한 정의로 이루어져 있다. 콜백 함수들과 테셀레이션 특성들이 gluTessBeginPolygon()과 gluTessEndPolygon()으로 경계지어지고, 테셀레이션 오브젝트를 설정 2010-2학기 가상현실
폴리곤의 일부인 닫혀진 윤곽선에 대한 지시를 시작하고 끝냄. void gluTessBeginCountour(GLUtesselator *tessobj); void gluTessEndCountour(GLUesselator *tessobj); 윤곽선을 생성하기 위해서는 최소한 세 개의 정점이 필요 void gluTessVertex(GLUtesselator *tessobj, Gldouble coords[3] , void *vertex_data); 테셀레이트 오브젝트에서 현재의 윤곽선 안에 있는 정점을 명시 coords는 3차원 정점 좌표 포함 vertex_data 는 GLU_TESS_VERTEX나 GLU_TESS_VERTEX_DATA와 관련된 콜백 포인터로, 정점 좌표 표면의 법선, 텍스처 좌표, 컬러 정보나 구현시 유용한 것들을 포함 2010-2학기 가상현실
예제 11-3 폴리곤 정의: tess.c #include <GL/glut.h> #include <stdlib.h> #include <stdio.h> #ifndef CALLBACK #define CALLBACK #endif GLuint startList; void display (void) { glClear(GL_COLOR_BUFFER_BIT); glColor3f(1.0, 1.0, 1.0); glCallList(startList); glCallList(startList + 1); glFlush(); } 2010-2학기 가상현실
void CALLBACK beginCallback(GLenum which) { glBegin(which); } void CALLBACK errorCallback(GLenum errorCode) const GLubyte *estring; estring = gluErrorString(errorCode); fprintf(stderr, "Tessellation Error: %s\n", estring); exit(0); 2010-2학기 가상현실
void CALLBACK endCallback(void) { glEnd(); } void CALLBACK vertexCallback(GLvoid *vertex) const GLdouble *pointer; pointer = (GLdouble *) vertex; glColor3dv(pointer+3); glVertex3dv(vertex); 2010-2학기 가상현실
void CALLBACK combineCallback(GLdouble coords[3], GLdouble *vertex_data[4], GLfloat weight[4], GLdouble **dataOut ) { GLdouble *vertex; int i; vertex = (GLdouble *) malloc(6 * sizeof(GLdouble)); vertex[0] = coords[0]; vertex[1] = coords[1]; vertex[2] = coords[2]; 2010-2학기 가상현실
vertex[i] = weight[0] * vertex_data[0][i] for (i = 3; i < 6; i++) vertex[i] = weight[0] * vertex_data[0][i] + weight[1] * vertex_data[1][i] + weight[2] * vertex_data[2][i] + weight[3] * vertex_data[3][i]; *dataOut = vertex; } void init (void) { GLUtesselator *tobj; 2010-2학기 가상현실
GLdouble rect[4][3] = {50.0, 50.0, 0.0, 200.0, 50.0, 0.0, 200.0, 200.0, 0.0, 50.0, 200.0, 0.0}; GLdouble tri[3][3] = {75.0, 75.0, 0.0, 125.0, 175.0, 0.0, 175.0, 75.0, 0.0}; GLdouble star[5][6] = {250.0, 50.0, 0.0, 1.0, 0.0, 1.0, 325.0, 200.0, 0.0, 1.0, 1.0, 0.0, 400.0, 50.0, 0.0, 0.0, 1.0, 1.0, 250.0, 150.0, 0.0, 1.0, 0.0, 0.0, 400.0, 150.0, 0.0, 0.0, 1.0, 0.0}; 2010-2학기 가상현실
startList = glGenLists(2); tobj = gluNewTess(); glClearColor(0.0, 0.0, 0.0, 0.0); startList = glGenLists(2); tobj = gluNewTess(); gluTessCallback(tobj, GLU_TESS_VERTEX, glVertex3dv); gluTessCallback(tobj, GLU_TESS_BEGIN, beginCallback); gluTessCallback(tobj, GLU_TESS_END, endCallback); gluTessCallback(tobj, GLU_TESS_ERROR, errorCallback); 2010-2학기 가상현실
glNewList(startList, GL_COMPILE); glShadeModel(GL_FLAT); gluTessBeginPolygon(tobj, NULL); gluTessBeginContour(tobj); gluTessVertex(tobj, rect[0], rect[0]); gluTessVertex(tobj, rect[1], rect[1]); gluTessVertex(tobj, rect[2], rect[2]); gluTessVertex(tobj, rect[3], rect[3]); gluTessEndContour(tobj); gluTessVertex(tobj, tri[0], tri[0]); gluTessVertex(tobj, tri[1], tri[1]); gluTessVertex(tobj, tri[2], tri[2]); 2010-2학기 가상현실
gluTessEndPolygon(tobj); glEndList(); gluTessCallback(tobj, GLU_TESS_VERTEX, vertexCallback); gluTessCallback(tobj, GLU_TESS_BEGIN, beginCallback); gluTessCallback(tobj, GLU_TESS_END, endCallback); gluTessCallback(tobj, GLU_TESS_ERROR, errorCallback); gluTessCallback(tobj, GLU_TESS_COMBINE, combineCallback); 2010-2학기 가상현실
glNewList(startList + 1, GL_COMPILE); glShadeModel(GL_SMOOTH); gluTessProperty(tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE); gluTessBeginPolygon(tobj, NULL); gluTessBeginContour(tobj); gluTessVertex(tobj, star[0], star[0]); gluTessVertex(tobj, star[1], star[1]); gluTessVertex(tobj, star[2], star[2]); gluTessVertex(tobj, star[3], star[3]); gluTessVertex(tobj, star[4], star[4]); gluTessEndContour(tobj); 2010-2학기 가상현실
gluTessEndPolygon(tobj); glEndList(); gluDeleteTess(tobj); } void reshape (int w, int h) { glViewport(0, 0, (GLsizei) w, (GLsizei) h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0, (GLdouble) w, 0.0, (GLdouble) h); 2010-2학기 가상현실
void keyboard(unsigned char key, int x, int y) { switch (key) { case 27: exit(0); break; } 2010-2학기 가상현실
int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowSize(500, 500); glutCreateWindow(argv[0]); init(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutKeyboardFunc(keyboard); glutMainLoop(); return 0; } 2010-2학기 가상현실
2010-2학기 가상현실
테셀레이션 오브젝트 삭제 테셀레이션 오브젝트가 더 이상 필요하지 않다면 그것을 지우고 메모리를 해제할 수 있다. void gluDeleteTess(GLUtesselator *tessobj); 지정된 테세렐이션 오브젝트인 tessobj를 삭제, 메모리 해제 2010-2학기 가상현실
테셀레이션 성능 팁 화면 표시 리스트나 다른 사용자 구조에 테셀레이터의 출력값을 저장 폴리곤 법선을 제공하기 위해 gluTessNormal()을 사용 각각의 폴리곤에 새로운 하나의 테셀레이터를 배정하기보다 많은 폴리곤들에 제공하기 위해 동일한 테셀레이터를 사용 2010-2학기 가상현실
GLU는 에러 코드의 표현 가능한 문자열을 포함하는 루틴을 제공 이 루틴은 테셀레이션에 제한되지 않고 NURBS와 quadric적인 에러에도 사용, GL의 기본적인 에러에도 적용됨 2010-2학기 가상현실
이차 곡면 – 구, 원기둥, 원의 렌더링 기본 OpenGL 라이브러리는 단순한 점, 선 그리고 폴리곤의 볼록면의 설계와 제작만을 제공 구, 실린더, 디스크, 부채꼴 등 이차방정식으로 계산되는 2D, 3D 오브젝트 (2차 곡면) 생성을 위해 GLU 루틴 제공 이차 곡면의 제작은 테셀레이터를 사용하는 것과 비슷 이차 곡면 오브젝트를 사용하기 위해서는 다음의 순서를 따름 곡면 오브젝트를 만들기 위해 gluNewQuadric()을 사용 곡면 오브젝트의 제작 형태를 지정 감기 방향과 내, 외부의 구분을 제어하기 위해 gluQuadricOrientation()을 사용 점, 선, 또는 채워진 폴리곤과 같은 오브젝트들을 렌더링 하기 위해 gluQuadricDrawStyle()을 사용 2010-2학기 가상현실
텍스처 이차 곡면 오브젝트에서 사용자가 텍스처 좌표를 산출하고 싶다면 gluQuadricTexture()를 사용 조명된 이차 곡면 오브젝트에서 하나의 벡터 단위 법선 또는 표면단위 법선을 지정하기 위해 gluQuadricNormals()를 사용한다. 텍스처 이차 곡면 오브젝트에서 사용자가 텍스처 좌표를 산출하고 싶다면 gluQuadricTexture()를 사용 gluQuadricCallback()과 함께 에러-조절 루틴을 기록하여 문제에 대비 이차 곡면 오브젝트의 원하는 타입의 렌더링 루틴을 호출 gluSphere(), gluCylinder(), gluDisk(), gluPartialDisk() 고정된 데이터의 최상의 실행을 위하여 이차곡면 오브젝트를 디스플레이 리스트에 캡슐화 수행이 끝나면, gluDeleteQuadric()을 이용하여 이 오브젝트를 삭제한다. 2010-2학기 가상현실
이차 곡면 오브젝트 다루기 이차 곡면 오브젝트는 GLUquadricObj 타입의 자료 구조에 저장되는 변수, 속성, 콜백으로 구성된다. 이차 곡면 오브젝트는 정점, 노멀, 텍스처좌표, 그리고 다른 자료들을 생성한다. GLUquadricObj *gluNewQuadric(void); 새로운 이차 곡면 오브젝트를 만들고, 포인터에 그것을 반환 void gluDeleteQuadric(GLUquadricObj *qobj); 이차 곡면 오브젝트 qobj를 삭제 사용되었던 메모리를 해제 void gluQuadricCallback(GLUquadricObj *qobj, GLenum which, void (*fn)()); 특수한 경우에 호출되는 fn함수를 정의 2010-2학기 가상현실
곡면 특성 제어하기 다음의 루틴들은 이차 곡면 루틴에서 만들어지는 자료의 종류에 영향을 준다. void gluQuadricDrawStyle(GLUquadricObj *qobj, GLenum drawStyle); 이차 곡면 오브젝트 qobj, drawStyle을 사용하여 제작 스타일 조절 drawStyle의 허용값: GLU_POINT, GLU_LINE, GLU_SILHOUETTE, GLU_FILL GLU_SILHOUETTE: 프리미티브가 선으로 렌더링되도록 지정, 동일 면을 구분해주는 모서리는 제외됨. gluDisk(), gluPartialDisk()에서 많이 사용 void gluQuadricOrientation(GLUquadricObj *qobj, GLenum orientation); 이차곡면 오브젝트 qobj, orientation 은 GLU_OUTSIDE(디폴트) 또는 GLU_INSIDE인데, 이것들은 법선이 가리키는 방향 제어 2010-2학기 가상현실
void gluQuadricNormals(GLUquadriObj *qobj, GLenum normals); 이차 곡면 오브젝트 qobj, normals는 GLU_NONE(디폴트), GLU_FLAT, 또는 GLU_SMOOTH이다. 이 루틴은 법선 벡터의 생성시기를 결정 void gluQuadricTexture(GLUquadricObj *qobj, GLboolean textureCoords); 이차 곡면 오브젝트 qobj를 위해 textureCoords는 GL_FALSE(디폴트)나 GL_TRUE이다. 2010-2학기 가상현실
이차 곡면 프리미티브 다음의 루틴은 이차 곡면 오브젝트를 구성하는 정점과 다른 데이터를 일반화 void gluSphere(GLUquadricObi *qobj, GLdouble radius, GLint slices, GLint stacks); (0,0,0) 을 중심으로 하는 주어진 radius 의 구를 그린다. 구는 z축을 중심으로 slices의 수(경도와 유사)와 z축을 중심으로 한 stack으로(위도와 비슷) 나누어진다. void gluCylinder(GLUquadricObi *qobj, GLdouble baseRadius, GLdouble topRadius, GLdouble height, GLint slices, GLint stacks); Z축을 중심으로 한 아래를 z = 0으로 하고 위를 z = height 로 하는 원기둥을 그린다. 2010-2학기 가상현실
원은 z축 주변의 slices의 수와 z축 주변의 동심원형의 rings 의 수로 나누어짐 void gluDisk(GLUquadricObj *qobj, GLdouble innerRadius, GLdouble outerRadius, GLint slices, GLint rings); 이 루틴은 innerRadius의 반지름으로 그려지는 내부 원과 평면에서 outerRadius 반지름으로 그려지는 외부 원이 동심원 형태로 배열되도록 그려줌 원은 z축 주변의 slices의 수와 z축 주변의 동심원형의 rings 의 수로 나누어짐 void gluPartialDisk(GLuquadricObj *qobj, GLdouble innerRadius, GLdouble outerRadius, GLint slices, GLint rings, GLdouble startAngle, GLdouble sweepAngle); 이 루틴은 z = 0의 평면의 일부를 그린다. startAngle에서부터 start + sweepAngle을 통해 부채꼴의 비율이 그려짐 2010-2학기 가상현실
예제 11-4 이차 곡면 오브젝트들: quadric.c #include <GL/glut.h> #include <stdio.h> #include <stdlib.h> #ifndef CALLBACK #define CALLBACK #endif GLuint startList; 2010-2학기 가상현실
void CALLBACK errorCallback(GLenum errorCode) { const GLubyte *estring; estring = gluErrorString(errorCode); fprintf(stderr, "Quadric Error: %s\n", estring); exit(0); } void init(void) GLUquadricObj *qobj; GLfloat mat_ambient[] = { 0.5, 0.5, 0.5, 1.0 }; GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 }; 2010-2학기 가상현실
GLfloat mat_shininess[] = { 50.0 }; GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 }; GLfloat model_ambient[] = { 0.5, 0.5, 0.5, 1.0 }; glClearColor(0.0, 0.0, 0.0, 0.0); glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess); glLightfv(GL_LIGHT0, GL_POSITION, light_position); glLightModelfv(GL_LIGHT_MODEL_AMBIENT, model_ambient); 2010-2학기 가상현실
glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_DEPTH_TEST); startList = glGenLists(4); qobj = gluNewQuadric(); gluQuadricCallback(qobj, GLU_ERROR, errorCallback); gluQuadricDrawStyle(qobj, GLU_FILL); gluQuadricNormals(qobj, GLU_SMOOTH); glNewList(startList, GL_COMPILE); gluSphere(qobj, 0.75, 15, 10); glEndList(); 2010-2학기 가상현실
gluQuadricDrawStyle(qobj, GLU_FILL); gluQuadricNormals(qobj, GLU_FLAT); glNewList(startList+1, GL_COMPILE); gluCylinder(qobj, 0.5, 0.3, 1.0, 15, 5); glEndList(); gluQuadricDrawStyle(qobj, GLU_LINE); gluQuadricNormals(qobj, GLU_NONE); glNewList(startList+2, GL_COMPILE); gluDisk(qobj, 0.25, 1.0, 20, 4); 2010-2학기 가상현실
glNewList(startList+3, GL_COMPILE); gluQuadricDrawStyle(qobj, GLU_SILHOUETTE); gluQuadricNormals(qobj, GLU_NONE); glNewList(startList+3, GL_COMPILE); gluPartialDisk(qobj, 0.0, 1.0, 20, 4, 0.0, 225.0); glEndList(); } void display(void) { glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); 2010-2학기 가상현실
glEnable(GL_LIGHTING); glShadeModel (GL_SMOOTH); glTranslatef(-1.0, -1.0, 0.0); glCallList(startList); glShadeModel (GL_FLAT); glTranslatef(0.0, 2.0, 0.0); glPushMatrix(); glRotatef(300.0, 1.0, 0.0, 0.0); glCallList(startList+1); glPopMatrix(); 2010-2학기 가상현실
glDisable(GL_LIGHTING); glColor3f(0.0, 1.0, 1.0); glTranslatef(2.0, -2.0, 0.0); glCallList(startList+2); glColor3f(1.0, 1.0, 0.0); glTranslatef(0.0, 2.0, 0.0); glCallList(startList+3); glPopMatrix(); glFlush(); } 2010-2학기 가상현실
void reshape (int w, int h) { glViewport(0, 0, (GLsizei) w, (GLsizei) h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if (w <= h) glOrtho(-2.5, 2.5, -2.5*(GLfloat)h/(GLfloat)w, 2.5*(GLfloat)h/(GLfloat)w, -10.0, 10.0); else glOrtho(-2.5*(GLfloat)w/(GLfloat)h, 2.5*(GLfloat)w/(GLfloat)h, -2.5, 2.5, -10.0, 10.0); glMatrixMode(GL_MODELVIEW); } 2010-2학기 가상현실
void keyboard(unsigned char key, int x, int y) { switch (key) { case 27: exit(0); break; } 2010-2학기 가상현실
int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(500, 500); glutInitWindowPosition(100, 100); glutCreateWindow(argv[0]); init(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutKeyboardFunc(keyboard); glutMainLoop(); return 0; } 2010-2학기 가상현실
실행결과 2010-2학기 가상현실