
LearnOpenGL(6) - 좌표 시스템
여기서 view공간에 대해서 설명할때 다음 포스트에서 자세하게 다룬다고 했음
그게 여기임ㅇㅇ
웰컴투 히어
뷰 공간은 카메라(관찰자)로부터 공간의 특정 지점을 바라본 공간을 의미함
즉, 카메라가 매우 중요하단걸 알 수 있음
카메라 위치를 정해야함
auto cameraPos = glm::vec3(0,0,3);
이런식으로 카메라의 위치를 정해주면 됨
여기서 중요한거!!!
오른손 좌표계냐 왼손 좌표계냐에 따라 카메라의 위치가 달라짐
오른손좌표계
양의 x축으로 엄지를 뻗고, 양의 y축으로 검지를 뻗었을때,
중지는 오른손 좌표계일때, 나를 가리키고 있음
즉, 그게 양의 z축이 중지가 가리키는 방향이고, 그게 나를 가리킨다는 거임
따라서 z축에 양수를 더하면 나한테 가까워지고
음수를 더하면 나한테서 멀어짐왼손좌표계
양의 x축으로 엄지를 뻗고, 양의 y축으로 검지를 뻗었을때,
중지는 왼손 좌표계일때, 나의 반대방향 가리키고 있음
즉, 그게 양의 z축이 중지가 가리키는 방향이고, 그게 나의 반대 방향이라는거임
따라서 z축에 양수를 더하면 나한테 더 멀어지고
음수를 더하면 나한테 더 가까워짐
카메라위치를 정할때는 좌표계에 맞춰 z축을 조정해야함!
카메라 방향을 정해줘야함
이것도 좌표계에 따라 달라짐
기본적으로 GL은 뷰 행렬에 사용할 z축은 양수를 원함(desire)
그리고 우리가 사용하고 있는 좌표계는 오른손 좌표계이므로
카메라의 방향은 음의 z축을 향하고 있음
따라서 카메라가 찍기를 원하는 타겟의 좌표가 p라고 햇을때
p.z와 cameraPos.z의 순서를 적절하게 바꿔 계산을 해서
벡터 방향이 양의 z를 향하도록 하면 됨!
auto cameraTarget = glm::vec3(0,0,0);
auto cameraDir = glm::normalize(cameraPos - cameraTarget);
지금의 카메라는 위아래가 뒤집힌건지 뭔지 모름
예를들어 카메라의 위아래가 뒤집혀잇다고 해보자
cameraPos, cameraDir모두 값에 차이가 없음
근데 y좌표 + 양수를 하면 카메라가 아래로 움직임
왜냐면 카메라는 로컬공간에서 움직이는거라
up방향으로 움직이고있는데
이는 월드좌표에서 down방향인거거덩
그걸 해결하기 위해 먼저 카메라의 right 방향을 구한다음
그걸 이용해서 up 방향을 구해야함
right는 쉬움
외적하면됨
월드의 up 방향과cameraDirection 방향을 외적하면
두 벡터의 수직인 벡터가 나오지?
이게 right방향벡터임
왜 up과 cameraDirection임?
외적은 순서에 따라 결과가 반대가 됨
외적은 방향이 순환되는 특성이 있음이걸 잘 봐보자
가 성립하는걸 볼 수 있음!(모르겠다면 엄지를 시작, 검지를 연산에 놓으면 중지가 결과 방향이 됨)
즉 이걸 다시 정리해보면
이 되는걸 볼 수 있음
잘 모르겠다면 right = , up = , dir = 로 외적 계산 ㄱㄱ
따라서 우리에게 필요한건 월드좌표기준 up벡터와 그 up과 카메라를 향하는 방향(양의 z축)을 외적하면 카메라의 right벡터가 나옴!!!
auto worldUp = glm::vec3(0,1,0);
auto cameraRight = glm::normalize(glm::cross(worldUp, cameraDir));
normalize를 해야하는 이유는
따라서 모든 방향벡터의 연산에는 normalize가 필요함
이제 카메라의 up을 구할 수 있게됨
위에서 설명한대로
이 성립함
그러니,,,
autp cameraUp = glm::cross(cameraDir, cameraRight);
를함
??? 여기선 왜 normalize안함??
일반적으로 normalize는 무거운 연산임
그리고 이미 dir과 right는 수직이 확실함
그러니 up도 완벽하게 수직이라는걸 알 수있음그러니까 normalize를 건너뛰어도 되는거임
그냥 GOAT임
위 계산과정을 한번에 파라미터로 넘기기만하면 수행해줌
사실 뷰 행렬은 간단함
임
: 카메라의 right
: 카메라의 up
: 카메라 Dir
그럼 lookAt은 어떻게 작동하냐>?
임
왜 임?
뷰 공간은 카메라 공간이라고 부름
이때 특이한 점은 카메라가 움직인다는 개념이 아님
카메라는 가만히 있고, 카메라를 기준으로 배경이 회전하고 움직인다는 느낌임약간 기차 생각하면 됨
세상 관점에서 보면 기차가 움직이고 풍경은 가만히 있음
근데 기차에 타고있는 사람관점에서보면 풍경이 나를 지나쳐감즉 내가 기차를 타고 5만큼 이동했다면
이거는 풍경이 기차를 기준으로 -5만큼 이동했다고 보면 되는거임따라서 현재
cameraPos를 x,y,z만큼 이동시켰으니
이거를 원점으로 돌려 카메라를 기준으로 회전시키기 위해 역를 기준으로 회전시키기 위해 역원을 더해 0으로 만들어주는거임
glm::mat4 view;
view = glm::lookAt(glm::vec3(0.0f, 0.0f, 3.0f),
glm::vec3(0.0f, 0.0f, 0.0f),
glm::vec3(0.0f, 1.0f, 0.0f));
끝!
const float radius = 10.0f;
float camX = sin(glfwGetTime()) * radius;
float camZ = cos(glfwGetTime()) * radius;
glm::mat4 view;
view = glm::lookAt(glm::vec3(camX, 0.0, camZ), glm::vec3(0.0, 0.0, 0.0), glm::vec3(0.0, 1.0, 0.0));
뷰 행렬을 회전하도록 해봤음
그럼 카메라가 회전하는것처럼 보이겠지??

빙빙 돌아가는~~
먼저 3개의 좌표를 만들어줌
auto cameraPos = glm::vec3(0,0,3);
auto cameraFront = glm::vec3(0,0,-1); //카메라가 보는 방향 벡터
auto worldUp = glm::vec3(0,1,0);
view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
여기서 cameraFront를 더해주는 이유는
그냥 카메라의 정면을 계속 보도록 하는거임
CameraPosition - 원점 을 하면 원점만 바라보는데,
cameraPosition + CameraFrontDir를 하면 카메라가 바라보는 방향이 나오게 되는거지
//key input callback
void process_input(GLFWwindow *window)
{
//...
const float cameraSpeed = 0.05f; // adjust accordingly
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
cameraPos += cameraSpeed * cameraFront;
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
cameraPos -= cameraSpeed * cameraFront;
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
cameraPos -= glm::normalize(glm::cross(cameraFront, worldUp)) * cameraSpeed;
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
cameraPos += glm::normalize(glm::cross(cameraFront, worldUp)) * cameraSpeed;
}
input관리하는 이벤트 콜백함수에서
대충 이런식으로 만들어줌
앞으로갈때는 cameraPos += cameraPos * cameraFront를 해서
카메라를 음의 z축(오른손 좌표계 기준)으로 움직이고
뒤로갈때는 그 반대,
오른쪽으로 갈때는 위에서 구한 right를 구하는 방법을 이용해서
오른손좌표계 기준 엄지를 front방향, 검지를 up방향으로 만들면 중지가 오른쪽 방향이므로 그 방향을 빼면, 왼쪽으로 이동
더하면 오른쪽으로 이동이 됨
정규화 필수!!!
사람들 컴터마다 연산처리속도가 다름
그래서 일반적인 프레임으로 처리를 해버리면 누구는 빠르고 누구는 느리게 됨
그래서 deltaTime을 계산해줘야함
고성능 PC를 A,
개똥컴 PC를 B라고 부르도록 하겠음
A는 1초에 10번 프레임 호출이 됨 (10fps)
B는 1초에 1번 프레임 호출이 됨 (1fps)
이때 각 프레임간의 호출시간 차이를 deltaTime이라고 하면
A의 deltaTime = 0.1초
B의 deltaTime = 1초
가 됨
이때 cameraSpeed가 2일때
A의 deltaTime * cameraSpeed = 0.1 * 2 = 0.2 (0.1초단위)
B의 deltaTime * cameraSpeed = 2 (1초단위)
즉, 1초가 같이 흐르게 되면
A컴퓨터와 B컴퓨터의 이동거리는 2로 동일한 거리만큼 이동되게 됨
float deltaTime = 0.0f;
float lastFrame = 0.0f;
이런 코드를 최상단에 선언해줌
그리고 매 프레임 시간을 계산해야됨
이거는 glfwGetTime()을 통해 아주 쉽게 계산가능함
float currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
그리고 이렇게 계산된 deltaTime을 cameraSpeed에 적용시켜주면...
void processInput(GLFWwindow *window)
{
float cameraSpeed = 5.0f * deltaTime;
//...
}

ㅋㅋㅋ
오일러 각을 알아야함
LearnOpenGL(5) - glm 여기서 오일러 각에 대해 간단하게 알아보고 오자!
현재 카메라시스템에서는 z축 회전이 필요없음!
배그같이 기울이기 되는거면 필요하겠지만,,,
y축의 회전은 yaw, x축 회전은 pitch임
우리는 마우스 이동을 통해 카메라가 바라보는 방향을 바꾸고자 하는거임
yaw는 y축으로의 회전임
따라서 y좌표는 변하지 않음

이걸보면 y좌표를 제외하고 좌표를 평면으로 본거임
이때 빗변과 밑변의 각을 yaw라고 할때,
yaw가 증가하거나 감소할때
가 됨
가 됨
따라서 카메라가 보는 방향을 camDir이라고 할때
glm::vec3 camDir;
camDir.x = cos(glm::radians(yaw));
camDir.z = sin(glm::radians(yaw));
가 됨
위의 yaw회전 결과에 pitch회전 결과만 붙이면 그게 바로 카메라가 보는 방향임

위의 yaw회전을 할때, yaw는 x/z축과 관계가 있었음
그 x/z 평면에서 pitch를 구하는것이므로 가 된거임
이때 빗변과 밑변(x/z평면)의 각이 pitch라고 할때
가됨
camDir.y = sin(glm::radians(pitch));
가 됨
우리 눈이 카메라라고 해보자

이 그림을 봐봐
카메라가 정면을 보면 위에서 살펴봤듯이 임
이때 로, 온전히 x/z평면에 귀속됨
근데 만약 카메라가 정확히 90도, 정수리 위를 본다면?
이 됨
이때 는?
우리 눈이 앞으로 튀어나오지 않는 이상
정확히 이 되고, x/z는 0인 상태에서 위를 바라보는거라고 해야됨
따라서 는 x/z평면에 영향을 끼침
가 1이면 온전히 x/z평면에 귀속되고
이 값이 변화함에따라 x/z평면의 방향과 블렌딩이 된다고 봐야하는거지 ㅇㅇ
따라서 전체 코드는
glm::vec3 camDir;
camDir.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
camDir.y = sin(glm::radians(pitch));
camDir.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
가 됨을 알 수 있음!!
여기에 z축 회전이 추가되어야 한다면
x/y평면에 대하여 z축의 을 구한다음
마찬가지로 영향을 끼치는 걸 판단해서 구해주면 되겠지
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
먼저 이런 코드를 작성해서
커서를 창에 박아놓음
창밖으로 나가지 않고, 커서는 안보이고 ㅇㅇ
그리고 마우스는 마우스 이동 이벤트를 받아서 처리해야하므로
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
이런 콜백을 만들어서 사용할거니 전방선언 후
그리고
bool firstMouse = true;
float yaw = -90.0f;
float pitch = 0.0f;
float lastX = 800.0f / 2.0;
float lastY = 600.0 / 2.0;
float fov = 45.0f;
이런 코드를 전역으로 선언해준다
yaw가 -90인 이유
- yaw = 0일때
이걸 보면 yaw가 0이면 완전 오른쪽을 바라보게 됨
즉, 정면이 아니라 오른쪽을 본상태로 시작한다는거지...- yaw = 90일때
위사진을 보면 yaw가 90이면 완전 양의 z축을 바라보게됨
근데 우리는 오른손 좌표계를 사용중임
즉, 90을 사용하면 카메라에서 양의 z축인 뒷편을 바라보게 된다는거임이런이유로 -90도를 사용해서 카메라에서 앞편인 음의 z축을 바라보게 해야함
그리고 마우스 이동 콜백은 한번만 수행하면 되므로
main 최상단에 박아버려~
//마우스 이동 인풋
glfwSetCursorPosCallback(window, mouse_callback);
이제 콜백 메서드를 작성할 차례임
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
if (firstMouse)
{
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
float xoffset = xpos - lastX;
float yoffset = lastY - ypos;
lastX = xpos;
lastY = ypos;
float sensitivity = 0.1f;
xoffset *= sensitivity;
yoffset *= sensitivity;
yaw += xoffset;
pitch += yoffset;
pitch = glm::clamp(pitch, -89.0f, 89.0f);
glm::vec3 direction;
direction.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
direction.y = sin(glm::radians(pitch));
direction.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
cameraFront = glm::normalize(direction);
}
천천히 살펴보자
if (firstMouse)
{
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
처음 프로그램이 실행되면 아무 값도 지정되어있지 않으므로
lastX와 lastY좌표를 현재 마우스 좌표로 넣어버림
float xoffset = xpos - lastX;
float yoffset = lastY - ypos;
lastX = xpos;
lastY = ypos;
float sensitivity = 0.1f;
xoffset *= sensitivity;
yoffset *= sensitivity;
yaw += xoffset;
pitch += yoffset;
pitch = glm::clamp(pitch, -89.0f, 89.0f);
clamp임
우리가 right방향을 구할때
worldUp = (0,1,0)과 외적했던거 기억남?
방향이 아예 같거나(0도 차이) 완전히 반대방향(180도차이)같은 두 벡터를 외적하면
외적 결과는 영 벡터가 됨
그럼 right벡터는 영 벡터인가?
말이 안되지 ㅇㅇㅇ
그러므로 이 worldUp벡터와 카메라의 방향 벡터가 평행이 되지 않는 조건을 만들기위해
-89~89도 사이로 위아래에 영향을 주는 pitch를 clamping하는거임
glm::vec3 direction;
direction.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
direction.y = sin(glm::radians(pitch));
direction.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
cameraFront = glm::normalize(direction);
위에서 살펴본 공식을 이용해 카메라가 보는 방향을 구하면 됨

윈도우 가로세로의 종횡비에 따라
가로 감도, 세로 감도를 직접 지정해주면 더 좋긴해~
그냥 이건 졸라 간단해!
확대하면 fovy가 감소해서 더 적은 부분만 렌더링하면 되는거고
축소하면 fovy가 증가해서 더 많은 부분을 렌더링하도록 하면 되는거임!
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
fov -= (float)yoffset;
fov = glm::clamp(fov, 1.0f, 45.0f);
}
대충 이런 콜백 메서드 만들어주고
전방선언하고
glfwSetScrollCallback(window, scroll_callback);
넣어주면 끄읕~~

#ifndef CAMERA_H
#define CAMERA_H
#include <glad/glad.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
enum Camera_Movement
{
FORWARD,
BACKWARD,
LEFT,
RIGHT
};
const float YAW = -90.0f;
const float PITCH = 0.0f;
const float SPEED = 2.5f;
const float SENSITIVITY = 0.1f;
const float ZOOM = 45.0f;
class Camera
{
public:
glm::vec3 Position;
glm::vec3 Front;
glm::vec3 Up;
glm::vec3 Right;
glm::vec3 WorldUp;
float Yaw;
float Pitch;
float MovementSpeed;
float MouseSensitivity;
float Zoom;
Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), float yaw = YAW, float pitch = PITCH) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVITY), Zoom(ZOOM)
{
Position = position;
WorldUp = up;
Yaw = yaw;
Pitch = pitch;
updateCameraVectors();
}
Camera(float posX, float posY, float posZ, float upX, float upY, float upZ, float yaw, float pitch) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVITY), Zoom(ZOOM)
{
Position = glm::vec3(posX, posY, posZ);
WorldUp = glm::vec3(upX, upY, upZ);
Yaw = yaw;
Pitch = pitch;
updateCameraVectors();
}
glm::mat4 GetViewMatrix()
{
return glm::lookAt(Position, Position + Front, Up);
}
void ProcessKeyboard(Camera_Movement direction, float deltaTime)
{
float velocity = MovementSpeed * deltaTime;
if (direction == FORWARD)
Position += Front * velocity;
if (direction == BACKWARD)
Position -= Front * velocity;
if (direction == LEFT)
Position -= Right * velocity;
if (direction == RIGHT)
Position += Right * velocity;
}
//때때로 pitch를 clamp하지 말아야할 경우도 생김
//예를들어 비행기 조작 ㅇㅇ
그럴땐 다른 방법을 써야지~
void ProcessMouseMovement(float xoffset, float yoffset, GLboolean constrainPitch = true)
{
xoffset *= MouseSensitivity;
yoffset *= MouseSensitivity;
Yaw += xoffset;
Pitch += yoffset;
if (constrainPitch)
{
Pitch = glm::clamp(Pitch, -89.0f, 89.0f);
}
updateCameraVectors();
}
void ProcessMouseScroll(float yoffset)
{
Zoom -= (float)yoffset;
Zoom = glm::clamp(Zoom, 1.0f, 45.0f);
}
private:
void updateCameraVectors()
{
glm::vec3 front;
front.x = cos(glm::radians(Yaw)) * cos(glm::radians(Pitch));
front.y = sin(glm::radians(Pitch));
front.z = sin(glm::radians(Yaw)) * cos(glm::radians(Pitch));
Front = glm::normalize(front);
Right = glm::normalize(glm::cross(Front, WorldUp));
Up = glm::normalize(glm::cross(Right, Front));
}
};
#endif
이런식으로 카메라에 필요한 코드를 옮김
#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 "Camera.h"
#include "Shader.h"
#include "stb_image.h"
//전방선언
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void process_input(GLFWwindow *window);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
//윈도우 사이즈
constexpr int windowWidth = 1280;
constexpr int windowHeight = 720;
//deltaTime용
float deltaTime = 0.0f;
float lastFrame = 0.0f;
//카메라 클래스
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
float lastX = 800.0f / 2.0;
float lastY = 600.0 / 2.0;
bool firstMouse = true;
int main()
{
//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);
//마우스 이동 인풋
glfwSetCursorPosCallback(window, mouse_callback);
//마우스 스크롤 인풋
glfwSetScrollCallback(window, scroll_callback);
//마우스 창에 가두기
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
//z,depth 버퍼
glEnable(GL_DEPTH_TEST);
// 쉐이더 생성
Shader shaderProgram(
"D:/01_Develope/CPP/learn_opengl/learn_opengl/VertexShaderSource.vsh",
"D:/01_Develope/CPP/learn_opengl/learn_opengl/FragmentShaderSource.fsh");
//--------------------------------------
//버텍스 버퍼, 버텍스 어레이, 버텍스 속성 링킹
//--------------------------------------
float vertices[] =
{
//포지션 텍스처 좌표
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f
};
glm::vec3 cubePositions[] =
{
glm::vec3( 0.0f, 0.0f, 0.0f),
glm::vec3( 2.0f, 5.0f, -15.0f),
glm::vec3(-1.5f, -2.2f, -2.5f),
glm::vec3(-3.8f, -2.0f, -12.3f),
glm::vec3( 2.4f, -0.4f, -3.5f),
glm::vec3(-1.7f, 3.0f, -7.5f),
glm::vec3( 1.3f, -2.0f, -2.5f),
glm::vec3( 1.5f, 2.0f, -2.5f),
glm::vec3( 1.5f, 0.2f, -1.5f),
glm::vec3(-1.3f, 1.0f, -1.5f)
};
// 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);
// 버텍스 속성 링킹
//position
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 5, (void*)0);
glEnableVertexAttribArray(0);
//텍스쳐 버텍스
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 5, (void*)(sizeof(float) * 3));
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 | GL_DEPTH_BUFFER_BIT);
//deltaTime 초기화
float currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
//텍스쳐 유닛 사용
glActiveTexture(GL_TEXTURE0); //0번 채널
glBindTexture(GL_TEXTURE_2D, texture1);
glActiveTexture(GL_TEXTURE1); //1번 채널
glBindTexture(GL_TEXTURE_2D, texture2);
shaderProgram.useShader();
// 모델, 뷰, 투영 행렬
glm::mat4 view;
view = camera.GetViewMatrix();
glm::mat4 projection = glm::mat4(1.0f);
projection = glm::perspective(glm::radians(camera.Zoom), (float)windowWidth/windowHeight, 0.1f, 100.0f);
uint32_t viewLoc = glGetUniformLocation(shaderProgram.shaderProgramID, "view");
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
uint32_t projectionLoc = glGetUniformLocation(shaderProgram.shaderProgramID, "projection");
glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection));
glBindVertexArray(VAO);
for(unsigned int i = 0; i < 10; i++)
{
// calculate the model matrix for each object and pass it to shader before drawing
glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, cubePositions[i]);
float angle = glfwGetTime() * 25.0f;
model = glm::rotate(model, glm::radians(angle * 10), glm::vec3(1.0f, 0.3f, 0.5f));
uint32_t modelLoc = glGetUniformLocation(shaderProgram.shaderProgramID, "model");
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
glDrawArrays(GL_TRIANGLES, 0, 36);
}
//VAO 활성화
//glBindVertexArray(VAO);
// 그리기
//glDrawArrays(GL_TRIANGLES, 0, 36);
//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_W) == GLFW_PRESS)
camera.ProcessKeyboard(FORWARD, deltaTime);
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
camera.ProcessKeyboard(BACKWARD, deltaTime);
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
camera.ProcessKeyboard(LEFT, deltaTime);
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
camera.ProcessKeyboard(RIGHT, deltaTime);
}
//마우스 입력 콜백
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
if (firstMouse)
{
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
float xoffset = xpos - lastX;
float yoffset = lastY - ypos;
lastX = xpos;
lastY = ypos;
camera.ProcessMouseMovement(xoffset, yoffset, true);
}
//스크롤 입력 콜백
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
camera.ProcessMouseScroll(yoffset);
}
끝!