OpenGL本身并没有摄像机（Camera）的概念但可以通过把场景中的所有物体往反方向移动的方式模拟出摄像机，产生一种摄像机在移动的感觉。

glm::vec3 camera_pos = glm::vec3(0.0f, 0.0f, 3.0f);

z轴指向屏幕，若摄像机向后移动则它沿着z轴正方向移动。

glm::vec3 camera_target = glm::vec3(0.0f, 0.0f, 0.0f);
glm::vec3 camera_direction = glm::mormalize(camera_pos - camera_target);

glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f);
glm::vec3 camera_right = glm::normalize(glm::cross(up, camera_direction));

glm::vec3 camera_up = glm::cross(camera_direction, camera_right);

$$LookAt = \begin{bmatrix} \color{red}{R_x} & \color{red}{R_y} & \color{red}{R_z} & 0 \\ \color{green}{U_x} & \color{green}{U_y} & \color{green}{U_z} & 0 \\ \color{blue}{D_x} & \color{blue}{D_y} & \color{blue}{D_z} & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} * \begin{bmatrix} 1 & 0 & 0 & -\color{purple}{P_x} \\ 0 & 1 & 0 & -\color{purple}{P_y} \\ 0 & 0 & 1 & -\color{purple}{P_z} \\ 0 & 0 & 0 & 1 \end{bmatrix}$$

GLM提供了这些支持。只需要定义一个摄像机位置、一个目标位置和一个表示世界空间中的上向量的向量（用于计算摄像机右向量），接着GLM就会创建一个LookAt矩阵，可以用作观察矩阵：

glm::mat4 view = glm::lookAt(gLm::(0.0f, 0.0f, 3.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));

glm::lookAt()函数需要一个位置、目标和上向量。

float radius = 10.0f;
float cam_x = std::sin(glfwGetTime()) * radius;
float cam_z = std::cos(glfwGetTime()) * radius;
glm::mat4 view = glm::lookAt(glm::vec3(cam_x, 0.0f, cam_z), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));

#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <iostream>

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

void FramebufferSizeCallback(GLFWwindow* window, int width, int height) {
glViewport(0, 0, width, height);
}

void ProcessInput(GLFWwindow* window) {
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
glfwSetWindowShouldClose(window, true);
}
}

const unsigned int window_width = 800;
const unsigned int window_height = 600;

int main() {
if (!glfwInit()) {
std::cout << "Failed to initialize GLFW" << std::endl;
return -1;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(window_width, window_height, "Hello OpenGL", NULL, NULL);
if (window == NULL) {
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, FramebufferSizeCallback);
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
glEnable(GL_DEPTH_TEST);
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 cube_positions[] = {
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)
};
unsigned vbo, vao;
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
unsigned int texture_1, texture_2;
glGenTextures(1, &texture_1);
glBindTexture(GL_TEXTURE_2D, texture_1);
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 tex_width, tex_height, color_channels;
unsigned char* data = stbi_load("src/container.jpg", &tex_width, &tex_height, &color_channels, 0);
if (data) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tex_width, tex_height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else {
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);
glGenTextures(1, &texture_2);
glBindTexture(GL_TEXTURE_2D, texture_2);
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("src/awesomeface.png", &tex_width, &tex_height, &color_channels, 0);
if (data) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_width, tex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else {
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);
glm::mat4 projection = glm::mat4(1.0f);
projection = glm::perspective(glm::radians(45.0f), (float)window_width / (float)window_height, 0.1f, 100.0f);
while (!glfwWindowShouldClose(window)) {
ProcessInput(window);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture_1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture_2);
float cam_x = std::sin(glfwGetTime()) * radius;
float cam_z = std::cos(glfwGetTime()) * radius;
glm::mat4 view = glm::lookAt(glm::vec3(cam_x, 0.0f, cam_z), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
glBindVertexArray(vao);
for (unsigned int i = 0; i < 10; ++i) {
glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, cube_positions[i]);
float angle = 20.0f * i;
model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
glDrawArrays(GL_TRIANGLES, 0, 36);
}
glfwSwapBuffers(window);
glfwPollEvents();
}
glDeleteVertexArrays(1, &vao);
glDeleteBuffers(1, &vbo);
glfwTerminate();
return 0;
}

glm::vec3 camera_pos = glm::vec3(0.0f, 0.0f, 3.0f);
glm::vec3 camera_front = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 camera_up = glm::vec3(0.0f, 1.0f, 0.0f);

glm::mat4 view = glm::lookAt(camera_pos, camera_pos + camera_front, camera_up);

float camera_speed = 2.5f * delta_time;
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) {
camera_pos += camera_speed * camera_front;
}
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) {
camera_pos -= camera_speed * camera_front;
}
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) {
camera_pos -= glm::normalize(glm::cross(camera_front, camera_up)) * camera_speed;
}
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) {
camera_pos += glm::normalize(glm::cross(camera_front, camera_up)) * camera_speed;
}

float current_frame = glfwGetTime();
delta_time = current_frame - last_frame;
last_frame = current_frame;

#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <iostream>

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

const unsigned int window_width = 800;
const unsigned int window_height = 600;

glm::vec3 camera_pos = glm::vec3(0.0f, 0.0f, 3.0f);
glm::vec3 camera_front = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 camera_up = glm::vec3(0.0f, 1.0f, 0.0f);

float delta_time = 0.0f;
float last_frame = 0.0f;

void FramebufferSizeCallback(GLFWwindow* window, int width, int height) {
glViewport(0, 0, width, height);
}

void ProcessInput(GLFWwindow* window) {
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
glfwSetWindowShouldClose(window, true);
}
float camera_speed = 2.5f * delta_time;
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) {
camera_pos += camera_speed * camera_front;
}
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) {
camera_pos -= camera_speed * camera_front;
}
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) {
camera_pos -= glm::normalize(glm::cross(camera_front, camera_up)) * camera_speed;
}
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) {
camera_pos += glm::normalize(glm::cross(camera_front, camera_up)) * camera_speed;
}
}

int main() {
if (!glfwInit()) {
std::cout << "Failed to initialize GLFW" << std::endl;
return -1;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(window_width, window_height, "Hello OpenGL", NULL, NULL);
if (window == NULL) {
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, FramebufferSizeCallback);
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
glEnable(GL_DEPTH_TEST);
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 cube_positions[] = {
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)
};
unsigned vbo, vao;
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
unsigned int texture_1, texture_2;
glGenTextures(1, &texture_1);
glBindTexture(GL_TEXTURE_2D, texture_1);
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 tex_width, tex_height, color_channels;
unsigned char* data = stbi_load("src/container.jpg", &tex_width, &tex_height, &color_channels, 0);
if (data) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tex_width, tex_height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else {
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);
glGenTextures(1, &texture_2);
glBindTexture(GL_TEXTURE_2D, texture_2);
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("src/awesomeface.png", &tex_width, &tex_height, &color_channels, 0);
if (data) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_width, tex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else {
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);
glm::mat4 projection = glm::mat4(1.0f);
projection = glm::perspective(glm::radians(45.0f), (float)window_width / (float)window_height, 0.1f, 100.0f);
while (!glfwWindowShouldClose(window)) {
float current_frame = glfwGetTime();
delta_time = current_frame - last_frame;
last_frame = current_frame;
ProcessInput(window);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture_1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture_2);
float cam_x = std::sin(glfwGetTime()) * radius;
float cam_z = std::cos(glfwGetTime()) * radius;
glm::mat4 view = glm::lookAt(camera_pos, camera_pos + camera_front, camera_up);
glBindVertexArray(vao);
for (unsigned int i = 0; i < 10; ++i) {
glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, cube_positions[i]);
float angle = 20.0f * i;
model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
glDrawArrays(GL_TRIANGLES, 0, 36);
}
glfwSwapBuffers(window);
glfwPollEvents();
}
glDeleteVertexArrays(1, &vao);
glDeleteBuffers(1, &vbo);
glfwTerminate();
return 0;
}

direction.y = std::sin(glm::radians(pitch));

direction.x = std::cos(glm::radians(pitch));
direction.z = std::cos(glm::radians(pitch));

direction.x = std::cos(glm::radians(pitch)) * std::cos(glm::radians(yaw));
direction.z = std::cos(glm::radians(pitch)) * std::sin(glm::radians(yaw));

glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

void CursorCallback(GLFWwindow* window, double xpos, double ypos);

glfwSetCursorPosCallback(window, CursorCallback);

• 计算鼠标距上一帧的偏移量。
• 把偏移量添加到摄像机的俯仰角和偏航角中。
• 对偏航角和俯仰角进行最大和最小值的限制。
• 计算方向向量。

float cursor_last_x = 400, cursor_last_y = 300;

float x_offset = x_pos - cursor_last_x;
float y_offset = cursor_last_y - y_pos;
cursor_last_x = x_pos;
cursor_last_y = y_pos;
float sensitivity = 0.05f;
x_offset *= sensitivity;
y_offset *= sensitivity;

yaw += x_offset;
pitch += y_offset;

if (pitch > 89.0f) {
pitch = 89.0f;
}
if (pitch < -89.0f) {
pitch = -89.0f;
}

if (first_mouse) {
cursor_last_x = x_pos;
cursor_last_y = y_pos;
first_mouse = false;
}

void ScrollCallback(GLFWwindow* window, double x_offset, double y_offset) {
if (fov >= 1.0f && fov <= 45.0f) {
fov -= y_offset;
}
if (fov <= 1.0f) {
fov = 1.0f;
}
if (fov >= 45.0f) {
fov = 45.0f;
}
}

glm::mat4 projection = glm::perspective(glm::radians(fov), (float)window_width / (float)window_height, 0.1f, 100.0f);

glfwSetScrollCallback(window, ScrollCallback);

#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <iostream>

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

const unsigned int window_width = 800;
const unsigned int window_height = 600;

glm::vec3 camera_pos = glm::vec3(0.0f, 0.0f, 3.0f);
glm::vec3 camera_front = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 camera_up = glm::vec3(0.0f, 1.0f, 0.0f);

bool first_mouse = true;
float yaw = -90.0f;
float pitch = 0.0f;
float cursor_last_x = window_width / 2.0f;
float cursor_last_y = window_height / 2.0f;
float fov = 45.0f;

float delta_time = 0.0f;
float last_frame = 0.0f;

void FramebufferSizeCallback(GLFWwindow* window, int width, int height) {
glViewport(0, 0, width, height);
}

void ProcessInput(GLFWwindow* window) {
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
glfwSetWindowShouldClose(window, true);
}
float camera_speed = 2.5f * delta_time;
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) {
camera_pos += camera_speed * camera_front;
}
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) {
camera_pos -= camera_speed * camera_front;
}
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) {
camera_pos -= glm::normalize(glm::cross(camera_front, camera_up)) * camera_speed;
}
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) {
camera_pos += glm::normalize(glm::cross(camera_front, camera_up)) * camera_speed;
}
}

void CursorCallback(GLFWwindow* window, double x_pos, double y_pos) {
if (first_mouse) {
cursor_last_x = x_pos;
cursor_last_y = y_pos;
first_mouse = false;
}
float x_offset = x_pos - cursor_last_x;
float y_offset = cursor_last_y - y_pos;
cursor_last_x = x_pos;
cursor_last_y = y_pos;
float sensitivity = 0.05f;
x_offset *= sensitivity;
y_offset *= sensitivity;
yaw += x_offset;
pitch += y_offset;
if (pitch > 89.0f) {
pitch = 89.0f;
}
if (pitch < -89.0f) {
pitch = -89.0f;
}
glm::vec3 front_buffer;
camera_front = glm::normalize(front_buffer);
}

void ScrollCallback(GLFWwindow* window, double x_offset, double y_offset) {
if (fov >= 1.0f && fov <= 45.0f) {
fov -= y_offset;
}
if (fov <= 1.0f) {
fov = 1.0f;
}
if (fov >= 45.0f) {
fov = 45.0f;
}
}

int main() {
if (!glfwInit()) {
std::cout << "Failed to initialize GLFW" << std::endl;
return -1;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(window_width, window_height, "Hello OpenGL", NULL, NULL);
if (window == NULL) {
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, FramebufferSizeCallback);
glfwSetCursorPosCallback(window, CursorCallback);
glfwSetScrollCallback(window, ScrollCallback);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
glEnable(GL_DEPTH_TEST);
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 cube_positions[] = {
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)
};
unsigned vbo, vao;
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
unsigned int texture_1, texture_2;
glGenTextures(1, &texture_1);
glBindTexture(GL_TEXTURE_2D, texture_1);
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 tex_width, tex_height, color_channels;
unsigned char* data = stbi_load("src/container.jpg", &tex_width, &tex_height, &color_channels, 0);
if (data) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tex_width, tex_height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else {
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);
glGenTextures(1, &texture_2);
glBindTexture(GL_TEXTURE_2D, texture_2);
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("src/awesomeface.png", &tex_width, &tex_height, &color_channels, 0);
if (data) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_width, tex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else {
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);
while (!glfwWindowShouldClose(window)) {
float current_frame = glfwGetTime();
delta_time = current_frame - last_frame;
last_frame = current_frame;
ProcessInput(window);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture_1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture_2);
float cam_x = std::sin(glfwGetTime()) * radius;
float cam_z = std::cos(glfwGetTime()) * radius;
glm::mat4 projection = glm::perspective(glm::radians(fov), (float)window_width / (float)window_height, 0.1f, 100.0f);
glm::mat4 view = glm::lookAt(camera_pos, camera_pos + camera_front, camera_up);
glBindVertexArray(vao);
for (unsigned int i = 0; i < 10; ++i) {
glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, cube_positions[i]);
float angle = 20.0f * i;
model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
glDrawArrays(GL_TRIANGLES, 0, 36);
}
glfwSwapBuffers(window);
glfwPollEvents();
}
glDeleteVertexArrays(1, &vao);
glDeleteBuffers(1, &vbo);
glfwTerminate();
return 0;
}

#pragma once

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <string>
#include <fstream>
#include <sstream>
#include <iostream>

enum Camera_Movement {
FORWARD,
BACKWARD,
LEFT,
RIGHT
};

const float yaw_init = -90.0f, pitch_init = 0.0f;
const float speed_init = 2.5f, sensitivity_init = 0.1f, fov_init = 45.0f;

class Camera {
public:
glm::vec3 position, front, up, right, world_up;
float yaw, pitch, movement_speed, mouse_sensitivity, fov;

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_init, float _pitch = pitch_init) {
front = glm::vec3(0.0f, 0.0f, -1.0f);
movement_speed = speed_init;
mouse_sensitivity = sensitivity_init;
fov = fov_init;
position = _position;
world_up = _up;
yaw = _yaw;
pitch = _pitch;
UpdateCameraVectors();
}

Camera(float pos_x, float pos_y, float pos_z, float up_x, float up_y, float up_z, float _yaw, float _pitch) {
front = glm::vec3(0.0f, 0.0f, -1.0f);
movement_speed = speed_init;
mouse_sensitivity = sensitivity_init;
fov = fov_init;
position = glm::vec3(pos_x, pos_y, pos_z);
world_up = glm::vec3(up_x, up_y, up_z);
yaw = _yaw;
pitch = _pitch;
UpdateCameraVectors();
}

glm::mat4 GetViewMatrix() {
return glm::lookAt(position, position + front, up);
}

void ProcessKeyboard(Camera_Movement direction, float delta_time) {
float velocity = movement_speed * delta_time;
if (direction == FORWARD) {
position += front * velocity;
}
if (direction == BACKWARD) {
position -= front * velocity;
}
if (direction == LEFT) {
position -= right * velocity;
}
if (direction == RIGHT) {
position += right * velocity;
}
}

void ProcessMouseMovement(float x_offset, float y_offset, GLboolean constrain_pitch = true) {
x_offset *= mouse_sensitivity;
y_offset *= mouse_sensitivity;
yaw += x_offset;
pitch += y_offset;
if (constrain_pitch) {
if (pitch > 89.0f) {
pitch = 89.0f;
}
if (pitch < -89.0f) {
pitch = -89.0f;
}
}
UpdateCameraVectors();
}

void ProcessMouseScroll(float y_offset) {
if (fov >= 1.0f && fov <= 45.0f) {
fov -= y_offset;
}
if (fov <= 1.0f) {
fov = 1.0f;
}
if (fov >= 45.0f) {
fov = 45.0f;
}
}

private:
void UpdateCameraVectors() {
glm::vec3 front_buffer;
front = glm::normalize(front_buffer);
right = glm::normalize(glm::cross(front, world_up));
up = glm::normalize(glm::cross(right, front));
}
};

#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <iostream>
#include "camera.h"

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

const unsigned int window_width = 800;
const unsigned int window_height = 600;

Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
float mouse_last_x = window_width / 2.0f;
float mouse_last_y = window_height / 2.0f;
bool first_mouse = true;

float delta_time = 0.0f;
float last_frame = 0.0f;

void FramebufferSizeCallback(GLFWwindow* window, int width, int height) {
glViewport(0, 0, width, height);
}

void ProcessInput(GLFWwindow* window) {
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
glfwSetWindowShouldClose(window, true);
}
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) {
camera.ProcessKeyboard(FORWARD, delta_time);
}
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) {
camera.ProcessKeyboard(BACKWARD, delta_time);
}
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) {
camera.ProcessKeyboard(LEFT, delta_time);
}
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) {
camera.ProcessKeyboard(RIGHT, delta_time);
}
}

void MouseCallback(GLFWwindow* window, double x_pos, double y_pos) {
if (first_mouse) {
mouse_last_x = x_pos;
mouse_last_y = y_pos;
first_mouse = false;
}
float x_offset = x_pos - mouse_last_x;
float y_offset = mouse_last_y - y_pos;
mouse_last_x = x_pos;
mouse_last_y = y_pos;
camera.ProcessMouseMovement(x_offset, y_offset);
}

void ScrollCallback(GLFWwindow* window, double x_offset, double y_offset) {
camera.ProcessMouseScroll(y_offset);
}

int main() {
if (!glfwInit()) {
std::cout << "Failed to initialize GLFW" << std::endl;
return -1;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(window_width, window_height, "Hello OpenGL", NULL, NULL);
if (window == NULL) {
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, FramebufferSizeCallback);
glfwSetCursorPosCallback(window, MouseCallback);
glfwSetScrollCallback(window, ScrollCallback);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
glEnable(GL_DEPTH_TEST);
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 cube_positions[] = {
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)
};
unsigned vbo, vao;
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
unsigned int texture_1, texture_2;
glGenTextures(1, &texture_1);
glBindTexture(GL_TEXTURE_2D, texture_1);
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 tex_width, tex_height, color_channels;
unsigned char* data = stbi_load("src/container.jpg", &tex_width, &tex_height, &color_channels, 0);
if (data) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tex_width, tex_height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else {
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);
glGenTextures(1, &texture_2);
glBindTexture(GL_TEXTURE_2D, texture_2);
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("src/awesomeface.png", &tex_width, &tex_height, &color_channels, 0);
if (data) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_width, tex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else {
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);
while (!glfwWindowShouldClose(window)) {
float current_frame = glfwGetTime();
delta_time = current_frame - last_frame;
last_frame = current_frame;
ProcessInput(window);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture_1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture_2);
float cam_x = std::sin(glfwGetTime()) * radius;
float cam_z = std::cos(glfwGetTime()) * radius;
glm::mat4 projection = glm::perspective(glm::radians(camera.fov), (float)window_width / (float)window_height, 0.1f, 100.0f);
glm::mat4 view = camera.GetViewMatrix();
glBindVertexArray(vao);
for (unsigned int i = 0; i < 10; ++i) {
glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, cube_positions[i]);
float angle = 20.0f * i;
model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
}