
나만의 tiny renderer 만들기 (4) - Z-Buffer, 애파인(아핀) 변환
여기서 아핀 변환쪽을 잠깐 이해하는 시간을 가져보도록 하자
행렬은 벡터에 적용되는 함수정도라고 생각하고 보면 이해될듯?
여기에 추가로 오일러 각 + 쿼터니언 개념 가지고 가면 좋음
3차원에서 물체의 회전을 x,y,z축 각각의 회전으로 보는거임
x,y,z축의 회전을 순서대로 각각 roll, pitch, yaw회전이라고 함
이때 각 축의 회전하는 함수(행렬)은 다음과 같음
X축 기준 회전 행렬
Y축 기준 회전 행렬
Z축 기준 회전 행렬
가만히 보면 완벽한 회전일거같은데 2개의 문제를 가짐

이렇게 두 점을 , , 만큼씩 움직인다고 생각해보자



즉 각 벡터의 이동경로만 따지고 보면

이런 괴상한 경로가 만들어지는거임
이렇게 되는 이유는 간단한데
x,y,z를 따로따로, 각각, 한개씩 회전을 해서 그럼

이 gif를 보자
첨엔 제대로 회전하다가 나중에는 x,y,z의 회전축이 겹쳐서
어느순간에는 어떤 축이 회전하는지 판단이 불가능한 순간이 옴
예를들어 z축으로 90도를 회전시키면
x축과 y축이 같은 방향을 가리키게 됨
이때, x축을 회전하는것과 y축을 회전하는것을 비교할 수 있을까?
어떤게 회전하고 있는걸까?
이러한 축 겹침은 x,y | y,z | x,z | x,y,z와 같이 모든 회전축에 대해 일어날 수 있음
이렇게 보간과 짐벌락 현상을 해결하기 위해 등장한 개념이
임
뭐 어떻게 생기고, 어떤 공식이 사용되고 이런거는 좀 영역밖의 일이라서 간단하게 원리를 설명하면
오일러각은 x,y,z축에 대하여 각각 회전을 주는 방식이었다면
쿼터니언은
1. 축의 회전순서가 정해져 있지 않음
2. 선형보간이 아닌 구면 선형보간(Slerp)를 이용해서 자연스러운 곡선 유도
3. slerp를 통한 회전에 필요한 가장 최단경로 유도
즉, 쿼터니언은
축 하나씩 따로 회전하지 않고
전체의 회전정보를 에 담아 한번에 회전시키는거임
glm이라는 라이브러리를 사용하면 행렬, 벡터, 변환 모두 자동으로 사용가능!!
https://glm.g-truc.net/0.9.8/index.html
이 라이브러리를 includes폴더로 이동시키면 됨
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
glm::vec4 vec(1.0f, 0.0f, 0.0f, 1.0f);
glm::mat4 trans = glm::mat4(1.0f);
trans = glm::translate(trans, glm::vec3(1.0f, 1.0f, 0.0f));
vec = trans * vec;
std::cout << vec.x << vec.y << vec.z << std::endl;
알고보면 간단함
자자 위에서 행렬을 뭐라고 했지?
행렬은 벡터 변환 전용 함수다
그럼 이 코드를 해석해보자
1,0,0,1이라는 vec4를 만듬trans라는 4*4의 단위행렬을 만듬translate(변환)메서드를 이용해서 1,1,0만큼 이동하는 변환 행렬을 만듬졸라쉬움!
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
glm::vec4 vec(1.0f, 0.0f, 0.0f, 1.0f);
glm::mat4 trans = glm::mat4(1.0f);
trans = glm::rotate(trans, glm::radians(60.0f), glm::vec3(/*회전축*/0, 0, 1));
vec = trans * vec;
std::cout << vec.x << vec.y << vec.z << std::endl;
이것도 쉬움
vec4로 원본좌표 표시glm::radians를 이용해 60분법이라 불리는 일상의 각도를 라디안으로 만들어주기(임 이게 ㅇㅇ)#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
glm::vec4 vec(1.0f, 0.0f, 0.0f, 1.0f);
glm::mat4 trans = glm::mat4(1.0f);
trans = glm::scale(trans, glm::vec3(0.8, 0.2, 1));
vec = trans * vec;
std::cout << vec.x << vec.y << vec.z << std::endl;
이것도 졸라쉬워
scale메서드를 이용해서 원하는 크기를 설정하는 변환행렬로 만들기쉬움!!
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
glm::vec4 vec(1.0f, 0.0f, 0.0f, 1.0f);
glm::mat4 trans = glm::mat4(1.0f);
trans = glm::translate(trans, glm::vec3(0.1f, 0.4f, -0.5f));
trans = glm::rotate(trans, glm::radians(60.0f), glm::vec3(/*회전축*/0, 0, 1));
trans = glm::scale(trans, glm::vec3(0.5f, 0.5f, 0.2f));
vec = trans * vec;
std::cout << vec.x << vec.y << vec.z << std::endl;
그냥 변환용 행렬을 원하는 함수로 만들어주면 되는거임
glsl에도 matrix 자료형이 있음
mat4임
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 2) in vec2 aTexCoord;
out vec2 TexCoord;
uniform mat4 transform;
void main()
{
gl_Position = transform * vec4(aPos, 1.0f);
TexCoord = vec2(aTexCoord.x, aTexCoord.y);
}
vertex shader를 위처럼 수정해주고
// 변환행렬
glm::mat4 transform = glm::mat4(1.0f); // make sure to initialize matrix to identity matrix first
transform = glm::translate(transform, glm::vec3(0.5f, -0.5f, 0.0f));
transform = glm::rotate(transform, (float)glfwGetTime(), glm::vec3(0.0f, 0.0f, 1.0f));
transform = glm::scale(transform, glm::vec3(0.5f, 0.5f, 0.5f));
// 쉐이더 사용
shaderProgram.useShader();
unsigned int transformLoc = glGetUniformLocation(shaderProgram.shaderProgramID, "transform");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));
이런식으로 쓰면 되는데
glUniformMatrix4fv이걸 설명해줌
glm::value_ptr을 이용해 행렬의 값만 넘겨주도록 하자#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "Shader.h"
#include "stb_image.h"
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void process_input(GLFWwindow *window);
float vertices[] =
{
//positions //colors //텍스쳐 좌표
0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, //우상단
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, //우하단
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, //좌하단
-0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f //좌상단
};
uint32_t indices[] =
{
0, 1, 3,
1, 2, 3
};
float lerp = 0.0f;
int main()
{
constexpr int windowWidth = 1280;
constexpr int windowHeight = 720;
//glfw 초기화
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//glfw로 창 만들기
GLFWwindow* window = glfwCreateWindow(windowWidth, windowHeight, "HukPak", nullptr, nullptr);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
//GLAD를 사용해 Gl함수들의 포인터를 체킹
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
//커스텀 콜백 메서드 framebuffer_size_callback를 이용해 glfw의 창 크기 설정
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
// 쉐이더 생성
Shader shaderProgram(
"D:/01_Develope/CPP/learn_opengl/learn_opengl/VertexShaderSource.vsh",
"D:/01_Develope/CPP/learn_opengl/learn_opengl/FragmentShaderSource.fsh");
// VAO
uint32_t VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
// VBO
uint32_t VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// EBO
uint32_t EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// 버텍스 속성 링킹
//position
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 8, (void*)0);
glEnableVertexAttribArray(0);
//color
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 8, (void*)(sizeof(float) * 3));
glEnableVertexAttribArray(1);
//텍스쳐 버텍스
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 8, (void*)(sizeof(float) * 6));
glEnableVertexAttribArray(2);
// GL에게 버텍스 매핑은 끝이므로, 더이상 바인딩 안한다고 명시적으로 알림
glBindVertexArray(0);
//텍스쳐 만들기
uint32_t texture1, texture2;
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);
//텍스쳐 크기를 넘어가는 버텍스에 대해선 가로/세로 모두 repeat
//텍스쳐가 축소되면 mipmap linear 축소, 텍스쳐가 확대되면 linear 확대
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//텍스쳐 이미지 로딩
int imageWidth, imageHeight, nrChannels;
stbi_set_flip_vertically_on_load(true);
uint8_t *data = stbi_load("wall.jpg", &imageWidth, &imageHeight, &nrChannels, 0);
//텍스쳐 이미지가 로딩 성공하면 해당 이미지로 텍스쳐Image2D 생성, mipmap생성
if (data)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, imageWidth, imageHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load Texture file" << std::endl;
}
//로드된 이미지 메모리 해제
stbi_image_free(data);
//2번 텍스쳐 반복
glGenTextures(1, &texture2);
glBindTexture(GL_TEXTURE_2D, texture2);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//
data = stbi_load("awesomeface.png", &imageWidth, &imageHeight, &nrChannels, 0);
if (data) //png이므로 RGBA채널 사용
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, imageWidth, imageHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load Texture file" << std::endl;
}
stbi_image_free(data);
//uniform사용하기 위해 shader program 미리 use해주기
shaderProgram.useShader();
glUniform1i(glGetUniformLocation(shaderProgram.shaderProgramID, "tex1"), 0);
glUniform1i(glGetUniformLocation(shaderProgram.shaderProgramID, "tex2"), 1);
//run window
while(!glfwWindowShouldClose(window))
{
process_input(window);
glClearColor(.0f, .0f, .0f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);
//텍스쳐 유닛 사용
glActiveTexture(GL_TEXTURE0); //0번 채널
glBindTexture(GL_TEXTURE_2D, texture1);
glActiveTexture(GL_TEXTURE1); //1번 채널
glBindTexture(GL_TEXTURE_2D, texture2);
// 변환행렬
glm::mat4 transform = glm::mat4(1.0f); // make sure to initialize matrix to identity matrix first
transform = glm::translate(transform, glm::vec3(0.5f, -0.5f, 0.0f));
transform = glm::rotate(transform, (float)glfwGetTime(), glm::vec3(0.0f, 0.0f, 1.0f));
transform = glm::scale(transform, glm::vec3(0.5f, 0.5f, 0.5f));
// 쉐이더 사용
shaderProgram.useShader();
unsigned int transformLoc = glGetUniformLocation(shaderProgram.shaderProgramID, "transform");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));
//VAO 활성화
glBindVertexArray(VAO);
// 그리기
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
//VAO비활성화
glBindVertexArray(0);
//swapping buffer frame, and execute event as like input or something
glfwSwapBuffers(window);
glfwPollEvents();
}
//optional, don't need it but keep do it until used to OpenGL
glDeleteBuffers(1, &VBO);
glDeleteVertexArrays(1, &VAO);
//stop glfw with runtime over
glfwTerminate();
return 0;
}
//viewport configuration callback method
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
//key input callback
void process_input(GLFWwindow *window)
{
if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
if (glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS)
{
if (lerp >= 0.9f) return;
lerp += 0.01f;
}
else if (glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS)
{
if (lerp <= 0.1f) return;
lerp -= 0.01f;
}
}
이렇게 while문 내부에 코드를 작성하면...

혹은 회전을 약간 다르게 해볼까?
이런식으로 변환 행렬을 만들면??

이렇게 되는 이유는 간단함
0,0,0이었음0,0,0을 기준으로 수행되는데이렇게 언제 회전하고, 언제 이동하고, 언제 scale을 조정하냐에 따라 변환은 신기하게 바뀜ㅋㅋㅋ
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <random>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "Shader.h"
#include "stb_image.h"
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void process_input(GLFWwindow *window);
float vertices[] =
{
//positions //colors //텍스쳐 좌표
0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, //우상단
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, //우하단
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, //좌하단
-0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f //좌상단
};
uint32_t indices[] =
{
0, 1, 3,
1, 2, 3
};
float lerp = 0.0f;
int main()
{
constexpr int windowWidth = 1280;
constexpr int windowHeight = 720;
//glfw 초기화
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//glfw로 창 만들기
GLFWwindow* window = glfwCreateWindow(windowWidth, windowHeight, "HukPak", nullptr, nullptr);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
//GLAD를 사용해 Gl함수들의 포인터를 체킹
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
//커스텀 콜백 메서드 framebuffer_size_callback를 이용해 glfw의 창 크기 설정
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
// 쉐이더 생성
Shader shaderProgram(
"D:/01_Develope/CPP/learn_opengl/learn_opengl/VertexShaderSource.vsh",
"D:/01_Develope/CPP/learn_opengl/learn_opengl/FragmentShaderSource.fsh");
// VAO
uint32_t VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
// VBO
uint32_t VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// EBO
uint32_t EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// 버텍스 속성 링킹
//position
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 8, (void*)0);
glEnableVertexAttribArray(0);
//color
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 8, (void*)(sizeof(float) * 3));
glEnableVertexAttribArray(1);
//텍스쳐 버텍스
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 8, (void*)(sizeof(float) * 6));
glEnableVertexAttribArray(2);
// GL에게 버텍스 매핑은 끝이므로, 더이상 바인딩 안한다고 명시적으로 알림
glBindVertexArray(0);
//텍스쳐 만들기
uint32_t texture1, texture2;
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);
//텍스쳐 크기를 넘어가는 버텍스에 대해선 가로/세로 모두 repeat
//텍스쳐가 축소되면 mipmap linear 축소, 텍스쳐가 확대되면 linear 확대
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//텍스쳐 이미지 로딩
int imageWidth, imageHeight, nrChannels;
stbi_set_flip_vertically_on_load(true);
uint8_t *data = stbi_load("wall.jpg", &imageWidth, &imageHeight, &nrChannels, 0);
//텍스쳐 이미지가 로딩 성공하면 해당 이미지로 텍스쳐Image2D 생성, mipmap생성
if (data)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, imageWidth, imageHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load Texture file" << std::endl;
}
//로드된 이미지 메모리 해제
stbi_image_free(data);
//2번 텍스쳐 반복
glGenTextures(1, &texture2);
glBindTexture(GL_TEXTURE_2D, texture2);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//
data = stbi_load("awesomeface.png", &imageWidth, &imageHeight, &nrChannels, 0);
if (data) //png이므로 RGBA채널 사용
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, imageWidth, imageHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load Texture file" << std::endl;
}
stbi_image_free(data);
//uniform사용하기 위해 shader program 미리 use해주기
shaderProgram.useShader();
glUniform1i(glGetUniformLocation(shaderProgram.shaderProgramID, "tex1"), 0);
glUniform1i(glGetUniformLocation(shaderProgram.shaderProgramID, "tex2"), 1);
std::random_device rd; // 하드웨어 기반 시드 생성
std::mt19937 gen(rd());
std::uniform_int_distribution<int> distrib(-100, 100);
float rand1 = static_cast<float>(distrib(gen)) / 100;
float rand2 = static_cast<float>(distrib(gen)) / 100;
float rand3 = static_cast<float>(distrib(gen)) / 100;
float rand4 = static_cast<float>(distrib(gen)) / 100;
//run window
while(!glfwWindowShouldClose(window))
{
process_input(window);
glClearColor(.0f, .0f, .0f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);
//텍스쳐 유닛 사용
glActiveTexture(GL_TEXTURE0); //0번 채널
glBindTexture(GL_TEXTURE_2D, texture1);
glActiveTexture(GL_TEXTURE1); //1번 채널
glBindTexture(GL_TEXTURE_2D, texture2);
// 변환행렬
glm::mat4 transform = glm::mat4(1.0f);
transform = glm::rotate(transform, (float)glfwGetTime(), glm::vec3(0.0f, 0.0f, 1.0f));
transform = glm::scale(transform, glm::vec3(0.5f, 0.5f, 0.5f));
transform = glm::translate(transform, glm::vec3(0.5f, -0.5f, 0.0f));
// 쉐이더 사용
shaderProgram.useShader();
unsigned int transformLoc = glGetUniformLocation(shaderProgram.shaderProgramID, "transform");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));
//VAO 활성화
glBindVertexArray(VAO);
// 그리기
//glDrawArrays(GL_TRIANGLES, 0, 3);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
//역회전
transform = glm::mat4(1.0f);
transform = glm::rotate(transform, -(float)glfwGetTime(), glm::vec3(0.0f, 0.0f, 1.0f));
transform = glm::scale(transform, glm::vec3(0.5f, 0.5f, 0.5f));
transform = glm::translate(transform, glm::vec3(0.5f, -0.5f, 0.0f));
shaderProgram.useShader();
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
//랜덤 1사분면
transform = glm::mat4(1.0f);
transform = glm::translate(transform, glm::vec3(0.5f, 0.5f, 0.0f));
transform = glm::rotate(transform, static_cast<float>(glfwGetTime()) * 8, glm::vec3(rand1, rand1, rand1));
transform = glm::scale(transform, glm::vec3(0.7f, 0.7f, 0.7f));
shaderProgram.useShader();
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
//랜덤 2사분면
transform = glm::mat4(1.0f);
transform = glm::translate(transform, glm::vec3(-0.5f, 0.5f, 0.0f));
transform = glm::rotate(transform, static_cast<float>(glfwGetTime()) * 8, glm::vec3(rand2, rand2, rand2));
transform = glm::scale(transform, glm::vec3(0.7f, 0.7f, 0.7f));
shaderProgram.useShader();
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
//랜덤 3사분면
transform = glm::mat4(1.0f);
transform = glm::translate(transform, glm::vec3(-0.5f, -0.5f, 0.0f));
transform = glm::rotate(transform, static_cast<float>(glfwGetTime()) * 8, glm::vec3(rand3, rand3, rand3));
transform = glm::scale(transform, glm::vec3(0.7f, 0.7f, 0.7f));
shaderProgram.useShader();
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
//랜덤 4사분면
transform = glm::mat4(1.0f);
transform = glm::translate(transform, glm::vec3(0.5f, -0.5f, 0.0f));
transform = glm::rotate(transform, static_cast<float>(glfwGetTime()) * 8, glm::vec3(rand4, rand4, rand4));
transform = glm::scale(transform, glm::vec3(0.7f, 0.7f, 0.7f));
shaderProgram.useShader();
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
//VAO비활성화
glBindVertexArray(0);
//swapping buffer frame, and execute event as like input or something
glfwSwapBuffers(window);
glfwPollEvents();
}
//optional, don't need it but keep do it until used to OpenGL
glDeleteBuffers(1, &VBO);
glDeleteVertexArrays(1, &VAO);
//stop glfw with runtime over
glfwTerminate();
return 0;
}
//viewport configuration callback method
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
//key input callback
void process_input(GLFWwindow *window)
{
if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
if (glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS)
{
if (lerp >= 0.9f) return;
lerp += 0.01f;
}
else if (glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS)
{
if (lerp <= 0.1f) return;
lerp -= 0.01f;
}
}
while문에서 약간 수정함
1개는
1. 회전
2. 스케일
3. 이동
순서로 먼저 그리고
5개를 추가로 그림
1개는 역회전
나머지 4개는 각 사분면에서 랜덤 회전축을 가지도록 ㅇㅇ

ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ