- 화면에 삼각형 그리기
- 화면에 삼각형 사사분면에 4개 그리기
- 삼각형은 각각 다른 색상 설정
- 마우스를 누르면 그 위치에 새로운 삼각형 을 그린다.
- 순서대로 이전에 그린 사각형을 삭제된다.
- 마우스 클릭 -> 첫번째 삭제되고 삭제되고 마우스 위치에 삼각형
- 마우스 클릭 -> 두번째 삼각형 삭제되고 마우스 위치에 삼각형
- 화면에는 항상 4개의 삼각형 만이 그려진다.
- 회전시키기
- 키보드 명령 수행
- f: 도형 그리기 모드 변경 (GL_FILL or GL_LINE)
- m: 4개의 삼각형이 임의의 방향으로 이동하고 가장자리에 도달하면 방향을 바꿔 계속 이동한다. (단 화면 밖으로 나가지 않는다)
- s: 멈추기
- c: 삼각형의 색상 바꾸기
- q: 프로그램 종료하기
버텍스 세이더(glsl파일)
#version 330 core
in vec3 positionAttribute;
in vec3 colorAttribute;
out vec3 passColorAttribute;
void main()
{
gl_Position = vec4(positionAttribute, 1.0);
passColorAttribute = colorAttribute;
};
프래그먼트 세이더(glsl파일)
#version 330 core
in vec3 passColorAttribute;
out vec4 fragmentColor;
void main()
{
fragmentColor = vec4(passColorAttribute, 1.0);
};
세이더 파일 버퍼에 저장하기
char* filetobuf(const char* file)
{
ifstream in(file, ios_base::binary);
if (!in)
{
cout << file << "파일 못찾음" << endl;
exit(1);
}
in.seekg(0, ios_base::end);
long len = in.tellg();
char* buf = new char[len + 1];
in.seekg(0, ios_base::beg);
int cnt = -1;
while (in >> noskipws >> buf[++cnt]){}
buf[len] = 0;
return buf;
}
bool initShaderProgram() {
//세이더 코드 파일 불러오기
const GLchar* vertexShaderSource = filetobuf("Resources/VertexShader.glsl");
const GLchar* fragmentShaderSource = filetobuf("Resources/FragmentShader.glsl");
//세이더 객체 만들기
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
//세이더 객체에 세이더 코드 붙이기
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
//세이더 객체 컴파일하기
glCompileShader(vertexShader);
GLint result;
GLchar errorLog[512];
//세이더 상태 가져오기
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &result);
if (!result)
{
glGetShaderInfoLog(vertexShader, 512, NULL, errorLog);
cerr << "ERROR: vertex shader 컴파일 실패\n" << errorLog << endl;
return false;
}
//세이더 객체 만들기
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
//세이더 객체에 세이더 코드 붙이기
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
//세이더 객체 컴파일하기
glCompileShader(fragmentShader);
//세이더 상태 가져오기
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &result);
if (!result)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, errorLog);
cerr << "ERROR: fragment shader 컴파일 실패\n" << errorLog << endl;
return false;
}
//세이더 프로그램 생성
shaderProgramID = glCreateProgram();
//세이더 프로그램에 세이더 객체들을 붙이기
glAttachShader(shaderProgramID, vertexShader);
glAttachShader(shaderProgramID, fragmentShader);
//세이더 프로그램 링크
glLinkProgram(shaderProgramID);
//세이더 객체 삭제하기
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
//프로그램 상태 가져오기
glGetProgramiv(shaderProgramID, GL_LINK_STATUS, &result);
if (!result) {
glGetProgramInfoLog(shaderProgramID, 512, NULL, errorLog);
cerr << "ERROR: shader program 연결 실패\n" << errorLog << endl;
return false;
}
//세이더 프로그램 활성화
glUseProgram(shaderProgramID);
return true;
}
삼각형 버텍스 위치와 색상 설정
struct Triangle {
glm::vec3 v[3];
glm::vec3 color[3];
};
Triangle tri[4] = {
{ {glm::vec3(0.4f, 0.4f, 0.0f), glm::vec3(0.6f, 0.4f, 0.0f), glm::vec3(0.5f, 0.6f, 0.0f)}, {glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.7f, 0.3f, 0.0f), glm::vec3(0.7f, 0.0f, 0.3f)}},
{ {glm::vec3(-0.4f, 0.4f, 0.0f), glm::vec3(-0.6f, 0.4f, 0.0f), glm::vec3(-0.5f, 0.6f, 0.0f)}, {glm::vec3(1.0f, 1.0f, 0.0f), glm::vec3(0.7f, 1.0f, 0.0f), glm::vec3(1.0f, 0.7f, 0.0f) }},
{ {glm::vec3(-0.4f, -0.6f, 0.0f), glm::vec3(-0.6f, -0.6f, 0.0f), glm::vec3(-0.5f, -0.4f, 0.0f)}, {glm::vec3(0.0f, 0.0f, 0.1f), glm::vec3(0.0f, 0.3f, 0.7f), glm::vec3(0.3f, 0.0f, 0.7f) }},
{ {glm::vec3(0.4f, -0.6f, 0.0f), glm::vec3(0.6f, -0.6f, 0.0f), glm::vec3(0.5f, -0.4f, 0.0f)}, {glm::vec3(0.0f, 1.0f, 1.0f), glm::vec3(0.0f, 0.7f, 1.0f), glm::vec3(0.0f, 1.0f, 0.7f) }}
};
VAO와 VBO 설정하기
bool defineVertexArrayObject() {
//삼각형을 구성하는 vertex 데이터 - position과 color
float position[9 * size(tri)];
for (int cnt = -1, i = 0; i < size(tri); ++i)
{
for (int j = 0; j < 3; ++j)
{
position[++cnt] = tri[i].v[j].x;
position[++cnt] = tri[i].v[j].y;
position[++cnt] = tri[i].v[j].z;
}
}
unsigned int positionIndex[] = {
0, 1, 2, // 첫 번째 삼각형
3, 4, 5, // 두 번째 삼각형
6, 7, 8,
9, 10, 11
};
float color[9 * size(tri)];
for (int cnt = -1, i = 0; i < size(tri); ++i)
{
for (int j = 0; j < 3; ++j)
{
color[++cnt] = tri[i].color[j].x;
color[++cnt] = tri[i].color[j].y;
color[++cnt] = tri[i].color[j].z;
}
}
//버텍스 배열 오브젝트 (VAO) 이름 생성
glGenVertexArrays(1, &triangleVertexArrayObject);
//VAO를 바인드한다.
glBindVertexArray(triangleVertexArrayObject);
//Vertex Buffer Object(VBO)를 생성하여 vertex 데이터를 복사한다.
//버텍스 버퍼 오브젝트 (VBO) 이름 생성
glGenBuffers(1, &trianglePositionVertexBufferObjectID);
//버퍼 오브젝트를 바인드 한다.
glBindBuffer(GL_ARRAY_BUFFER, trianglePositionVertexBufferObjectID);
//버퍼 오브젝트의 데이터를 생성
glBufferData(GL_ARRAY_BUFFER, sizeof(position), position, GL_STATIC_DRAW);
//엘리멘트 버퍼 오브젝트 (EBO) 이름 생성
glGenBuffers(1, &trianglePositionElementBufferObject);
//버퍼 오브젝트를 바인드 한다.
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, trianglePositionElementBufferObject);
//버퍼 오브젝트의 데이터를 생성
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(positionIndex), positionIndex, GL_STATIC_DRAW);
//위치 가져오기 함수
GLint positionAttribute = glGetAttribLocation(shaderProgramID, "positionAttribute");
if (positionAttribute == -1) {
cerr << "position 속성 설정 실패" << endl;
return false;
}
//버텍스 속성 데이터의 배열을 정의
glVertexAttribPointer(positionAttribute, 3, GL_FLOAT, GL_FALSE, 0, 0);
//버텍스 속성 배열을 사용하도록 한다.
glEnableVertexAttribArray(positionAttribute);
//칼라 버퍼 오브젝트 (VBO) 이름 생성
glGenBuffers(1, &triangleColorVertexBufferObjectID);
//버퍼 오브젝트를 바인드 한다.
glBindBuffer(GL_ARRAY_BUFFER, triangleColorVertexBufferObjectID);
//버퍼 오브젝트의 데이터를 생성
glBufferData(GL_ARRAY_BUFFER, sizeof(color), color, GL_STATIC_DRAW);
//위치 가져오기 함수
GLint colorAttribute = glGetAttribLocation(shaderProgramID, "colorAttribute");
if (colorAttribute == -1) {
cerr << "color 속성 설정 실패" << endl;
return false;
}
//버퍼 오브젝트를 바인드 한다.
glBindBuffer(GL_ARRAY_BUFFER, triangleColorVertexBufferObjectID);
//버텍스 속성 데이터의 배열을 정의
glVertexAttribPointer(colorAttribute, 3, GL_FLOAT, GL_FALSE, 0, 0);
//버텍스 속성 배열을 사용하도록 한다.
glEnableVertexAttribArray(colorAttribute);
glBindVertexArray(0);
return true;
}
세이더 사용하여 렌더링하기
//--- 콜백 함수: 그리기 콜백 함수
GLvoid drawScene()
{
// 바탕색을 설정
glClearColor(background_rgb.x, background_rgb.y, background_rgb.z, 1.0f);
// 설정된 색으로 전체를 칠하기
glClear(GL_COLOR_BUFFER_BIT);
//만들어진 세이더 프로그램 사용하기
glUseProgram(shaderProgramID);
// 사용할 VAO 불러오기
glBindVertexArray(triangleVertexArrayObject);
//배열 데이터로부터 프리미티브 렌더링 하기
glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0);
//glDrawArrays(GL_TRIANGLES, 0, 12);
// 화면에 출력하기
glutSwapBuffers();
}
#include <GL/glew.h>
#include <GL/freeglut.h>
#include <GL/freeglut_ext.h>
#include <glm/glm.hpp>
#include <glm/ext.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <iostream>
#include <random>
using namespace std;
random_device seeder;
mt19937 eng(seeder());
uniform_real_distribution<double> dist(0.0f, 1.0f);
const int WIN_X = 10, WIN_Y = 10;
const int WIN_W = 800, WIN_H = 800;
const double RECT_WH = 0.4f;
const double HALFRECT_WH = RECT_WH / 2.0f;
const glm::vec3 background_rgb = glm::vec3(1.0f, 1.0f, 1.0f);
struct Triangle {
glm::vec3 v[3];
glm::vec3 color[3];
};
Triangle tri[4] = {
{ {glm::vec3(0.4f, 0.4f, 0.0f), glm::vec3(0.6f, 0.4f, 0.0f), glm::vec3(0.5f, 0.6f, 0.0f)}, {glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.7f, 0.3f, 0.0f), glm::vec3(0.7f, 0.0f, 0.3f)}},
{ {glm::vec3(-0.4f, 0.4f, 0.0f), glm::vec3(-0.6f, 0.4f, 0.0f), glm::vec3(-0.5f, 0.6f, 0.0f)}, {glm::vec3(1.0f, 1.0f, 0.0f), glm::vec3(0.7f, 1.0f, 0.0f), glm::vec3(1.0f, 0.7f, 0.0f) }},
{ {glm::vec3(-0.4f, -0.6f, 0.0f), glm::vec3(-0.6f, -0.6f, 0.0f), glm::vec3(-0.5f, -0.4f, 0.0f)}, {glm::vec3(0.0f, 0.0f, 0.1f), glm::vec3(0.0f, 0.3f, 0.7f), glm::vec3(0.3f, 0.0f, 0.7f) }},
{ {glm::vec3(0.4f, -0.6f, 0.0f), glm::vec3(0.6f, -0.6f, 0.0f), glm::vec3(0.5f, -0.4f, 0.0f)}, {glm::vec3(0.0f, 1.0f, 1.0f), glm::vec3(0.0f, 0.7f, 1.0f), glm::vec3(0.0f, 1.0f, 0.7f) }}
};
int whatTriRepos = 0;
//bool IsTimerAlive = true;
GLfloat mx = 0.0f;
GLfloat my = 0.0f;
int framebufferWidth, framebufferHeight;
GLuint triangleVertexArrayObject;
GLuint shaderProgramID;
GLuint trianglePositionVertexBufferObjectID, triangleColorVertexBufferObjectID;
GLuint trianglePositionElementBufferObject;
char* filetobuf(const char* file)
{
FILE* fptr;
long length;
char* buf;
fptr = fopen(file, "rb"); // Open file for reading
if (!fptr) // Return NULL on failure
{
cout << file << "파일 못찾음" << endl;
exit(1);
}
fseek(fptr, 0, SEEK_END); // Seek to the end of the file
length = ftell(fptr); // Find out how many bytes into the file we are
buf = (char*)malloc(length + 1); // Allocate a buffer for the entire length of the file and a null terminator
fseek(fptr, 0, SEEK_SET); // Go back to the beginning of the file
fread(buf, length, 1, fptr); // Read the contents of the file in to the buffer
fclose(fptr); // Close the file
buf[length] = 0; // Null terminator
return buf; // Return the buffer
}
bool initShaderProgram() {
//세이더 코드 파일 불러오기
const GLchar* vertexShaderSource = filetobuf("Resources/VertexShader.glsl");
const GLchar* fragmentShaderSource = filetobuf("Resources/FragmentShader.glsl");
//세이더 객체 만들기
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
//세이더 객체에 세이더 코드 붙이기
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
//세이더 객체 컴파일하기
glCompileShader(vertexShader);
GLint result;
GLchar errorLog[512];
//세이더 상태 가져오기
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &result);
if (!result)
{
glGetShaderInfoLog(vertexShader, 512, NULL, errorLog);
cerr << "ERROR: vertex shader 컴파일 실패\n" << errorLog << endl;
return false;
}
//세이더 객체 만들기
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
//세이더 객체에 세이더 코드 붙이기
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
//세이더 객체 컴파일하기
glCompileShader(fragmentShader);
//세이더 상태 가져오기
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &result);
if (!result)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, errorLog);
cerr << "ERROR: fragment shader 컴파일 실패\n" << errorLog << endl;
return false;
}
//세이더 프로그램 생성
shaderProgramID = glCreateProgram();
//세이더 프로그램에 세이더 객체들을 붙이기
glAttachShader(shaderProgramID, vertexShader);
glAttachShader(shaderProgramID, fragmentShader);
//세이더 프로그램 링크
glLinkProgram(shaderProgramID);
//세이더 객체 삭제하기
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
//프로그램 상태 가져오기
glGetProgramiv(shaderProgramID, GL_LINK_STATUS, &result);
if (!result) {
glGetProgramInfoLog(shaderProgramID, 512, NULL, errorLog);
cerr << "ERROR: shader program 연결 실패\n" << errorLog << endl;
return false;
}
//세이더 프로그램 활성화
glUseProgram(shaderProgramID);
return true;
}
bool defineVertexArrayObject() {
//삼각형을 구성하는 vertex 데이터 - position과 color
float position[9 * size(tri)];
for (int cnt = -1, i = 0; i < size(tri); ++i)
{
for (int j = 0; j < 3; ++j)
{
position[++cnt] = tri[i].v[j].x;
position[++cnt] = tri[i].v[j].y;
position[++cnt] = tri[i].v[j].z;
}
}
unsigned int positionIndex[] = {
0, 1, 2, // 첫 번째 삼각형
3, 4, 5, // 두 번째 삼각형
6, 7, 8,
9, 10, 11
};
float color[9 * size(tri)];
for (int cnt = -1, i = 0; i < size(tri); ++i)
{
for (int j = 0; j < 3; ++j)
{
color[++cnt] = tri[i].color[j].x;
color[++cnt] = tri[i].color[j].y;
color[++cnt] = tri[i].color[j].z;
}
}
//버텍스 배열 오브젝트 (VAO) 이름 생성
glGenVertexArrays(1, &triangleVertexArrayObject);
//VAO를 바인드한다.
glBindVertexArray(triangleVertexArrayObject);
//Vertex Buffer Object(VBO)를 생성하여 vertex 데이터를 복사한다.
//버텍스 버퍼 오브젝트 (VBO) 이름 생성
glGenBuffers(1, &trianglePositionVertexBufferObjectID);
//버퍼 오브젝트를 바인드 한다.
glBindBuffer(GL_ARRAY_BUFFER, trianglePositionVertexBufferObjectID);
//버퍼 오브젝트의 데이터를 생성
glBufferData(GL_ARRAY_BUFFER, sizeof(position), position, GL_STATIC_DRAW);
//엘리멘트 버퍼 오브젝트 (EBO) 이름 생성
glGenBuffers(1, &trianglePositionElementBufferObject);
//버퍼 오브젝트를 바인드 한다.
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, trianglePositionElementBufferObject);
//버퍼 오브젝트의 데이터를 생성
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(positionIndex), positionIndex, GL_STATIC_DRAW);
//위치 가져오기 함수
GLint positionAttribute = glGetAttribLocation(shaderProgramID, "positionAttribute");
if (positionAttribute == -1) {
cerr << "position 속성 설정 실패" << endl;
return false;
}
//버텍스 속성 데이터의 배열을 정의
glVertexAttribPointer(positionAttribute, 3, GL_FLOAT, GL_FALSE, 0, 0);
//버텍스 속성 배열을 사용하도록 한다.
glEnableVertexAttribArray(positionAttribute);
//칼라 버퍼 오브젝트 (VBO) 이름 생성
glGenBuffers(1, &triangleColorVertexBufferObjectID);
//버퍼 오브젝트를 바인드 한다.
glBindBuffer(GL_ARRAY_BUFFER, triangleColorVertexBufferObjectID);
//버퍼 오브젝트의 데이터를 생성
glBufferData(GL_ARRAY_BUFFER, sizeof(color), color, GL_STATIC_DRAW);
//위치 가져오기 함수
GLint colorAttribute = glGetAttribLocation(shaderProgramID, "colorAttribute");
if (colorAttribute == -1) {
cerr << "color 속성 설정 실패" << endl;
return false;
}
//버퍼 오브젝트를 바인드 한다.
glBindBuffer(GL_ARRAY_BUFFER, triangleColorVertexBufferObjectID);
//버텍스 속성 데이터의 배열을 정의
glVertexAttribPointer(colorAttribute, 3, GL_FLOAT, GL_FALSE, 0, 0);
//버텍스 속성 배열을 사용하도록 한다.
glEnableVertexAttribArray(colorAttribute);
glBindVertexArray(0);
return true;
}
//--- 콜백 함수: 그리기 콜백 함수
GLvoid drawScene()
{
// 바탕색을 설정
glClearColor(background_rgb.x, background_rgb.y, background_rgb.z, 1.0f);
// 설정된 색으로 전체를 칠하기
glClear(GL_COLOR_BUFFER_BIT);
//만들어진 세이더 프로그램 사용하기
glUseProgram(shaderProgramID);
// 사용할 VAO 불러오기
glBindVertexArray(triangleVertexArrayObject);
//배열 데이터로부터 프리미티브 렌더링 하기
glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0);
//glDrawArrays(GL_TRIANGLES, 0, 12);
// 화면에 출력하기
glutSwapBuffers();
}
//--- 콜백 함수: 다시 그리기 콜백 함수
GLvoid Reshape(int w, int h)
{
glViewport(0, 0, w, h);
}
void Mouse(int button, int state, int x, int y)
{
GLfloat half_w = WIN_W / 2.0f;
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
mx = (x - half_w) / half_w;
my = (half_w - y) / half_w;
tri[whatTriRepos].v[0] = glm::vec3(mx - 0.1f, my - 0.1f, 0.0f);
tri[whatTriRepos].v[1] = glm::vec3(mx + 0.1f, my - 0.1f, 0.0f);
tri[whatTriRepos].v[2] = glm::vec3(mx, my + 0.1f, 0.0f);
whatTriRepos = ++whatTriRepos % 4;
}
defineVertexArrayObject();
glutPostRedisplay();
}
int main(int argc, char** argv)
{
//윈도우 생성
glutInit(&argc, argv); // glut 초기화
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA); // 디스플레이 모드 설정
glutInitWindowPosition(WIN_X, WIN_Y); // 윈도우의 위치 지정
glutInitWindowSize(WIN_W, WIN_H); // 윈도우의 크기 지정
glutCreateWindow("Example1"); // 윈도우 생성(윈도우 이름)
//GLEW 초기화하기
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK)
{
std::cerr << "Unable to initialize GLEW" << std::endl;
exit(EXIT_FAILURE);
}
else
std::cout << "GLEW Initialized\n";
if (!initShaderProgram()) {
cerr << "Error: Shader Program 생성 실패" << endl;
std::exit(EXIT_FAILURE);
}
if (!defineVertexArrayObject()) {
cerr << "Error: Shader Program 생성 실패" << endl;
std::exit(EXIT_FAILURE);
}
glutDisplayFunc(drawScene); // 출력 함수의 지정
glutReshapeFunc(Reshape); // 다시 그리기 함수 지정
glutMouseFunc(Mouse);
glutMainLoop(); // 이벤트 처리 시작
}