[컴퓨터그래픽스] 실습: ColorCylinder

Serun1017·2024년 10월 23일
0

컴퓨터그래픽스

목록 보기
19/31

이번 실습에선 ColorCylinder를 만들고 사용자 입력에 따라 vertex number를 변경하고 도형의 회전, 정지 기능을 만들어보자.

MyColorCylinder.h

Preprocessor, Struct, Member Variables

이번엔 MyCylinderVertex 구조체를 정의하여 vertex data를 저장하자.

#ifndef _MY_COLOR_CYLINDER_H_
#define _MY_COLOR_CYLINDER_H_

#include <vgl.h>
#include <vec.h>

struct MyCylinderVertex {
	vec4 position;
	vec4 color;
}

class MyColorCylinder {
public :
	int m_NumPoly;   // number of division for a circle
	int m_NumVertex; // = m_NumPoly * 2 * 3

	GLuint m_vao;    // handle for vertex array
	GLuint m_vbo;    // handle for array buffer

	bool m_bInit;    // check whether initialized
}

#endif

Constructor

MyColorCylinder() {
	m_NumPoly = 0;
	m_NumVertex = 0;
	m_bInit = false;
}

void init

init 함수는 int numPolybool bCap 을 입력 받는다. 이때 numPoly 는 원 기둥의 원을 삼각형으로 나누었을 때 만들어지는 꼭짓점의 개수이다. bCap 은 원 기둥의 옆 면을 제외한 원 뚜껑을 씌울지 여부로 기본적으로 true 이다.

void init(int numPoly, bool bCap = true) {
	if (numPoly < 3) numPoly = 3;
	if (m_bInit == true && numPoly == m_NumPoly) return;

	m_NumPoly = numPoly;
	m_NumVertex = m_NumPoly * 2 * 3;
	if (bCap) {
		m_NumVertex += m_NumPoly * 3 * 2;
	}

	printf("Cylinder: NumPoly = %d NumVertex = %d\n", m_NumPoly, m_NumVertex);

	MyCylinderVertex* vertices = new MyCylinderVertex[m_NumVertex];
	float r = 0.5;
	float dtheta = 2 * 3.141592f / m_NumPoly;
	
	int cur = 0;
	for (int i = 0; i < m_NumPoly; i++) {
		float ang1 = dtheta * i;
		float ang2 = dtheta * (i + 1);

		vec4 a = vec4(r * cos(ang1), 0.5, r * sin(ang1), 1); // vec4(x, y, z, w)
		vec4 b = vec4(r * cos(ang2), 0.5, r * sin(ang2), 1);
		vec4 c = vec4(r * cos(ang1), -0.5, r * sin(ang1), 1);
		vec4 d = vec4(r * cos(ang2), -0.5, r * sin(ang2), 1);
		
		vec4 o1 = vec4(0, 0.5, 0, 1);
		vec4 o2 = vec4(0, 0.5, 0, -1);
		vec4 cc = vec4(0, 1, 0, 1);

		float ra = (float)i / (m_NumPoly - 1);
		vec4 color = vec4(ra, 0, 1 - ra, 1); //vec4(r, g, b, a);

		if (bCap) {
			// triangle for upper cap
			vertices[cur].position = a; vertices[cur].color = cc; cur++;
			vertices[cur].position = o1; vertices[cur].color = cc; cur++;
			vertices[cur].position = b; vertices[cur].color = cc; cur++;
		}

		// triangle 1
		vertices[cur].position = a; vertices[cur].color = color; cur++;
		vertices[cur].position = b; vertices[cur].color = color; cur++;
		vertices[cur].position = c; vertices[cur].color = color; cur++;

		// triangle 2
		vertices[cur].position = b; vertices[cur].color = color; cur++;
		vertices[cur].position = d; vertices[cur].color = color; cur++;
		vertices[cur].position = c; vertices[cur].color = color; cur++;

		if (bCap) {
			// triangle for lower cap
			vertices[cur].position = c; vertices[cur].color = cc; cur++;
			vertices[cur].position = d; vertices[cur].color = cc; cur++;
			vertices[cur].position = o2; vertices[cur].color = cc; cur++;
		}
	}

	if (m_bInit == false) {
		glGenVertexArrays(1, &m_vao);
		glBindVertexArray(m_vao);

		glGenBuffers(1, &m_vbo);
		glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
	}
	else {
		glBindVertexArray(m_vao);
		glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
	}
	glBufferData(GL_ARRAY_BUFFER, sizeof(MyCylinderVertex) * m_NumVertex, vertices, GL_STATIC_DRAW);

	delete[] vertices;

	m_bInit = true;
}

원 기둥을 만들기 위한 최소 numPoly 는 3 이고, 이미 원 기둥의 vertex data에 대한 초기화 작업을 수행하였고 numPoly의 갯수를 변경하지 않은경우 업데이트 하지 않으므로 아래 조건을 추가하였다.

	if (numPoly < 3) numPoly = 3;
	if (m_bInit == true && numPoly == m_NumPoly) return;

또한 m_bInit 을 이용해 이미 초기화를 했는지 여부를 확인하여 GPU의 buffer 를 추가적으로 사용하지 않도록 한다. 만약, 아래의 조건이 없다면 init 함수를 호출할 때마다 GPU에 새로운 m_vaom_vbo를 할당하기 때문에 메모리 공간이 낭비된다.

	if (m_bInit == false) {
		glGenVertexArrays(1, &m_vao);
		glBindVertexArray(m_vao);

		glGenBuffers(1, &m_vbo);
		glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
	}
	else {
		glBindVertexArray(m_vao);
		glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
	}

bind 된 buffer 에 vertex data를 저장한다.

glBufferData(GL_ARRAY_BUFFER, sizeof(MyCylinderVertex) * m_NumVertex, vertices, GL_STATIC_DRAW);

그 외 멤버 함수

	void connectShader(GLuint prog) {
		GLuint vPosition = glGetAttribLocation(prog, "vPosition");
		glEnableVertexAttribArray(vPosition);
		glVertexAttribPointer(vPosition, 4, GL_FLOAT, GL_FALSE, sizeof(MyCylinderVertex), BUFFER_OFFSET(0));

		GLuint vColor = glGetAttribLocation(prog, "vColor");
		glEnableVertexAttribArray(vColor);
		glVertexAttribPointer(vColor, 4, GL_FLOAT, GL_FALSE, sizeof(MyCylinderVertex), BUFFER_OFFSET(sizeof(vec4)));
	}
	void draw(GLuint program) {
		glBindVertexArray(m_vao);
		glUseProgram(program);
		connectShader(program);
		glDrawArrays(GL_TRIANGLES, 0, m_NumVertex);
	}
	void increase() {
		int num = m_NumPoly + 1;
		init(num);
	}
	void decrease() {
		int num = m_NumPoly - 1;
		init(num);
	}

ColorCylinder.cpp

#include <vgl.h>
#include <vec.h>
#include <InitShader.h>
#include "MyColorCylinder.h"

GLuint program;
MyColorCylinder cylinder;

void myInit() {
	
	cylinder.init(8);

	// 3. load shaders
	program = InitShader("vshader.glsl", "fshader.glsl");
	glUseProgram(program);

}

float myTime = 0;

void display() {

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glEnable(GL_DEPTH_TEST);


	glUseProgram(program);

	GLuint uTime = glGetUniformLocation(program, "uTime");
	glUniform1f(uTime, myTime);

	cylinder.draw(program);

	glFlush();
}

bool bPlay = true;
void idle() {
	if (bPlay) myTime += 0.0333f;
	Sleep(33);

	glutPostRedisplay();
}

void keyboard(unsigned char key, int x, int y) {
	switch (key) {
	case '1' :
		cylinder.increase();
		break;
	case '2' :
		cylinder.decrease();
		break;
	case ' ' :
		printf("stop or play! \n"); 
		bPlay = !bPlay;
		break;
	default :
		break;
	}
}

int main(int argc, char** argv) {
	
	glutInit(&argc, argv);
	
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA | GLUT_DEPTH);
	glutInitWindowSize(800, 800);
	glutCreateWindow("A Color Cube");

	glewExperimental = true;
	glewInit();

	myInit();

	glutDisplayFunc(display);
	glutIdleFunc(idle);
	glutKeyboardFunc(keyboard);

	glutMainLoop();

	return 0;
}

keyboard Callback 함수에서 사용자 키보드 입력을 받았을 때의 조건을 추가한 후 glutKeyboardFunc(keyboard) 함수로 Callback 등록 한다.

vshader.glsl

#version 330

uniform float uTime;

in vec4 vPosition;
in vec4 vColor;

out vec4 color;
out vec4 position;

void main() {
	float scale = 1 + sin(uTime);

	float ang = uTime*90/180.0f*3.141592f;

	mat4 m1 = mat4(1.0f);

	// x-rotation
	m1[1][1] = cos(ang);
	m1[2][1] = -sin(ang);
	m1[1][2] = sin(ang);
	m1[2][2] = cos(ang);


	mat4 m2 = mat4(1.0f);
	// z-rotation
	/*m[0][0] = cos(ang);
	m[1][0] = -sin(ang);
	m[0][1] = sin(ang);
	m[1][1] = cos(ang);*/

	// x-rotation
	/*m[1][1] = cos(ang);
	m[2][1] = -sin(ang);
	m[1][2] = sin(ang);
	m[2][2] = cos(ang);*/

	// y-rotation
	m2[0][0] = cos(ang);
	m2[2][0] = sin(ang);
	m2[0][2] = -sin(ang);
	m2[2][2] = cos(ang);


	gl_Position = m2*m1*vPosition;
	gl_Position.w = 1.0f;

	color = vColor;
	position = vPosition;
}

fshader.glsl

#version 330

in vec4 color;
in vec4 position;

out vec4 fColor;

void main() {	
	fColor = color;
}

결과

ColorCylinder_result.png

0개의 댓글