#define GLM_ENABLE_EXPERIMENTAL
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/quaternion.hpp>
#include <iostream>
#include <fstream>
#include <string>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
// Buffers and Shader Program
GLuint VBO, VAO, EBO, shaderProgram;
// Arcball Variables
glm::vec3 lastPos;
glm::vec3 currPos;
bool isDragging = false;
bool isRotating = false; // Rotation 상태
glm::vec2 lastCursorPos; // 이전 마우스 커서 위치
glm::mat4 translationMatrix = glm::mat4(1.0f);
glm::quat rotationQuat = glm::quat(1.0f, 0.0f, 0.0f, 0.0f);
float zoomLevel = 3.0f; // 초기 줌 레벨
GLuint textures[6]; // 6개의 텍스처 저장
// ReadFile
std::string ReadFile(const char* filePath) {
std::ifstream file(filePath, std::ios::in);
if (!file.is_open()) {
std::cerr << "Failed to open file: " << filePath << std::endl;
return "";
}
std::string content, line;
while (std::getline(file, line)) {
content += line + "\\n";
}
file.close();
return content;
}
// CreateShaderProgram
GLuint CreateShaderProgram(const char* vertexPath, const char* fragmentPath) {
std::string vsCode = ReadFile(vertexPath);
std::string fsCode = ReadFile(fragmentPath);
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
const char* vsSource = vsCode.c_str();
glShaderSource(vertexShader, 1, &vsSource, NULL);
glCompileShader(vertexShader);
GLint success;
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success) {
char infoLog[512];
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
std::cerr << "Error compiling vertex shader: " << infoLog << std::endl;
}
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
const char* fsSource = fsCode.c_str();
glShaderSource(fragmentShader, 1, &fsSource, NULL);
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success) {
char infoLog[512];
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
std::cerr << "Error compiling fragment shader: " << infoLog << std::endl;
}
GLuint program = glCreateProgram();
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
glLinkProgram(program);
glGetProgramiv(program, GL_LINK_STATUS, &success);
if (!success) {
char infoLog[512];
glGetProgramInfoLog(program, 512, NULL, infoLog);
std::cerr << "Error linking program: " << infoLog << std::endl;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
return program;
}
// LoadTexture
GLuint LoadTexture(const char* filename) {
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
int width, height, nrChannels;
unsigned char* data = stbi_load(filename, &width, &height, &nrChannels, 0);
if (data) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
std::cout << "Texture loaded: " << filename << " (" << width << "x" << height << ")" << std::endl;
}
else {
std::cerr << "Failed to load texture: " << filename << std::endl;
}
stbi_image_free(data);
return texture;
}
// CreateCube
void CreateCube() {
GLfloat vertices[] = {
// Positions // Texture Coords
-1.0f, -1.0f, 1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, -1.0f, 0.0f, 0.0f,
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f,
1.0f, 1.0f, -1.0f, 1.0f, 1.0f,
1.0f, -1.0f, -1.0f, 1.0f, 0.0f,
};
GLuint indices[] = {
0, 1, 2, 2, 3, 0,
4, 5, 6, 6, 7, 4,
4, 5, 3, 3, 0, 4,
1, 2, 6, 6, 7, 1,
3, 2, 6, 6, 5, 3,
4, 7, 1, 1, 0, 4,
};
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (void*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
std::cout << "Cube created." << std::endl;
}
// Scroll Callback (Zoom 구현)
void ScrollCallback(GLFWwindow* window, double xoffset, double yoffset) {
zoomLevel -= (float)yoffset * 0.1f;
zoomLevel = glm::clamp(zoomLevel, 1.0f, 10.0f); // 줌 레벨 제한
}
// Mouse Button Callback
void MouseButtonCallback(GLFWwindow* window, int button, int action, int mods) {
if (button == GLFW_MOUSE_BUTTON_LEFT) {
if (action == GLFW_PRESS) {
isDragging = true;
double x, y;
glfwGetCursorPos(window, &x, &y);
lastCursorPos = glm::vec2(x, y);
}
else if (action == GLFW_RELEASE) {
isDragging = false;
}
}
if (button == GLFW_MOUSE_BUTTON_RIGHT) {
if (action == GLFW_PRESS) {
isRotating = true;
double x, y;
glfwGetCursorPos(window, &x, &y);
lastCursorPos = glm::vec2(x, y);
}
else if (action == GLFW_RELEASE) {
isRotating = false;
}
}
}
// Cursor Position Callback
void CursorPosCallback(GLFWwindow* window, double xpos, double ypos) {
glm::vec2 currentPos = glm::vec2(xpos, ypos);
glm::vec2 delta = currentPos - lastCursorPos;
if (isDragging) {
translationMatrix = glm::translate(translationMatrix, glm::vec3(delta.x * 0.01f, -delta.y * 0.01f, 0.0f));
lastCursorPos = currentPos;
}
if (isRotating) {
float angle = glm::length(delta) * 0.01f;
glm::vec3 axis = glm::vec3(-delta.y, delta.x, 0.0f);
if (glm::length(axis) > 0.0001f) {
axis = glm::normalize(axis);
rotationQuat = glm::rotate(rotationQuat, angle, axis);
}
lastCursorPos = currentPos;
}
}
int main() {
if (!glfwInit()) return -1;
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(800, 600, "Arcball Interaction", NULL, NULL);
if (!window) return -1;
glfwMakeContextCurrent(window);
if (glewInit() != GLEW_OK) return -1;
// Set up cube and shaders
shaderProgram = CreateShaderProgram("shader.vert", "shader.frag");
CreateCube();
textures[0] = LoadTexture("C:/Users/kshyu/OpenGLBasic/OpenGLVS/OpenGLVS/texture/front.jpg");
textures[1] = LoadTexture("C:/Users/kshyu/OpenGLBasic/OpenGLVS/OpenGLVS/texture/back.jpg");
textures[2] = LoadTexture("C:/Users/kshyu/OpenGLBasic/OpenGLVS/OpenGLVS/texture/left.jpg");
textures[3] = LoadTexture("C:/Users/kshyu/OpenGLBasic/OpenGLVS/OpenGLVS/texture/right.jpg");
textures[4] = LoadTexture("C:/Users/kshyu/OpenGLBasic/OpenGLVS/OpenGLVS/texture/top.jpg");
textures[5] = LoadTexture("C:/Users/kshyu/OpenGLBasic/OpenGLVS/OpenGLVS/texture/bottom.jpg");
glEnable(GL_DEPTH_TEST);
// Register callbacks
glfwSetScrollCallback(window, ScrollCallback);
glfwSetMouseButtonCallback(window, MouseButtonCallback);
glfwSetCursorPosCallback(window, CursorPosCallback);
while (!glfwWindowShouldClose(window)) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(shaderProgram);
glm::mat4 model = glm::mat4(1.0f);
model = translationMatrix * glm::toMat4(rotationQuat) * model;
glm::mat4 view = glm::lookAt(
glm::vec3(3.0f, 3.0f, zoomLevel),
glm::vec3(0.0f, 0.0f, 0.0f),
glm::vec3(0.0f, 1.0f, 0.0f)
);
glm::mat4 projection = glm::perspective(
glm::radians(45.0f), 800.0f / 600.0f, 0.1f, 100.0f
);
glm::mat4 mvp = projection * view * model;
GLuint mvpLoc = glGetUniformLocation(shaderProgram, "MVP");
glUniformMatrix4fv(mvpLoc, 1, GL_FALSE, glm::value_ptr(mvp));
for (int i = 0; i < 6; i++) {
glUniform1i(glGetUniformLocation(shaderProgram, "faceIndex"), i);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textures[i]);
glUniform1i(glGetUniformLocation(shaderProgram, "textures[0]"), 0);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, (void*)(i * 6 * sizeof(GLuint)));
}
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
shader.frag
#version 460
in vec2 TexCoords; // Vertex Shader에서 전달받은 텍스처 좌표
out vec4 FragColor; // 최종 픽셀 색상 출력
uniform sampler2D textures[6]; // 6개의 텍스처 샘플러
uniform int faceIndex; // 현재 렌더링 중인 면의 인덱스
void main() {
FragColor = texture(textures[faceIndex], TexCoords); // 현재 면에 해당하는 텍스처 샘플링
}
shader.vert
#version 460
layout(location = 0) in vec3 pos; // 정점 위치
layout(location = 1) in vec2 texCoord; // 텍스처 좌표
out vec2 TexCoords; // 텍스처 좌표를 Fragment Shader로 전달
uniform mat4 MVP; // 모델-뷰-투영 행렬
void main() {
gl_Position = MVP * vec4(pos, 1.0); // 모델-뷰-투영 행렬을 적용하여 정점 위치 계산
TexCoords = texCoord; // 텍스처 좌표를 그대로 전달
}