본문 바로가기
College Computer Science/Computer Graphics

[컴퓨터그래픽스] 세가지 방법으로 오각형 그리기, OpenGL에서 primitive 그리기, glVertex, glDrawArrays, glDrawElements

by 2den 2020. 5. 22.
728x90

 

이번 학기 컴퓨터 그래픽스 과목 첫 번째 실습은 바로, '세 가지 방법으로 오각형 그리기'이다.

 

OpenGL 애플리케이션에서 primitave를 그리는 방식은 세 가지가 있는데, glBegin - glVertex - glEnd를 사용하는 방법과, glDrawArrays를 사용하는 방법, glDrawElements를 사용하는 방법이다.

 

 

 

glBegin - glVertex - glEnd

직접 vertex의 위치를 지정하여 그려주는 방식이다. 먼저 glBegin()을 실행해야 한다. 이 때 parameter로 primitive의 종류*를 정해준다.

 

1) glBegin(GL_POINTS)

2) glBegin(GL_TRIANGLES)

3) glBegin(GL_POLYGON)

... 등등

 

 

glBegin()의 뒤를 이어, glVertex()를 통해 위에서 설정한 primitive로 그려질 정점의 좌표를 직접 입력한다. 그 후, glEnd()를 실행하여 primitive 그리기를 종료한다.

 

아래는 과제로 제출한 오각형 그리기 코드의 일부이다. 각각의 정점의 좌표는 A(0, 0.4), B(-0.4, 0.1), C(-0.2, -0.3), D(0.2, -0.3), E(0.4, 0.1)로 설정했다. (임의로 설정한 값)

void RenderScene(void)
{
	glClear(GL_COLOR_BUFFER_BIT);
	glColor3f(0.5f, 0.5f, 0.5f);
    
	glBegin(GL_TRIANGLES);
		glVertex3f(0.0f, 0.4f, 0.f);
		glVertex3f(-0.4f, 0.1f, 0.f);
		glVertex3f(-0.2f, -0.3f, 0.f);

		glVertex3f(-0.2f, -0.3f, 0.f);
		glVertex3f(0.2f, -0.3f, 0.f);
		glVertex3f(0.0f, 0.4f, 0.f);

		glVertex3f(0.0f, 0.4f, 0.f);
		glVertex3f(0.2f, -0.3f, 0.f);
		glVertex3f(0.4f, 0.1f, 0.f);
	glEnd();

	glFlush();
}

 

 

 

glDrawArrays

정점 데이터를 배열로 저장하여, 데이터를 링크하여 그리는 방식이다.

 

glEnableClientState(GL_VERTEX_ARRAY)로 클라이언트(CPU)에서 서버(GPU)로의 데이터 전송을 설정한다. primitive를 그리고 나면, glDisableClientState(GL_VERTEX_ARRAY)로 설정을 꺼준다.

 

glDrawArrays() 함수를 호출하기 전에 glVertexPointer()를 이용해 정점 데이터를 링크해야 한다.

glVertexPointer(GLint size, GLenum type, GLsizei stride, const GLvoid* pointer)

 

- size : 배열 안의 값을 차원 수로 나눈 것, 즉 배열 안에 있는 X-Y 또는 X-Y-Z 쌍의 개수

- type : GL_FLOAT, GL_SHORT, GL_INT or GL_DOUBLE, 일반적으로 GL_FLOAT을 많이 사용

- stride : 다음 정점으로의 offset 바이트 수, 인터리브(interleave)된 배열**에 사용

- pointer : 정점 데이터를 저장한 배열을 가리키는 포인터

 

준비가 되었다면, glDrawArrays()를 호출한다.

glDrawArrays(GLenum mode, GLint first, GLsizei count)

 

- mode : primitive 종류

- first : 정점(X-Y 또는 X-Y-Z를 묶어서 생각)의 시작 인덱스

- count : 정점의 개수

 

아래는 과제로 제출한 오각형 그리기 코드의 일부이다.

void RenderScene(void)
{
	glClear(GL_COLOR_BUFFER_BIT);
	glColor3f(0.5f, 0.5f, 0.5f);

	GLfloat vertices[] = {
		0.0f, 0.4f, 0.f,
		-0.4f, 0.1f, 0.f,
		-0.2f, -0.3f, 0.f,
		0.2f, -0.3f, 0.f,
		0.0f, 0.4f, 0.f,
		0.2f, -0.3f, 0.f,
		0.4f, 0.1f, 0.f
	};

	glEnableClientState(GL_VERTEX_ARRAY);
	glVertexPointer(3, GL_FLOAT, 0, vertices);

	glDrawArrays(GL_TRIANGLES, 0, 3);
	glDrawArrays(GL_TRIANGLES, 2, 3);
	glDrawArrays(GL_TRIANGLES, 4, 3);

	glDisableClientState(GL_VERTEX_ARRAY);

	glFlush();
}

 

 

 

glDrawElements

glDrawArrays는 다시 지나는 정점도 모두 데이터에 저장해준 반면, glDrawElements를 사용하면 한 정점은 한 번만 배열에 저장하여 사용할 수 있다.

 

GLubyte 배열을 선언하여 (GLushort, GLuint도 가능), 정점을 그리는 순서에 따라 인덱스를 저장해주면 된다.

glDrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices)

 

- mode : primitive의 종류

- count : 그리는 정점의 개수

- type : 인덱스 배열의 자료형

- indices : 인덱스 배열을 가리키는 포인터

 

아래는 과제로 제출한 오각형 그리기 코드의 일부이다. 배열 indice에 저장된 순서대로 0 - 1 - 2 정점으로 삼각형 하나, 2 - 3 - 0 정점으로 삼각형 하나, 0 - 3 - 4 정점으로 삼각형 하나를 그린다.

void RenderScene(void)
{
	glClear(GL_COLOR_BUFFER_BIT);
	glColor3f(0.5f, 0.5f, 0.5f);

	GLfloat vertices[] = {
		0.0f, 0.4f, 0.f,
		-0.4f, 0.1f, 0.f,
		-0.2f, -0.3f, 0.f,
		0.2f, -0.3f, 0.f,
		0.4f, 0.1f, 0.f
	};
	GLubyte indices[] = { 0,1,2, 2,3,0, 0,3,4 };

	glEnableClientState(GL_VERTEX_ARRAY);

	glVertexPointer(3, GL_FLOAT, 0, vertices);
	glDrawElements(GL_TRIANGLES, 9, GL_UNSIGNED_BYTE, indices);
	glDisableClientState(GL_VERTEX_ARRAY);
    
	glFlush();
}

 

 

 

최종 제출 코드

 

이번 실습은 RenderScene() 함수에 세가지 방식을 주석처리하여 .txt 파일로 제출하면 된다. 따라서 위에서 작성한 코드들을 모두 합친 아래와 같은 코드를 제출하였다.

 

void RenderScene(void)
{
	glClear(GL_COLOR_BUFFER_BIT);
	glColor3f(0.5f, 0.5f, 0.5f);
	/*
	glBegin(GL_TRIANGLES);
		glVertex3f(0.0f, 0.4f, 0.f);
		glVertex3f(-0.4f, 0.1f, 0.f);
		glVertex3f(-0.2f, -0.3f, 0.f);

		glVertex3f(-0.2f, -0.3f, 0.f);
		glVertex3f(0.2f, -0.3f, 0.f);
		glVertex3f(0.0f, 0.4f, 0.f);

		glVertex3f(0.0f, 0.4f, 0.f);
		glVertex3f(0.2f, -0.3f, 0.f);
		glVertex3f(0.4f, 0.1f, 0.f);
	glEnd();
	*/

	/*
	GLfloat vertices[] = {
		0.0f, 0.4f, 0.f,
		-0.4f, 0.1f, 0.f,
		-0.2f, -0.3f, 0.f,
		0.2f, -0.3f, 0.f,
		0.0f, 0.4f, 0.f,
		0.2f, -0.3f, 0.f,
		0.4f, 0.1f, 0.f
	};

	glEnableClientState(GL_VERTEX_ARRAY);
	glVertexPointer(3, GL_FLOAT, 0, vertices);

	glDrawArrays(GL_TRIANGLES, 0, 3);
	glDrawArrays(GL_TRIANGLES, 2, 3);
	glDrawArrays(GL_TRIANGLES, 4, 3);

	glDisableClientState(GL_VERTEX_ARRAY);
	*/

	/*
	GLfloat vertices[] = {
		0.0f, 0.4f, 0.f,
		-0.4f, 0.1f, 0.f,
		-0.2f, -0.3f, 0.f,
		0.2f, -0.3f, 0.f,
		0.4f, 0.1f, 0.f
	};
	GLubyte indices[] = { 0,1,2, 2,3,0, 0,3,4 };

	glEnableClientState(GL_VERTEX_ARRAY);

	glVertexPointer(3, GL_FLOAT, 0, vertices);
	glDrawElements(GL_TRIANGLES, 9, GL_UNSIGNED_BYTE, indices);
	glDisableClientState(GL_VERTEX_ARRAY);
	*/

	glFlush();
}

 

 

 

실행 결과 화면은 세가지 방식 모두 같은 정점 좌표를 사용하여 동일하게 출력된다.

 

 

 

 


* primitive의 종류 : GL에는 10개의 primitive가 존재하며 (GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_LINE_LOOP, GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_QUADS, GL_QUAD_STRIP, GL_POLYGON), 각각의 설정에 따라 주어진 정점들을 어떻게 그릴지 결정한다.

 

(이미지 출처) https://www.3dgep.com/rendering-primitives-with-opengl/

 

** 인터리브 (interleave) : 컴퓨터 하드디스크의 성능을 높이기 위해 데이터를 서로 인접하지 않게 배열하는 방식을 말한다. 인터리브(interleave)라는 단어는 ‘교차로 배치하다’라는 뜻이며, 이를 통해 디스크 드라이브를 좀 더 효율적으로 만들 수 있다. 인터리브는 기억장치를 몇 개의 부분으로 나누어서 메모리 액세스를 동시에 할 수 있게 함으로써 복수의 명령을 처리하여 메모리 액세스의 효율화를 도모하는 것이다. 대부분의 하드디스크는 드라이브의 속도와 운영체제(OS), 응용 프로그램 등에 영향을 받기 때문에 인터리브 값을 미리 설정하지는 않는다. 인터리브 값이 작을수록 하드디스크 드라이브의 속도가 빨라진다.

(출처) https://www.scienceall.com/%EC%9D%B8%ED%84%B0%EB%A6%AC%EB%B8%8Cinterleave/

728x90

댓글