이번 실습에선 ColorCylinder를 만들고 사용자 입력에 따라 vertex number를 변경하고 도형의 회전, 정지 기능을 만들어보자.
이번엔 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
MyColorCylinder() {
m_NumPoly = 0;
m_NumVertex = 0;
m_bInit = false;
}
void init
init
함수는 int numPoly
와 bool 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_vao
와 m_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);
}
#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 등록 한다.
#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;
}
#version 330
in vec4 color;
in vec4 position;
out vec4 fColor;
void main() {
fColor = color;
}