๐ฎ ์คํGL๋ก ์์ํ๋ C++ 3D ๊ทธ๋ํฝ์ค ํ๋ก๊ทธ๋๋ฐ ์์ ์ ๋ณต ํํ ๋ฆฌ์ผ (2025๋ ์ต์ ๋ฒ์ )

์๋ , ๊ทธ๋ํฝ์ค ํ๋ก๊ทธ๋๋ฐ์ ๊ด์ฌ ์๋ ์น๊ตฌ์ผ! ๐๏ธ ์ค๋์ C++๊ณผ OpenGL์ ํ์ฉํ ๊ทธ๋ํฝ์ค ํ๋ก๊ทธ๋๋ฐ์ ์ธ๊ณ๋ก ํจ๊ป ์ฌํ์ ๋ ๋๋ณผ๊น? 2025๋ ํ์ฌ ๊ฒ์ ๊ฐ๋ฐ๋ถํฐ ์๋ฎฌ๋ ์ด์ , ๋ฐ์ดํฐ ์๊ฐํ๊น์ง ๋ค์ํ ๋ถ์ผ์์ ํ์ฉ๋๋ OpenGL์ ๋ชจ๋ ๊ฒ์ ์๋ ค์ค๊ฒ. ์ฝ๋ ํ ์ค ํ ์ค์ด ํ๋ฉด์์ ์ด์ ์์ง์ด๋ ๋ง๋ฒ ๊ฐ์ ๊ฒฝํ์ ํจ๊ป ํด๋ณด์!
์ด ํํ ๋ฆฌ์ผ์ ๋ง์คํฐํ๋ฉด ๋๋ ๋ฉ์ง 3D ๊ทธ๋ํฝ์ค ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ง๋ค ์ ์์ด!
๐ ๋ชฉ์ฐจ
- OpenGL๊ณผ C++์ ๊ธฐ๋ณธ ์ดํดํ๊ธฐ
- ๊ฐ๋ฐ ํ๊ฒฝ ์ค์ ํ๊ธฐ (2025๋ ์ต์ ๋ฒ์ )
- ์ฒซ ๋ฒ์งธ OpenGL ์ฐฝ ๋ง๋ค๊ธฐ
- ๊ธฐ๋ณธ ๋ํ ๊ทธ๋ฆฌ๊ธฐ์ ์ ฐ์ด๋ ํ๋ก๊ทธ๋๋ฐ
- ํ ์ค์ฒ์ ์กฐ๋ช ํจ๊ณผ ์ ์ฉํ๊ธฐ
- 3D ๋ชจ๋ธ ๋ก๋ฉ๊ณผ ๋ ๋๋ง
- ์นด๋ฉ๋ผ ์์คํ ๊ณผ ์ฌ์ฉ์ ์ํธ์์ฉ
- ๊ณ ๊ธ ๊ทธ๋ํฝ์ค ๊ธฐ๋ฒ ํ๊ตฌ
- ์ฑ๋ฅ ์ต์ ํ ์ ๋ต
- ์ค์ ํ๋ก์ ํธ: ๋ฏธ๋ ๊ฒ์ ์์ง ๋ง๋ค๊ธฐ
1. OpenGL๊ณผ C++์ ๊ธฐ๋ณธ ์ดํดํ๊ธฐ ๐งฉ
OpenGL(Open Graphics Library)์ 2D ๋ฐ 3D ๊ทธ๋ํฝ์ค๋ฅผ ๋ ๋๋งํ๊ธฐ ์ํ ํฌ๋ก์ค ํ๋ซํผ API์ผ. 1992๋ ์ ์ฒ์ ๋ฑ์ฅํ ์ดํ๋ก ๊ณ์ ๋ฐ์ ํด์๊ณ , 2025๋ ํ์ฌ๋ OpenGL 5.0๊น์ง ๋์์ด. C++๊ณผ ํจ๊ป ์ฌ์ฉํ๋ฉด ์ ๋ง ๊ฐ๋ ฅํ ๊ทธ๋ํฝ์ค ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ง๋ค ์ ์์ง!
OpenGL์ ํน์ง ๐ก
1. ํฌ๋ก์ค ํ๋ซํผ: Windows, macOS, Linux, ๋ชจ๋ฐ์ผ ๋ฑ ๋ค์ํ ํ๋ซํผ์์ ๋์
2. ํ๋์จ์ด ๊ฐ์: GPU๋ฅผ ์ง์ ํ์ฉํด ๋น ๋ฅธ ๋ ๋๋ง ๊ฐ๋ฅ
3. ํ์ฅ์ฑ: ๋ค์ํ ํ์ฅ ๊ธฐ๋ฅ์ ํตํด ์ต์ ๊ทธ๋ํฝ์ค ๊ธฐ์ ์ง์
4. ์ฐ์ ํ์ค: ๊ฒ์ ๊ฐ๋ฐ, CAD, ์๋ฎฌ๋ ์ด์ ๋ฑ ๋ค์ํ ๋ถ์ผ์์ ํ์ฉ
C++์ OpenGL๊ณผ ํจ๊ป ์ฌ์ฉํ๊ธฐ์ ์๋ฒฝํ ์ธ์ด์ผ. ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ์ ์ฑ๋ฅ ์ต์ ํ๊ฐ ์ค์ํ ๊ทธ๋ํฝ์ค ํ๋ก๊ทธ๋๋ฐ์์ C++์ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ๋ค์ด ๋น์ ๋ฐํ์ง. ํนํ 2025๋ ์๋ C++23 ํ์ค์ด ๋๋ฆฌ ๋ณด๊ธ๋๋ฉด์ ๋ ๊ฐ๊ฒฐํ๊ณ ํจ์จ์ ์ธ ์ฝ๋ ์์ฑ์ด ๊ฐ๋ฅํด์ก์ด.
OpenGL์ ๊ทธ๋ํฝ์ค ํ์ดํ๋ผ์ธ์ด๋ผ๋ ๊ฐ๋ ์ ์ค์ฌ์ผ๋ก ๋์ํด. ์ด ํ์ดํ๋ผ์ธ์ 3D ๋ชจ๋ธ ๋ฐ์ดํฐ๊ฐ 2D ํ๋ฉด์ ํ์๋๊ธฐ๊น์ง์ ์ฌ๋ฌ ๋จ๊ณ๋ฅผ ํฌํจํ๊ณ ์์ด. ๊ธฐ๋ณธ์ ์ธ ํ์ดํ๋ผ์ธ ๋จ๊ณ๋ ๋ค์๊ณผ ๊ฐ์:
- ์ ์ ์ฒ๋ฆฌ(Vertex Processing): 3D ๊ณต๊ฐ์ ์ ์ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌ
- ํ ์ ๋ ์ด์ (Tessellation): ํ๋ฉด์ ๋ ์์ ๊ธฐํํ์ ์์๋ก ๋ถํ
- ์ง์ค๋ฉํธ๋ฆฌ ์ฒ๋ฆฌ(Geometry Processing): ๊ธฐํํ์ ๋ฐ์ดํฐ ์ถ๊ฐ ์ฒ๋ฆฌ
- ๋์คํฐํ(Rasterization): ๋ฒกํฐ ๋ฐ์ดํฐ๋ฅผ ํฝ์ ๋ก ๋ณํ
- ํ๋๊ทธ๋จผํธ ์ฒ๋ฆฌ(Fragment Processing): ํฝ์ ๋ณ ์์ ๋ฐ ํจ๊ณผ ์ ์ฉ
- ์ถ๋ ฅ ๋ณํฉ(Output Merger): ์ต์ข ์ด๋ฏธ์ง ์์ฑ
์ด์ OpenGL๊ณผ C++์ ๊ธฐ๋ณธ ๊ฐ๋ ์ ์ดํดํ์ผ๋, ๊ฐ๋ฐ ํ๊ฒฝ์ ์ค์ ํ๋ ๋ฐฉ๋ฒ์ ์์๋ณผ๊น?
2. ๊ฐ๋ฐ ํ๊ฒฝ ์ค์ ํ๊ธฐ (2025๋ ์ต์ ๋ฒ์ ) ๐ ๏ธ
๊ทธ๋ํฝ์ค ํ๋ก๊ทธ๋๋ฐ์ ์์ํ๊ธฐ ์ ์ ๋จผ์ ๊ฐ๋ฐ ํ๊ฒฝ์ ์ ๋๋ก ์ค์ ํด์ผ ํด. 2025๋ ๊ธฐ์ค์ผ๋ก ๊ฐ์ฅ ์ต์ ์ ๋๊ตฌ๋ค์ ์ฌ์ฉํด๋ณผ๊ฒ!
ํ์ํ ๋๊ตฌ๋ค ๐งฐ
- C++ ์ปดํ์ผ๋ฌ: GCC 13.2, Clang 18.0, MSVC 19.40 ์ค ํ๋
- IDE: Visual Studio 2025, CLion 2025.1, VS Code + C++ ํ์ฅ
- OpenGL ๋ผ์ด๋ธ๋ฌ๋ฆฌ: OpenGL 5.0 ํธํ ๋๋ผ์ด๋ฒ
- ๋ณด์กฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ: GLFW, GLAD, GLM
- ๋น๋ ์์คํ : CMake 3.28 ์ด์
์ฌ๋ฅ๋ท์์๋ ๊ทธ๋ํฝ์ค ํ๋ก๊ทธ๋๋ฐ ๊ด๋ จ ๋ฉํ ๋ง ์๋น์ค๋ ์ ๊ณตํ๊ณ ์์ด. ํ๊ฒฝ ์ค์ ์ ์ด๋ ค์์ ๊ฒช๋๋ค๋ฉด ์ ๋ฌธ๊ฐ์ ๋์์ ๋ฐ์๋ณด๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ผ!
Windows์์ ํ๊ฒฝ ์ค์ ํ๊ธฐ
Windows์์๋ Visual Studio 2025 Community Edition์ ์ฌ์ฉํ๋ ๊ฒ์ด ๊ฐ์ฅ ํธ๋ฆฌํด. ๋ค์ ๋จ๊ณ๋ฅผ ๋ฐ๋ผํด๋ด:
- Visual Studio 2025 ์ค์น (C++ ๋ฐ์คํฌํฑ ๊ฐ๋ฐ ์ํฌ๋ก๋ ์ ํ)
- vcpkg ํจํค์ง ๋งค๋์ ์ค์น:
git clone https://github.com/Microsoft/vcpkg.git cd vcpkg bootstrap-vcpkg.bat vcpkg integrate install
- ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น:
vcpkg install glfw3:x64-windows vcpkg install glad:x64-windows vcpkg install glm:x64-windows
macOS์์ ํ๊ฒฝ ์ค์ ํ๊ธฐ
macOS์์๋ Xcode์ Homebrew๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์:
- Xcode ์ค์น (App Store์์)
- Homebrew ์ค์น:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
- ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น:
brew install glfw brew install glm brew install cmake
- GLAD ์ค์ (์น ์๋น์ค ์ด์ฉ):
https://glad.dav1d.de/ ์์ OpenGL ๋ฒ์ ์ ํ ํ ์์ฑ๋ ํ์ผ ๋ค์ด๋ก๋
Linux์์ ํ๊ฒฝ ์ค์ ํ๊ธฐ
Ubuntu ๊ธฐ์ค์ผ๋ก ์ค๋ช ํ ๊ฒ:
- ํ์ํ ํจํค์ง ์ค์น:
sudo apt update sudo apt install build-essential sudo apt install libglfw3-dev sudo apt install libglm-dev sudo apt install cmake
- GLAD ์ค์ (์น ์๋น์ค ์ด์ฉ):
https://glad.dav1d.de/ ์์ OpenGL ๋ฒ์ ์ ํ ํ ์์ฑ๋ ํ์ผ ๋ค์ด๋ก๋
๐ก GLAD์ด๋?
GLAD๋ OpenGL ํจ์ ํฌ์ธํฐ๋ฅผ ๋ก๋ํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ผ. OpenGL์ API๋ง ์ ์ํ๊ณ ์ค์ ๊ตฌํ์ ๊ทธ๋ํฝ ๋๋ผ์ด๋ฒ์ ์์กดํ๊ธฐ ๋๋ฌธ์, ๋ฐํ์์ ํจ์ ํฌ์ธํฐ๋ฅผ ๋ก๋ํด์ผ ํด. GLAD๊ฐ ์ด ๊ณผ์ ์ ๊ฐ์ํํด์ฃผ์ง!
CMake ํ๋ก์ ํธ ์ค์
๋ค์์ ๊ธฐ๋ณธ์ ์ธ CMakeLists.txt ํ์ผ์ด์ผ:
cmake_minimum_required(VERSION 3.28)
project(OpenGLTutorial VERSION 1.0)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(OpenGL REQUIRED)
find_package(glfw3 REQUIRED)
find_package(glm REQUIRED)
add_executable(${PROJECT_NAME} main.cpp glad.c)
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(${PROJECT_NAME} PRIVATE OpenGL::GL glfw glm::glm)
์ด์ ๊ฐ๋ฐ ํ๊ฒฝ์ด ์ค๋น๋์์ผ๋, ์ฒซ ๋ฒ์งธ OpenGL ์ฐฝ์ ๋ง๋ค์ด๋ณผ๊น?
3. ์ฒซ ๋ฒ์งธ OpenGL ์ฐฝ ๋ง๋ค๊ธฐ ๐ช
OpenGL๋ก ๋ฌด์ธ๊ฐ๋ฅผ ๊ทธ๋ฆฌ๊ธฐ ์ ์ ๋จผ์ ์ฐฝ์ ๋ง๋ค์ด์ผ ํด. GLFW ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ฉด ์ด ๊ณผ์ ์ด ๋งค์ฐ ๊ฐ๋จํด์ ธ. ๋ค์์ ๊ธฐ๋ณธ ์ฐฝ์ ์์ฑํ๋ ์ฝ๋์ผ:
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
// ์ฐฝ ํฌ๊ธฐ ๋ณ๊ฒฝ ์ฝ๋ฐฑ ํจ์
void framebuffer_size_callback(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);
}
int main() {
// GLFW ์ด๊ธฐํ
if (!glfwInit()) {
std::cerr << "GLFW ์ด๊ธฐํ ์คํจ" << std::endl;
return -1;
}
// OpenGL ๋ฒ์ ์ค์ (4.6)
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// ์ฐฝ ์์ฑ
GLFWwindow* window = glfwCreateWindow(800, 600, "์ฒซ ๋ฒ์งธ OpenGL ์ฐฝ", NULL, NULL);
if (!window) {
std::cerr << "GLFW ์ฐฝ ์์ฑ ์คํจ" << std::endl;
glfwTerminate();
return -1;
}
// ํ์ฌ ์ปจํ
์คํธ๋ก ์ค์
glfwMakeContextCurrent(window);
// ์ฐฝ ํฌ๊ธฐ ๋ณ๊ฒฝ ์ฝ๋ฐฑ ๋ฑ๋ก
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
// GLAD ๋ก๋
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
std::cerr << "GLAD ์ด๊ธฐํ ์คํจ" << std::endl;
return -1;
}
// ๋ ๋๋ง ๋ฃจํ
while (!glfwWindowShouldClose(window)) {
// ์
๋ ฅ ์ฒ๋ฆฌ
processInput(window);
// ๋ ๋๋ง
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// ๋ฒํผ ๊ต์ฒด ๋ฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ
glfwSwapBuffers(window);
glfwPollEvents();
}
// ์ข
๋ฃ
glfwTerminate();
return 0;
}
์ด ์ฝ๋๋ฅผ ์คํํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ์ฐฝ์ด ๋ํ๋ ๊ฑฐ์ผ:
์ฝ๋๋ฅผ ์์ธํ ์ดํด๋ณด์:
- GLFW ์ด๊ธฐํ:
glfwInit()
ํจ์๋ก GLFW ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด๊ธฐํํด. - OpenGL ๋ฒ์ ์ค์ :
glfwWindowHint()
ํจ์๋ก ์ฌ์ฉํ OpenGL ๋ฒ์ ์ ์ง์ ํด. - ์ฐฝ ์์ฑ:
glfwCreateWindow()
ํจ์๋ก ์ค์ ์ฐฝ์ ์์ฑํด. - OpenGL ์ปจํ
์คํธ ์ค์ :
glfwMakeContextCurrent()
ํจ์๋ก ํ์ฌ ์ค๋ ๋์ ์ปจํ ์คํธ๋ฅผ ์ค์ ํด. - GLAD ์ด๊ธฐํ: OpenGL ํจ์ ํฌ์ธํฐ๋ฅผ ๋ก๋ํด.
- ๋ ๋๋ง ๋ฃจํ: ์ฐฝ์ด ๋ซํ ๋๊น์ง ๊ณ์ํด์ ๊ทธ๋ฆฌ๊ธฐ ์์ ์ ์ํํด.
- ์ ๋ฆฌ:
glfwTerminate()
ํจ์๋ก GLFW ๋ฆฌ์์ค๋ฅผ ์ ๋ฆฌํด.
OpenGL์ ์ขํ๊ณ๋ ์ค์์ด (0,0)์ด๊ณ , ์ค๋ฅธ์ชฝ ์๊ฐ (1,1), ์ผ์ชฝ ์๋๊ฐ (-1,-1)์ธ ์ ๊ทํ๋ ์ขํ๊ณ๋ฅผ ์ฌ์ฉํด. ์ด ์ ์ ๊ธฐ์ตํด๋๋ฉด ๋์ค์ ๋ํ์ ๊ทธ๋ฆด ๋ ๋์์ด ๋ ๊ฑฐ์ผ!
์ฐฝ ์์ฑ ์ ์์ฃผ ๋ฐ์ํ๋ ๋ฌธ์ ์ ํด๊ฒฐ์ฑ ๐ง
-
๋ฌธ์ : "GLFW ์ด๊ธฐํ ์คํจ" ์ค๋ฅ
ํด๊ฒฐ์ฑ : ๊ทธ๋ํฝ ๋๋ผ์ด๋ฒ๊ฐ ์ต์ ๋ฒ์ ์ธ์ง ํ์ธํ๊ณ , ํ์ํ ์์คํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ค์น๋์ด ์๋์ง ํ์ธํด. -
๋ฌธ์ : "GLAD ์ด๊ธฐํ ์คํจ" ์ค๋ฅ
ํด๊ฒฐ์ฑ : GLAD๊ฐ ์ฌ๋ฐ๋ฅธ OpenGL ๋ฒ์ ์ ๋์์ผ๋ก ์์ฑ๋์๋์ง ํ์ธํ๊ณ , ๊ทธ๋ํฝ ์นด๋๊ฐ ํด๋น ๋ฒ์ ์ ์ง์ํ๋์ง ํ์ธํด. -
๋ฌธ์ : ์ฐฝ์ด ์ฆ์ ๋ซํ
ํด๊ฒฐ์ฑ : ๋ ๋๋ง ๋ฃจํ์ ์ค๋ฅ๊ฐ ์๋์ง ํ์ธํ๊ณ , ๋๋ฒ๊น ๋ฉ์์ง๋ฅผ ์ถ๊ฐํด์ ์ด๋์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋์ง ํ์ ํด.
์ด์ ๊ธฐ๋ณธ ์ฐฝ์ ๋ง๋ค์์ผ๋, ๋ค์ ๋จ๊ณ๋ก ๋์ด๊ฐ์ ์ค์ ๋ก ๋ํ์ ๊ทธ๋ ค๋ณผ๊น?
4. ๊ธฐ๋ณธ ๋ํ ๊ทธ๋ฆฌ๊ธฐ์ ์ ฐ์ด๋ ํ๋ก๊ทธ๋๋ฐ ๐จ
OpenGL์์ ๋ํ์ ๊ทธ๋ฆฌ๋ ค๋ฉด ์ ์ (Vertex)์ด๋ผ๋ ๊ฐ๋ ์ ์ดํดํด์ผ ํด. ์ ์ ์ 3D ๊ณต๊ฐ์์ ์ ์ผ๋ก, ์์น์ ์์, ํ ์ค์ฒ ์ขํ ๋ฑ์ ์์ฑ์ ๊ฐ์ง ์ ์์ด. ์ฌ๋ฌ ์ ์ ์ ์ฐ๊ฒฐํด์ ์ผ๊ฐํ์ ๋ง๋ค๊ณ , ์ด ์ผ๊ฐํ๋ค๋ก ๋ณต์กํ ๋ํ์ ๊ตฌ์ฑํ๋ ๊ฑฐ์ง.
์ผ๊ฐํ ๊ทธ๋ฆฌ๊ธฐ
๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ๋ํ์ธ ์ผ๊ฐํ์ ๊ทธ๋ ค๋ณด์:
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
// ์ ์ ์
ฐ์ด๋ ์์ค ์ฝ๋
const char* vertexShaderSource = R"(
#version 460 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
out vec3 vertexColor;
void main() {
gl_Position = vec4(aPos, 1.0);
vertexColor = aColor;
}
)";
// ํ๋๊ทธ๋จผํธ ์
ฐ์ด๋ ์์ค ์ฝ๋
const char* fragmentShaderSource = R"(
#version 460 core
in vec3 vertexColor;
out vec4 FragColor;
void main() {
FragColor = vec4(vertexColor, 1.0);
}
)";
int main() {
// GLFW ์ด๊ธฐํ ๋ฐ ์ฐฝ ์์ฑ (์ด์ ์ฝ๋์ ๋์ผ)
// ...
// ์
ฐ์ด๋ ์ปดํ์ผ
// ์ ์ ์
ฐ์ด๋
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
// ์ปดํ์ผ ์ค๋ฅ ์ฒดํฌ
int success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
std::cerr << "์ ์ ์
ฐ์ด๋ ์ปดํ์ผ ์ค๋ฅ: " << infoLog << std::endl;
}
// ํ๋๊ทธ๋จผํธ ์
ฐ์ด๋
unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
// ์ปดํ์ผ ์ค๋ฅ ์ฒดํฌ
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
std::cerr << "ํ๋๊ทธ๋จผํธ ์
ฐ์ด๋ ์ปดํ์ผ ์ค๋ฅ: " << infoLog << std::endl;
}
// ์
ฐ์ด๋ ํ๋ก๊ทธ๋จ ์์ฑ ๋ฐ ๋งํฌ
unsigned int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// ๋งํฌ ์ค๋ฅ ์ฒดํฌ
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
std::cerr << "์
ฐ์ด๋ ํ๋ก๊ทธ๋จ ๋งํฌ ์ค๋ฅ: " << infoLog << std::endl;
}
// ์
ฐ์ด๋ ๊ฐ์ฒด๋ ๋งํฌ ํ ์ญ์ ๊ฐ๋ฅ
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
// ์ ์ ๋ฐ์ดํฐ ์ค์
float vertices[] = {
// ์์น // ์์
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // ์ผ์ชฝ ์๋ (๋นจ๊ฐ)
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // ์ค๋ฅธ์ชฝ ์๋ (์ด๋ก)
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f // ์ (ํ๋)
};
// ์ ์ ๋ฒํผ ๊ฐ์ฒด(VBO)์ ์ ์ ๋ฐฐ์ด ๊ฐ์ฒด(VAO) ์์ฑ
unsigned int VBO, VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
// VAO ๋ฐ์ธ๋ฉ
glBindVertexArray(VAO);
// VBO ๋ฐ์ธ๋ฉ ๋ฐ ๋ฐ์ดํฐ ๋ณต์ฌ
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// ์์น ์์ฑ ์ค์
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// ์์ ์์ฑ ์ค์
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
// VBO ์ธ๋ฐ์ธ๋ฉ (์ ํ ์ฌํญ)
glBindBuffer(GL_ARRAY_BUFFER, 0);
// VAO ์ธ๋ฐ์ธ๋ฉ
glBindVertexArray(0);
// ๋ ๋๋ง ๋ฃจํ
while (!glfwWindowShouldClose(window)) {
// ์
๋ ฅ ์ฒ๋ฆฌ
processInput(window);
// ๋ ๋๋ง
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// ์
ฐ์ด๋ ํ๋ก๊ทธ๋จ ์ฌ์ฉ
glUseProgram(shaderProgram);
// VAO ๋ฐ์ธ๋ฉ
glBindVertexArray(VAO);
// ์ผ๊ฐํ ๊ทธ๋ฆฌ๊ธฐ
glDrawArrays(GL_TRIANGLES, 0, 3);
// ๋ฒํผ ๊ต์ฒด ๋ฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ
glfwSwapBuffers(window);
glfwPollEvents();
}
// ๋ฆฌ์์ค ์ ๋ฆฌ
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteProgram(shaderProgram);
// GLFW ์ข
๋ฃ
glfwTerminate();
return 0;
}
์ด ์ฝ๋๋ฅผ ์คํํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ์ผ๊ฐํ์ด ๊ทธ๋ ค์ง ๊ฑฐ์ผ:
์ ฐ์ด๋๋ ๋ฌด์์ธ๊ฐ? ๐ค
์ ฐ์ด๋(Shader)๋ GPU์์ ์คํ๋๋ ์์ ํ๋ก๊ทธ๋จ์ด์ผ. OpenGL์์๋ ์ฃผ๋ก ๋ ๊ฐ์ง ์ข ๋ฅ์ ์ ฐ์ด๋๋ฅผ ์ฌ์ฉํด:
-
์ ์ ์
ฐ์ด๋(Vertex Shader): ๊ฐ ์ ์ ์ ๋ํด ์คํ๋๋ฉฐ, ์ฃผ๋ก ์ ์ ์ ์์น ๋ณํ์ ๋ด๋นํด.
void main() { gl_Position = vec4(aPos, 1.0); // ์ ์ ์์น ์ค์ vertexColor = aColor; // ์์ ์ ๋ณด ์ ๋ฌ }
-
ํ๋๊ทธ๋จผํธ ์
ฐ์ด๋(Fragment Shader): ๊ฐ ํฝ์
(ํ๋๊ทธ๋จผํธ)์ ๋ํด ์คํ๋๋ฉฐ, ํฝ์
์ ์ต์ข
์์์ ๊ฒฐ์ ํด.
void main() { FragColor = vec4(vertexColor, 1.0); // RGBA ์์ ์ถ๋ ฅ }
์ ฐ์ด๋๋ GLSL(OpenGL Shading Language)์ด๋ผ๋ C์ ์ ์ฌํ ์ธ์ด๋ก ์์ฑ๋ผ. 2025๋ ํ์ฌ๋ GLSL 4.6์ด ๊ฐ์ฅ ๋๋ฆฌ ์ฌ์ฉ๋๊ณ ์์ด!
์ ์ ๋ฒํผ ๊ฐ์ฒด(VBO)์ ์ ์ ๋ฐฐ์ด ๊ฐ์ฒด(VAO)
OpenGL์์ ๊ทธ๋ํฝ์ค ๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํด ์ฌ์ฉํ๋ ์ค์ํ ๊ฐ๋ ์ด์ผ:
- ์ ์ ๋ฒํผ ๊ฐ์ฒด(VBO): GPU ๋ฉ๋ชจ๋ฆฌ์ ์ ์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ ๋ฒํผ์ผ. CPU์์ GPU๋ก ๋ฐ์ดํฐ๋ฅผ ํจ์จ์ ์ผ๋ก ์ ์กํ ์ ์๊ฒ ํด์ค.
- ์ ์ ๋ฐฐ์ด ๊ฐ์ฒด(VAO): ์ ์ ์์ฑ ํฌ์ธํฐ์ ๊ด๋ จ VBO๋ฅผ ์ ์ฅํ๋ ๊ฐ์ฒด์ผ. ์ฌ๋ฌ ์ค์ ์ ํ ๋ฒ์ ์ ์ฅํ๊ณ ๋ถ๋ฌ์ฌ ์ ์์ด์ ์ฝ๋๋ฅผ ๊ฐ๊ฒฐํ๊ฒ ๋ง๋ค์ด์ค.
์ฌ๊ฐํ ๊ทธ๋ฆฌ๊ธฐ (์ธ๋ฑ์ค ๋ฒํผ ์ฌ์ฉ)
์ด๋ฒ์๋ ์ธ๋ฑ์ค ๋ฒํผ ๊ฐ์ฒด(EBO)๋ฅผ ์ฌ์ฉํด์ ์ฌ๊ฐํ์ ๊ทธ๋ ค๋ณผ๊ฒ. ์ฌ๊ฐํ์ ๋ ๊ฐ์ ์ผ๊ฐํ์ผ๋ก ๊ตฌ์ฑ๋๋๋ฐ, ์ธ๋ฑ์ค ๋ฒํผ๋ฅผ ์ฌ์ฉํ๋ฉด ์ ์ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฌ์ฉํ ์ ์์ด์ ํจ์จ์ ์ด์ผ:
// ์ ์ ๋ฐ์ดํฐ ์ค์
float vertices[] = {
// ์์น // ์์
0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // ์ค๋ฅธ์ชฝ ์
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // ์ค๋ฅธ์ชฝ ์๋
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, // ์ผ์ชฝ ์๋
-0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f // ์ผ์ชฝ ์
};
// ์ธ๋ฑ์ค ๋ฐ์ดํฐ ์ค์
unsigned int indices[] = {
0, 1, 3, // ์ฒซ ๋ฒ์งธ ์ผ๊ฐํ
1, 2, 3 // ๋ ๋ฒ์งธ ์ผ๊ฐํ
};
// EBO ์์ฑ ๋ฐ ๋ฐ์ธ๋ฉ
unsigned int EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// ๋ ๋๋ง ์
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
์ด์ ๊ธฐ๋ณธ์ ์ธ ๋ํ์ ๊ทธ๋ฆฌ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์ ์ผ๋, ๋ค์์ผ๋ก ํ ์ค์ฒ์ ์กฐ๋ช ํจ๊ณผ๋ฅผ ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ ์์๋ณผ๊น?
5. ํ ์ค์ฒ์ ์กฐ๋ช ํจ๊ณผ ์ ์ฉํ๊ธฐ ๐ก
์ง๊ธ๊น์ง๋ ๋จ์์ผ๋ก๋ง ๋ํ์ ๊ทธ๋ ธ์ง๋ง, ์ค์ ๊ทธ๋ํฝ์ค ์ ํ๋ฆฌ์ผ์ด์ ์์๋ ํ ์ค์ฒ์ ์กฐ๋ช ํจ๊ณผ๊ฐ ํ์์ ์ด์ผ. ์ด๋ฒ์๋ ์ด ๋ ๊ฐ์ง ์์๋ฅผ ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์!
ํ ์ค์ฒ ์ ์ฉํ๊ธฐ
ํ ์ค์ฒ๋ ๋ํ ํ๋ฉด์ ์ด๋ฏธ์ง๋ฅผ ์ ํ๋ ๊ธฐ์ ์ด์ผ. OpenGL์์๋ ๋ค์๊ณผ ๊ฐ์ ๊ณผ์ ์ผ๋ก ํ ์ค์ฒ๋ฅผ ์ ์ฉํด:
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <stb_image.h> // ์ด๋ฏธ์ง ๋ก๋ฉ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
#include <iostream>
// ํ
์ค์ฒ ๋ก๋ฉ ํจ์
unsigned int loadTexture(const char* path) {
unsigned int textureID;
glGenTextures(1, &textureID);
int width, height, nrChannels;
stbi_set_flip_vertically_on_load(true); // OpenGL์ ์ด๋ฏธ์ง์ ์์ ์ด ์ขํ๋จ์ด๋ฏ๋ก ๋ค์ง๊ธฐ
unsigned char* data = stbi_load(path, &width, &height, &nrChannels, 0);
if (data) {
GLenum format;
if (nrChannels == 1)
format = GL_RED;
else if (nrChannels == 3)
format = GL_RGB;
else if (nrChannels == 4)
format = GL_RGBA;
glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
// ํ
์ค์ฒ ํ๋ผ๋ฏธํฐ ์ค์
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_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
} else {
std::cerr << "ํ
์ค์ฒ ๋ก๋ฉ ์คํจ: " << path << std::endl;
}
stbi_image_free(data);
return textureID;
}
int main() {
// GLFW ์ด๊ธฐํ ๋ฐ ์ฐฝ ์์ฑ (์ด์ ์ฝ๋์ ๋์ผ)
// ...
// ์
ฐ์ด๋ ์ค์ (์ด์ ์ฝ๋์ ์ ์ฌํ์ง๋ง ํ
์ค์ฒ ์ขํ ์ถ๊ฐ)
const char* vertexShaderSource = R"(
#version 460 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;
out vec3 ourColor;
out vec2 TexCoord;
void main() {
gl_Position = vec4(aPos, 1.0);
ourColor = aColor;
TexCoord = aTexCoord;
}
)";
const char* fragmentShaderSource = R"(
#version 460 core
in vec3 ourColor;
in vec2 TexCoord;
out vec4 FragColor;
uniform sampler2D texture1;
void main() {
FragColor = texture(texture1, TexCoord) * vec4(ourColor, 1.0);
}
)";
// ์
ฐ์ด๋ ์ปดํ์ผ ๋ฐ ๋งํฌ (์ด์ ์ฝ๋์ ๋์ผ)
// ...
// ์ ์ ๋ฐ์ดํฐ ์ค์ (ํ
์ค์ฒ ์ขํ ์ถ๊ฐ)
float vertices[] = {
// ์์น // ์์ // ํ
์ค์ฒ ์ขํ
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 // ์ผ์ชฝ ์
};
unsigned int indices[] = {
0, 1, 3, // ์ฒซ ๋ฒ์งธ ์ผ๊ฐํ
1, 2, 3 // ๋ ๋ฒ์งธ ์ผ๊ฐํ
};
// VAO, VBO, EBO ์ค์ (์ด์ ์ฝ๋์ ์ ์ฌ)
unsigned int VBO, VAO, EBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// ์์น ์์ฑ
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// ์์ ์์ฑ
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
// ํ
์ค์ฒ ์ขํ ์์ฑ
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);
// ํ
์ค์ฒ ๋ก๋ฉ
unsigned int texture = loadTexture("texture.jpg");
// ์
ฐ์ด๋์ ํ
์ค์ฒ ์ ๋ ์ค์
glUseProgram(shaderProgram);
glUniform1i(glGetUniformLocation(shaderProgram, "texture1"), 0);
// ๋ ๋๋ง ๋ฃจํ
while (!glfwWindowShouldClose(window)) {
// ์
๋ ฅ ์ฒ๋ฆฌ
processInput(window);
// ๋ ๋๋ง
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// ํ
์ค์ฒ ๋ฐ์ธ๋ฉ
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
// ์
ฐ์ด๋ ํ๋ก๊ทธ๋จ ์ฌ์ฉ
glUseProgram(shaderProgram);
// VAO ๋ฐ์ธ๋ฉ
glBindVertexArray(VAO);
// ์ฌ๊ฐํ ๊ทธ๋ฆฌ๊ธฐ
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
// ๋ฒํผ ๊ต์ฒด ๋ฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ
glfwSwapBuffers(window);
glfwPollEvents();
}
// ๋ฆฌ์์ค ์ ๋ฆฌ
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
glDeleteProgram(shaderProgram);
// GLFW ์ข
๋ฃ
glfwTerminate();
return 0;
}
ํ ์ค์ฒ ์ขํ๋? ๐
ํ ์ค์ฒ ์ขํ๋ ์ด๋ฏธ์ง์ ์ด๋ ๋ถ๋ถ์ ๋ํ์ ์ด๋ ์ง์ ์ ๋งคํํ ์ง ๊ฒฐ์ ํด. ํ ์ค์ฒ ์ขํ๋ ์ผ๋ฐ์ ์ผ๋ก (0,0)์์ (1,1) ์ฌ์ด์ ๊ฐ์ ๊ฐ์ง๋ฉฐ, (0,0)์ ์ด๋ฏธ์ง์ ์ผ์ชฝ ์๋, (1,1)์ ์ค๋ฅธ์ชฝ ์๋ฅผ ๋ํ๋ด.
์กฐ๋ช ํจ๊ณผ ์ ์ฉํ๊ธฐ
์ด์ 3D ๊ฐ์ฒด์ ์กฐ๋ช ํจ๊ณผ๋ฅผ ์ ์ฉํด๋ณด์. OpenGL์์๋ ๋ค์ํ ์กฐ๋ช ๋ชจ๋ธ์ ๊ตฌํํ ์ ์๋๋ฐ, ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ๊ฒ์ Phong ์กฐ๋ช ๋ชจ๋ธ์ด์ผ. ์ด ๋ชจ๋ธ์ ์ฃผ๋ณ๊ด(Ambient), ํ์ฐ๊ด(Diffuse), ๋ฐ์ฌ๊ด(Specular)์ ์ธ ๊ฐ์ง ๊ตฌ์ฑ ์์๋ก ์ด๋ฃจ์ด์ ธ ์์ด.
// ์ ์ ์
ฐ์ด๋
const char* vertexShaderSource = R"(
#version 460 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
out vec3 FragPos;
out vec3 Normal;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main() {
FragPos = vec3(model * vec4(aPos, 1.0));
Normal = mat3(transpose(inverse(model))) * aNormal;
gl_Position = projection * view * vec4(FragPos, 1.0);
}
)";
// ํ๋๊ทธ๋จผํธ ์
ฐ์ด๋
const char* fragmentShaderSource = R"(
#version 460 core
in vec3 FragPos;
in vec3 Normal;
out vec4 FragColor;
uniform vec3 lightPos;
uniform vec3 viewPos;
uniform vec3 lightColor;
uniform vec3 objectColor;
void main() {
// ์ฃผ๋ณ๊ด
float ambientStrength = 0.1;
vec3 ambient = ambientStrength * lightColor;
// ํ์ฐ๊ด
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;
// ๋ฐ์ฌ๊ด
float specularStrength = 0.5;
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = specularStrength * spec * lightColor;
// ์ต์ข
๊ฒฐ๊ณผ
vec3 result = (ambient + diffuse + specular) * objectColor;
FragColor = vec4(result, 1.0);
}
)";
์ด ์ ฐ์ด๋๋ฅผ ์ฌ์ฉํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ์กฐ๋ช ํจ๊ณผ๋ฅผ ์ป์ ์ ์์ด:
Phong ์กฐ๋ช ๋ชจ๋ธ์ ๊ตฌ์ฑ ์์ ๐ก
- ์ฃผ๋ณ๊ด(Ambient): ์ฅ๋ฉด ์ ์ฒด์ ๊ท ์ผํ๊ฒ ์ ์ฉ๋๋ ๊ธฐ๋ณธ ์กฐ๋ช . ์์ ํ ์ด๋ ์ ๋ฐฉ์งํด.
- ํ์ฐ๊ด(Diffuse): ๋น์ด ๋ฌผ์ฒด ํ๋ฉด์ ๋ถ๋ชํ ๋ชจ๋ ๋ฐฉํฅ์ผ๋ก ๊ณ ๋ฅด๊ฒ ๋ฐ์ฌ๋๋ ํจ๊ณผ. ๋ฌผ์ฒด์ ํํ๋ฅผ ๋๋ฌ๋ด๋ ์ฃผ์ ์์์ผ.
- ๋ฐ์ฌ๊ด(Specular): ๋น์ด ํน์ ๋ฐฉํฅ์ผ๋ก ๊ฐํ๊ฒ ๋ฐ์ฌ๋๋ ํจ๊ณผ. ํ์ด๋ผ์ดํธ๋ ๊ดํ์ ํํํด.
์กฐ๋ช ๊ณ์ฐ์๋ ๋ฒ์ ๋ฒกํฐ(Normal Vector)๊ฐ ์ค์ํด! ๋ฒ์ ๋ฒกํฐ๋ ํ๋ฉด์ ์์ง์ธ ๋ฐฉํฅ์ ๊ฐ๋ฆฌํค๋ ๋ฒกํฐ๋ก, ๋น์ ๋ฐ์ฌ ๋ฐฉํฅ์ ๊ณ์ฐํ๋ ๋ฐ ์ฌ์ฉ๋ผ.
์ด์ ํ ์ค์ฒ์ ์กฐ๋ช ํจ๊ณผ๋ฅผ ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์ ์ผ๋, ๋ค์์ผ๋ก 3D ๋ชจ๋ธ์ ๋ก๋ฉํ๊ณ ๋ ๋๋งํ๋ ๋ฐฉ๋ฒ์ ์์๋ณผ๊น?
6. 3D ๋ชจ๋ธ ๋ก๋ฉ๊ณผ ๋ ๋๋ง ๐
์ง๊ธ๊น์ง๋ ๊ฐ๋จํ ๋ํ๋ง ๋ค๋ค์ง๋ง, ์ค์ ๊ทธ๋ํฝ์ค ์ ํ๋ฆฌ์ผ์ด์ ์์๋ ๋ณต์กํ 3D ๋ชจ๋ธ์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์. ์ด๋ฒ์๋ ์ธ๋ถ 3D ๋ชจ๋ธ ํ์ผ์ ๋ก๋ฉํ๊ณ ๋ ๋๋งํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์!
Assimp ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์๊ฐ
3D ๋ชจ๋ธ์ ๋ก๋ฉํ๊ธฐ ์ํด ๊ฐ์ฅ ๋๋ฆฌ ์ฌ์ฉ๋๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ Assimp(Open Asset Import Library)์ผ. ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋ค์ํ 3D ํ์ผ ํ์(OBJ, FBX, COLLADA, glTF ๋ฑ)์ ์ง์ํด.
// Assimp ์ค์น (vcpkg ์ฌ์ฉ)
vcpkg install assimp:x64-windows
// CMakeLists.txt์ ์ถ๊ฐ
find_package(assimp REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE assimp::assimp)
๋ชจ๋ธ ํด๋์ค ๊ตฌํ
3D ๋ชจ๋ธ์ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ๊ธฐ ์ํด Model๊ณผ Mesh ํด๋์ค๋ฅผ ๊ตฌํํด๋ณด์:
// Mesh ํด๋์ค (Mesh.h)
#pragma once
#include <glad/glad.h>
#include <glm/glm.hpp>
#include <string>
#include <vector>
struct Vertex {
glm::vec3 Position;
glm::vec3 Normal;
glm::vec2 TexCoords;
};
struct Texture {
unsigned int id;
std::string type;
std::string path;
};
class Mesh {
public:
// ๋ฉ์ ๋ฐ์ดํฐ
std::vector<vertex> vertices;
std::vector<unsigned int> indices;
std::vector<texture> textures;
// ์์ฑ์
Mesh(const std::vector<vertex>& vertices, const std::vector<unsigned int>& indices, const std::vector<texture>& textures)
: vertices(vertices), indices(indices), textures(textures) {
setupMesh();
}
// ๋ ๋๋ง ํจ์
void Draw(unsigned int shaderProgram) {
// ํ
์ค์ฒ ๋ฐ์ธ๋ฉ
unsigned int diffuseNr = 1;
unsigned int specularNr = 1;
for (unsigned int i = 0; i < textures.size(); i++) {
glActiveTexture(GL_TEXTURE0 + i);
std::string number;
std::string name = textures[i].type;
if (name == "texture_diffuse")
number = std::to_string(diffuseNr++);
else if (name == "texture_specular")
number = std::to_string(specularNr++);
glUniform1i(glGetUniformLocation(shaderProgram, (name + number).c_str()), i);
glBindTexture(GL_TEXTURE_2D, textures[i].id);
}
// ๋ฉ์ ๊ทธ๋ฆฌ๊ธฐ
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
// ํ
์ค์ฒ ์ด๊ธฐํ
glActiveTexture(GL_TEXTURE0);
}
private:
// ๋ ๋๋ง ๋ฐ์ดํฐ
unsigned int VAO, VBO, EBO;
// ์ด๊ธฐํ ํจ์
void setupMesh() {
// VAO, VBO, EBO ์์ฑ
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
// VAO ๋ฐ์ธ๋ฉ
glBindVertexArray(VAO);
// VBO ๋ฐ์ธ๋ฉ ๋ฐ ๋ฐ์ดํฐ ๋ณต์ฌ
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW);
// EBO ๋ฐ์ธ๋ฉ ๋ฐ ๋ฐ์ดํฐ ๋ณต์ฌ
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW);
// ์ ์ ์์ฑ ์ค์
// ์์น
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
// ๋ฒ์
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Normal));
// ํ
์ค์ฒ ์ขํ
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, TexCoords));
// VAO ์ธ๋ฐ์ธ๋ฉ
glBindVertexArray(0);
}
};
// Model ํด๋์ค (Model.h)
#pragma once
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include "Mesh.h"
#include <string>
#include <vector>
class Model {
public:
// ์์ฑ์
Model(const std::string& path) {
loadModel(path);
}
// ๋ ๋๋ง ํจ์
void Draw(unsigned int shaderProgram) {
for (unsigned int i = 0; i < meshes.size(); i++) {
meshes[i].Draw(shaderProgram);
}
}
private:
// ๋ชจ๋ธ ๋ฐ์ดํฐ
std::vector<mesh> meshes;
std::string directory;
std::vector<texture> textures_loaded;
// ๋ชจ๋ธ ๋ก๋ฉ ํจ์
void loadModel(const std::string& path) {
Assimp::Importer importer;
const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_CalcTangentSpace);
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
std::cerr << "Assimp ์ค๋ฅ: " << importer.GetErrorString() << std::endl;
return;
}
directory = path.substr(0, path.find_last_of('/'));
processNode(scene->mRootNode, scene);
}
// ๋
ธ๋ ์ฒ๋ฆฌ ํจ์ (์ฌ๊ท์ )
void processNode(aiNode* node, const aiScene* scene) {
// ํ์ฌ ๋
ธ๋์ ๋ชจ๋ ๋ฉ์ ์ฒ๋ฆฌ
for (unsigned int i = 0; i < node->mNumMeshes; i++) {
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
meshes.push_back(processMesh(mesh, scene));
}
// ์์ ๋
ธ๋ ์ฒ๋ฆฌ
for (unsigned int i = 0; i < node->mNumChildren; i++) {
processNode(node->mChildren[i], scene);
}
}
// ๋ฉ์ ์ฒ๋ฆฌ ํจ์
Mesh processMesh(aiMesh* mesh, const aiScene* scene) {
std::vector<vertex> vertices;
std::vector<unsigned int> indices;
std::vector<texture> textures;
// ์ ์ ์ฒ๋ฆฌ
for (unsigned int i = 0; i < mesh->mNumVertices; i++) {
Vertex vertex;
// ์์น
vertex.Position.x = mesh->mVertices[i].x;
vertex.Position.y = mesh->mVertices[i].y;
vertex.Position.z = mesh->mVertices[i].z;
// ๋ฒ์
if (mesh->HasNormals()) {
vertex.Normal.x = mesh->mNormals[i].x;
vertex.Normal.y = mesh->mNormals[i].y;
vertex.Normal.z = mesh->mNormals[i].z;
}
// ํ
์ค์ฒ ์ขํ
if (mesh->mTextureCoords[0]) {
vertex.TexCoords.x = mesh->mTextureCoords[0][i].x;
vertex.TexCoords.y = mesh->mTextureCoords[0][i].y;
} else {
vertex.TexCoords = glm::vec2(0.0f, 0.0f);
}
vertices.push_back(vertex);
}
// ์ธ๋ฑ์ค ์ฒ๋ฆฌ
for (unsigned int i = 0; i < mesh->mNumFaces; i++) {
aiFace face = mesh->mFaces[i];
for (unsigned int j = 0; j < face.mNumIndices; j++) {
indices.push_back(face.mIndices[j]);
}
}
// ์ฌ์ง ์ฒ๋ฆฌ
if (mesh->mMaterialIndex >= 0) {
aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
// ๋ํจ์ฆ ํ
์ค์ฒ
std::vector<texture> diffuseMaps = loadMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse");
textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
// ์คํํ๋ฌ ํ
์ค์ฒ
std::vector<texture> specularMaps = loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular");
textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
}
return Mesh(vertices, indices, textures);
}
// ํ
์ค์ฒ ๋ก๋ฉ ํจ์
std::vector<texture> loadMaterialTextures(aiMaterial* mat, aiTextureType type, std::string typeName) {
std::vector<texture> textures;
for (unsigned int i = 0; i < mat->GetTextureCount(type); i++) {
aiString str;
mat->GetTexture(type, i, &str);
bool skip = false;
for (unsigned int j = 0; j < textures_loaded.size(); j++) {
if (std::strcmp(textures_loaded[j].path.data(), str.C_Str()) == 0) {
textures.push_back(textures_loaded[j]);
skip = true;
break;
}
}
if (!skip) {
Texture texture;
texture.id = loadTexture(str.C_Str(), directory);
texture.type = typeName;
texture.path = str.C_Str();
textures.push_back(texture);
textures_loaded.push_back(texture);
}
}
return textures;
}
// ํ
์ค์ฒ ๋ก๋ฉ ํจ์ (์ด์ ์ ์ ์ํ ํจ์์ ์ ์ฌ)
unsigned int loadTexture(const char* path, const std::string& directory) {
std::string filename = std::string(path);
filename = directory + '/' + filename;
unsigned int textureID;
glGenTextures(1, &textureID);
int width, height, nrComponents;
unsigned char* data = stbi_load(filename.c_str(), &width, &height, &nrComponents, 0);
if (data) {
GLenum format;
if (nrComponents == 1)
format = GL_RED;
else if (nrComponents == 3)
format = GL_RGB;
else if (nrComponents == 4)
format = GL_RGBA;
glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
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_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
stbi_image_free(data);
} else {
std::cerr << "ํ
์ค์ฒ ๋ก๋ฉ ์คํจ: " << path << std::endl;
stbi_image_free(data);
}
return textureID;
}
};</texture></texture></texture></texture></texture></unsigned></vertex></texture></mesh></texture></unsigned></vertex></texture></unsigned></vertex>
๋ชจ๋ธ ๋ ๋๋งํ๊ธฐ
์ด์ ๋ฉ์ธ ์ฝ๋์์ ๋ชจ๋ธ์ ๋ก๋ฉํ๊ณ ๋ ๋๋งํด๋ณด์:
#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 "Model.h"
#include <iostream>
int main() {
// GLFW ์ด๊ธฐํ ๋ฐ ์ฐฝ ์์ฑ (์ด์ ์ฝ๋์ ๋์ผ)
// ...
// ์
ฐ์ด๋ ์ค์ (๋ชจ๋ธ ๋ ๋๋ง์ฉ)
// ...
// ๋ชจ๋ธ ๋ก๋ฉ
Model ourModel("models/nanosuit/nanosuit.obj");
// ๋ ๋๋ง ๋ฃจํ
while (!glfwWindowShouldClose(window)) {
// ์
๋ ฅ ์ฒ๋ฆฌ
processInput(window);
// ๋ ๋๋ง
glClearColor(0.05f, 0.05f, 0.05f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// ๊น์ด ํ
์คํธ ํ์ฑํ
glEnable(GL_DEPTH_TEST);
// ์
ฐ์ด๋ ํ๋ก๊ทธ๋จ ์ฌ์ฉ
glUseProgram(shaderProgram);
// ๋ณํ ํ๋ ฌ ์ค์
glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
glm::mat4 view = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -3.0f));
glm::mat4 model = glm::rotate(glm::mat4(1.0f), (float)glfwGetTime(), glm::vec3(0.0f, 1.0f, 0.0f));
// ์
ฐ์ด๋์ ๋ณํ ํ๋ ฌ ์ ๋ฌ
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "view"), 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(model));
// ๋ชจ๋ธ ๊ทธ๋ฆฌ๊ธฐ
ourModel.Draw(shaderProgram);
// ๋ฒํผ ๊ต์ฒด ๋ฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ
glfwSwapBuffers(window);
glfwPollEvents();
}
// GLFW ์ข
๋ฃ
glfwTerminate();
return 0;
}
3D ๋ชจ๋ธ ํ์ผ ํ์ ๐๏ธ
๋ค์ํ 3D ๋ชจ๋ธ ํ์ผ ํ์์ด ์๋๋ฐ, ๊ฐ๊ฐ ์ฅ๋จ์ ์ด ์์ด:
- OBJ: ๊ฐ๋จํ๊ณ ๋๋ฆฌ ์ง์๋๋ ํ์. ํ ์ค์ฒ์ ๊ธฐ๋ณธ ์ฌ์ง ์ ๋ณด๋ฅผ ํฌํจํ ์ ์์ด.
- FBX: Autodesk์์ ๊ฐ๋ฐํ ํ์์ผ๋ก, ์ ๋๋ฉ์ด์ ๊ณผ ๋ณต์กํ ์ฌ์ง ์ ๋ณด๋ฅผ ํฌํจํ ์ ์์ด.
- COLLADA (DAE): XML ๊ธฐ๋ฐ ํ์์ผ๋ก, ๋ค์ํ ๋ฐ์ดํฐ๋ฅผ ํฌํจํ ์ ์์ด.
- glTF: Khronos Group์์ ๊ฐ๋ฐํ ์ต์ ํ์์ผ๋ก, ์น๊ณผ ๋ชจ๋ฐ์ผ ํ๊ฒฝ์ ์ต์ ํ๋์ด ์์ด. 2025๋ ํ์ฌ ๊ฐ์ฅ ์ธ๊ธฐ ์๋ ํ์ ์ค ํ๋์ผ.
์ฌ๋ฅ๋ท์์๋ 3D ๋ชจ๋ธ๋ง๊ณผ ๊ทธ๋ํฝ์ค ํ๋ก๊ทธ๋๋ฐ์ ๋ฐฐ์ธ ์ ์๋ ๋ค์ํ ๊ฐ์ข๊ฐ ์์ด. ์ ๋ฌธ๊ฐ๋ค์ ๋ ธํ์ฐ๋ฅผ ๋ฐฐ์๋ณด๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ง!
์ด์ 3D ๋ชจ๋ธ์ ๋ก๋ฉํ๊ณ ๋ ๋๋งํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์ ์ผ๋, ๋ค์์ผ๋ก ์นด๋ฉ๋ผ ์์คํ ๊ณผ ์ฌ์ฉ์ ์ํธ์์ฉ์ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ์์๋ณผ๊น?
7. ์นด๋ฉ๋ผ ์์คํ ๊ณผ ์ฌ์ฉ์ ์ํธ์์ฉ ๐ฎ
3D ๊ทธ๋ํฝ์ค ์ ํ๋ฆฌ์ผ์ด์ ์์ ์นด๋ฉ๋ผ๋ ์ฌ์ฉ์๊ฐ ๊ฐ์ ์ธ๊ณ๋ฅผ ํ์ํ ์ ์๊ฒ ํด์ฃผ๋ ์ค์ํ ์์์ผ. ์ด๋ฒ์๋ ์นด๋ฉ๋ผ ์์คํ ์ ๊ตฌํํ๊ณ ํค๋ณด๋์ ๋ง์ฐ์ค๋ก ์ ์ดํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์!
์นด๋ฉ๋ผ ํด๋์ค ๊ตฌํ
๋จผ์ ์นด๋ฉ๋ผ์ ๊ธฐ๋ณธ ๊ธฐ๋ฅ์ ๊ตฌํํ ํด๋์ค๋ฅผ ๋ง๋ค์ด๋ณด์:
// Camera.h
#pragma once
#include <glad/glad.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
// ์นด๋ฉ๋ผ ์ด๋ ๋ฐฉํฅ ์ ์
enum Camera_Movement {
FORWARD,
BACKWARD,
LEFT,
RIGHT,
UP,
DOWN
};
// ์นด๋ฉ๋ผ ๊ธฐ๋ณธ ๊ฐ
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();
}
// ๋ทฐ ํ๋ ฌ ๋ฐํ
glm::mat4 GetViewMatrix() const {
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;
if (direction == UP)
Position += Up * velocity;
if (direction == DOWN)
Position -= Up * velocity;
}
// ๋ง์ฐ์ค ์
๋ ฅ ์ฒ๋ฆฌ
void ProcessMouseMovement(float xoffset, float yoffset, bool constrainPitch = true) {
xoffset *= MouseSensitivity;
yoffset *= MouseSensitivity;
Yaw += xoffset;
Pitch += yoffset;
// ํผ์น ์ ํ (์ฒ์ฅ์ด๋ ๋ฐ๋ฅ์ ๋ซ์ง ์๋๋ก)
if (constrainPitch) {
if (Pitch > 89.0f)
Pitch = 89.0f;
if (Pitch < -89.0f)
Pitch = -89.0f;
}
// ์นด๋ฉ๋ผ ๋ฒกํฐ ์
๋ฐ์ดํธ
updateCameraVectors();
}
// ๋ง์ฐ์ค ์คํฌ๋กค ์ฒ๋ฆฌ (์ค)
void ProcessMouseScroll(float yoffset) {
Zoom -= yoffset;
if (Zoom < 1.0f)
Zoom = 1.0f;
if (Zoom > 45.0f)
Zoom = 45.0f;
}
private:
// ์นด๋ฉ๋ผ ๋ฒกํฐ ๊ณ์ฐ
void updateCameraVectors() {
// ์๋ก์ด Front ๋ฒกํฐ ๊ณ์ฐ
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์ Up ๋ฒกํฐ ๊ณ์ฐ
Right = glm::normalize(glm::cross(Front, WorldUp));
Up = glm::normalize(glm::cross(Right, Front));
}
};
์ ๋ ฅ ์ฝ๋ฐฑ ํจ์ ์ค์
์ด์ GLFW์ ์ฝ๋ฐฑ ํจ์๋ฅผ ์ค์ ํด์ ํค๋ณด๋์ ๋ง์ฐ์ค ์ ๋ ฅ์ ์ฒ๋ฆฌํด๋ณด์:
// ์ ์ญ ๋ณ์
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
float lastX = SCR_WIDTH / 2.0f;
float lastY = SCR_HEIGHT / 2.0f;
bool firstMouse = true;
float deltaTime = 0.0f;
float lastFrame = 0.0f;
// ํค๋ณด๋ ์
๋ ฅ ์ฒ๋ฆฌ ํจ์
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, 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);
if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS)
camera.ProcessKeyboard(UP, deltaTime);
if (glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS)
camera.ProcessKeyboard(DOWN, deltaTime);
}
// ๋ง์ฐ์ค ์ด๋ ์ฝ๋ฐฑ ํจ์
void mouse_callback(GLFWwindow* window, double xposIn, double yposIn) {
float xpos = static_cast<float>(xposIn);
float ypos = static_cast<float>(yposIn);
if (firstMouse) {
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
float xoffset = xpos - lastX;
float yoffset = lastY - ypos; // ๋ฐ์ ๋ y ์ขํ
lastX = xpos;
lastY = ypos;
camera.ProcessMouseMovement(xoffset, yoffset);
}
// ๋ง์ฐ์ค ์คํฌ๋กค ์ฝ๋ฐฑ ํจ์
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) {
camera.ProcessMouseScroll(static_cast<float>(yoffset));
}
int main() {
// GLFW ์ด๊ธฐํ ๋ฐ ์ฐฝ ์์ฑ (์ด์ ์ฝ๋์ ๋์ผ)
// ...
// ์ฝ๋ฐฑ ํจ์ ๋ฑ๋ก
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetScrollCallback(window, scroll_callback);
// ๋ง์ฐ์ค ์ปค์ ์บก์ฒ
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
// ๋ ๋๋ง ๋ฃจํ
while (!glfwWindowShouldClose(window)) {
// ํ๋ ์ ์๊ฐ ๊ณ์ฐ
float currentFrame = static_cast<float>(glfwGetTime());
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
// ์
๋ ฅ ์ฒ๋ฆฌ
processInput(window);
// ๋ ๋๋ง
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// ์
ฐ์ด๋ ํ๋ก๊ทธ๋จ ์ฌ์ฉ
glUseProgram(shaderProgram);
// ํฌ์ ํ๋ ฌ ์ค์ (์ค ์ ์ฉ)
glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
// ๋ทฐ ํ๋ ฌ ์ค์ (์นด๋ฉ๋ผ ์ ์ฉ)
glm::mat4 view = camera.GetViewMatrix();
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "view"), 1, GL_FALSE, glm::value_ptr(view));
// ๋ชจ๋ธ ๊ทธ๋ฆฌ๊ธฐ
// ...
// ๋ฒํผ ๊ต์ฒด ๋ฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ
glfwSwapBuffers(window);
glfwPollEvents();
}
// GLFW ์ข
๋ฃ
glfwTerminate();
return 0;
}</float></float></float></float>
์นด๋ฉ๋ผ ์์คํ ์ ์ฃผ์ ๊ฐ๋ ๐ธ
- ์์น(Position): 3D ๊ณต๊ฐ์์ ์นด๋ฉ๋ผ์ ์์น๋ฅผ ๋ํ๋ด๋ ๋ฒกํฐ
- ๋ฐฉํฅ(Direction): ์นด๋ฉ๋ผ๊ฐ ๋ฐ๋ผ๋ณด๋ ๋ฐฉํฅ์ ๋ํ๋ด๋ ๋ฒกํฐ
-
์ค์ผ๋ฌ ๊ฐ(Euler Angles): ์นด๋ฉ๋ผ์ ํ์ ์ ๋ํ๋ด๋ ๊ฐ๋
- Yaw(์): ์ข์ฐ ํ์ (์ํ ํ์ )
- Pitch(ํผ์น): ์ํ ํ์ (์์ง ํ์ )
- Roll(๋กค): ์นด๋ฉ๋ผ ์ถ์ ์ค์ฌ์ผ๋ก ํ ํ์ (์ด ์์ ์์๋ ์ฌ์ฉํ์ง ์์)
- ๋ทฐ ํ๋ ฌ(View Matrix): ์๋ ๊ณต๊ฐ์ ์ขํ๋ฅผ ์นด๋ฉ๋ผ ๊ณต๊ฐ์ ์ขํ๋ก ๋ณํํ๋ ํ๋ ฌ
FPS ์นด๋ฉ๋ผ์ ๊ถค๋ ์นด๋ฉ๋ผ
3D ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ฃผ๋ก ์ฌ์ฉ๋๋ ๋ ๊ฐ์ง ์นด๋ฉ๋ผ ์ ํ์ด ์์ด:
- FPS ์นด๋ฉ๋ผ(First-Person Camera): 1์ธ์นญ ์์ ์ผ๋ก, ํ๋ ์ด์ด๊ฐ ์ง์ ์ธ๊ณ๋ฅผ ํ์ํ๋ ๋๋์ ์ค. ์์์ ๊ตฌํํ ์นด๋ฉ๋ผ๊ฐ ์ด ์ ํ์ ํด๋นํด.
- ๊ถค๋ ์นด๋ฉ๋ผ(Orbit Camera): ํน์ ๋์์ ์ค์ฌ์ผ๋ก ํ์ ํ๋ ์นด๋ฉ๋ผ๋ก, 3D ๋ชจ๋ธ๋ง ํ๋ก๊ทธ๋จ์ด๋ CAD ์ํํธ์จ์ด์์ ์์ฃผ ์ฌ์ฉ๋ผ.
๊ถค๋ ์นด๋ฉ๋ผ๋ฅผ ๊ตฌํํ๋ ค๋ฉด ๋ค์๊ณผ ๊ฐ์ ์ฝ๋๋ฅผ ์ถ๊ฐํ๋ฉด ๋ผ:
// ๊ถค๋ ์นด๋ฉ๋ผ ๊ตฌํ
glm::vec3 target = glm::vec3(0.0f, 0.0f, 0.0f); // ์ค์ฌ์
float radius = 5.0f; // ๊ถค๋ ๋ฐ๊ฒฝ
// ๋ง์ฐ์ค ์ด๋์ ๋ฐ๋ผ ๊ฐ๋ ๋ณ๊ฒฝ
float theta = glm::radians(yaw); // ์ํ ๊ฐ๋
float phi = glm::radians(pitch); // ์์ง ๊ฐ๋
// ๊ตฌ๋ฉด ์ขํ๊ณ๋ฅผ ์ง๊ต ์ขํ๊ณ๋ก ๋ณํ
float x = radius * cos(phi) * cos(theta);
float y = radius * sin(phi);
float z = radius * cos(phi) * sin(theta);
// ์นด๋ฉ๋ผ ์์น ์ค์
camera.Position = target + glm::vec3(x, y, z);
// ์นด๋ฉ๋ผ๊ฐ ํญ์ ์ค์ฌ์ ์ ๋ฐ๋ผ๋ณด๋๋ก ์ค์
camera.Front = glm::normalize(target - camera.Position);
์นด๋ฉ๋ผ ์์คํ ์ ๊ฒ์ ๊ฐ๋ฐ์์ ๋งค์ฐ ์ค์ํ ์์์ผ. ์ฌ๋ฅ๋ท์์๋ ๊ฒ์ ๊ฐ๋ฐ๊ณผ ๊ด๋ จ๋ ๋ค์ํ ์๋น์ค๋ฅผ ์ ๊ณตํ๊ณ ์์ผ๋ ์ฐธ๊ณ ํด๋ด!
์ด์ ์นด๋ฉ๋ผ ์์คํ ๊ณผ ์ฌ์ฉ์ ์ํธ์์ฉ์ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์ ์ผ๋, ๋ค์์ผ๋ก ๊ณ ๊ธ ๊ทธ๋ํฝ์ค ๊ธฐ๋ฒ์ ์์๋ณผ๊น?
8. ๊ณ ๊ธ ๊ทธ๋ํฝ์ค ๊ธฐ๋ฒ ํ๊ตฌ ๐
๊ธฐ๋ณธ์ ์ธ ๋ ๋๋ง ๊ธฐ์ ์ ๋ง์คํฐํ์ผ๋, ์ด์ ๋ ํ์ค์ ์ด๊ณ ๋ฉ์ง ๊ทธ๋ํฝ์ค๋ฅผ ์ํ ๊ณ ๊ธ ๊ธฐ๋ฒ๋ค์ ์์๋ณด์! 2025๋ ํ์ฌ ๊ฐ์ฅ ์ธ๊ธฐ ์๋ ๊ธฐ๋ฒ๋ค์ ์ค์ฌ์ผ๋ก ์ดํด๋ณผ๊ฒ.
1. ํ๋ ์๋ฒํผ์ ํ์ฒ๋ฆฌ ํจ๊ณผ
ํ๋ ์๋ฒํผ(Framebuffer)๋ ๋ ๋๋ง ๊ฒฐ๊ณผ๋ฅผ ์ ์ฅํ๋ ๋ฒํผ์ผ. ์ด๋ฅผ ํ์ฉํ๋ฉด ๋ธ๋ฃธ(Bloom), ๋ชจ์ ๋ธ๋ฌ(Motion Blur), ํผ์ฌ๊ณ ์ฌ๋(Depth of Field) ๊ฐ์ ํ์ฒ๋ฆฌ ํจ๊ณผ๋ฅผ ์ ์ฉํ ์ ์์ด.
// ํ๋ ์๋ฒํผ ์์ฑ
unsigned int framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
// ํ
์ค์ฒ ์ฒจ๋ถ
unsigned int textureColorBuffer;
glGenTextures(1, &textureColorBuffer);
glBindTexture(GL_TEXTURE_2D, textureColorBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureColorBuffer, 0);
// ๋ ๋๋ฒํผ ์ฒจ๋ถ (๊น์ด ๋ฐ ์คํ
์ค)
unsigned int rbo;
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, SCR_WIDTH, SCR_HEIGHT);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);
// ํ๋ ์๋ฒํผ ์์ ์ฑ ์ฒดํฌ
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
std::cerr << "ํ๋ ์๋ฒํผ๊ฐ ์์ ํ์ง ์์ต๋๋ค!" << std::endl;
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// ๋ ๋๋ง ๋ฃจํ ๋ด์์
// 1. ํ๋ ์๋ฒํผ์ ์ฅ๋ฉด ๋ ๋๋ง
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// ์ฅ๋ฉด ๋ ๋๋ง ์ฝ๋...
// 2. ๊ธฐ๋ณธ ํ๋ ์๋ฒํผ๋ก ์ ํํ๊ณ ํ์ฒ๋ฆฌ ํจ๊ณผ ์ ์ฉ
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(postProcessingShader);
glBindTexture(GL_TEXTURE_2D, textureColorBuffer);
// ํ๋ฉด ํฌ๊ธฐ ์ฌ๊ฐํ ๊ทธ๋ฆฌ๊ธฐ
2. ๊ทธ๋ฆผ์ ๋งคํ
๊ทธ๋ฆผ์๋ ์ฅ๋ฉด์ ํ์ค๊ฐ์ ๋ํด์ฃผ๋ ์ค์ํ ์์์ผ. ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ๊ทธ๋ฆผ์ ๋งคํ ๊ธฐ๋ฒ์ ์๋์ฐ ๋งคํ(Shadow Mapping)์ด์ผ.
// 1. ๊ทธ๋ฆผ์ ๋งต ์์ฑ
unsigned int depthMapFBO;
glGenFramebuffers(1, &depthMapFBO);
const unsigned int SHADOW_WIDTH = 1024, SHADOW_HEIGHT = 1024;
unsigned int depthMap;
glGenTextures(1, &depthMap);
glBindTexture(GL_TEXTURE_2D, depthMap);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
float borderColor[] = { 1.0f, 1.0f, 1.0f, 1.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// 2. ๊ด์ ์์ ์์ ๊น์ด ๋งต ๋ ๋๋ง
glm::mat4 lightProjection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, 1.0f, 7.5f);
glm::mat4 lightView = glm::lookAt(lightPos, glm::vec3(0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
glm::mat4 lightSpaceMatrix = lightProjection * lightView;
glUseProgram(simpleDepthShader);
glUniformMatrix4fv(glGetUniformLocation(simpleDepthShader, "lightSpaceMatrix"), 1, GL_FALSE, glm::value_ptr(lightSpaceMatrix));
glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT);
glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO);
glClear(GL_DEPTH_BUFFER_BIT);
// ์ฅ๋ฉด ๋ ๋๋ง (๊น์ด ์ ๋ณด๋ง)
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// 3. ์ผ๋ฐ ๋ ๋๋ง์์ ๊ทธ๋ฆผ์ ์ ์ฉ
glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(shader);
// ๋ณํ ํ๋ ฌ ์ค์
glUniformMatrix4fv(glGetUniformLocation(shader, "lightSpaceMatrix"), 1, GL_FALSE, glm::value_ptr(lightSpaceMatrix));
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, depthMap);
glUniform1i(glGetUniformLocation(shader, "shadowMap"), 1);
// ์ฅ๋ฉด ๋ ๋๋ง
์๋์ฐ ๋งคํ์ ์๋ฆฌ ๐
- ๊ด์ ์์ ์์ ์ฅ๋ฉด์ ๋ ๋๋งํ๊ณ ๊น์ด ์ ๋ณด๋ฅผ ํ ์ค์ฒ์ ์ ์ฅํด.
- ์นด๋ฉ๋ผ ์์ ์์ ์ฅ๋ฉด์ ๋ ๋๋งํ ๋, ๊ฐ ํฝ์ ์ ์์น๋ฅผ ๊ด์ ์์ ์ ์ขํ๋ก ๋ณํํด.
- ๋ณํ๋ ์ขํ์ ๊น์ด ๊ฐ๊ณผ ์ ์ฅ๋ ๊น์ด ๋งต์ ๊ฐ์ ๋น๊ตํด.
- ์ ์ฅ๋ ๊น์ด ๊ฐ๋ณด๋ค ํ์ฌ ๊น์ด ๊ฐ์ด ํฌ๋ฉด ํด๋น ํฝ์ ์ ๊ทธ๋ฆผ์ ์์ ์๋ ๊ฒ์ผ๋ก ํ๋จํด.
3. PBR (๋ฌผ๋ฆฌ ๊ธฐ๋ฐ ๋ ๋๋ง)
๋ฌผ๋ฆฌ ๊ธฐ๋ฐ ๋ ๋๋ง(Physically Based Rendering, PBR)์ ์ค์ ๋ฌผ๋ฆฌ ๋ฒ์น์ ๊ธฐ๋ฐํ ๋ ๋๋ง ๊ธฐ๋ฒ์ผ๋ก, ๋ ์ฌ์ค์ ์ธ ์ฌ์ง ํํ์ด ๊ฐ๋ฅํด. 2025๋ ํ์ฌ ๋๋ถ๋ถ์ ๊ฒ์ ์์ง๊ณผ ๊ทธ๋ํฝ์ค ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ฌ์ฉ๋๊ณ ์์ด.
// PBR ํ๋๊ทธ๋จผํธ ์
ฐ์ด๋
const char* pbrFragmentShader = R"(
#version 460 core
out vec4 FragColor;
in vec3 WorldPos;
in vec3 Normal;
in vec2 TexCoords;
// ์ฌ์ง ์์ฑ
uniform sampler2D albedoMap;
uniform sampler2D normalMap;
uniform sampler2D metallicMap;
uniform sampler2D roughnessMap;
uniform sampler2D aoMap;
// ๊ด์
uniform vec3 lightPositions[4];
uniform vec3 lightColors[4];
uniform vec3 camPos;
const float PI = 3.14159265359;
// ๋ฒ์ ๋ถํฌ ํจ์
float DistributionGGX(vec3 N, vec3 H, float roughness) {
float a = roughness*roughness;
float a2 = a*a;
float NdotH = max(dot(N, H), 0.0);
float NdotH2 = NdotH*NdotH;
float nom = a2;
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
denom = PI * denom * denom;
return nom / denom;
}
// ๊ธฐํ ๊ฐ์ ํจ์
float GeometrySchlickGGX(float NdotV, float roughness) {
float r = (roughness + 1.0);
float k = (r*r) / 8.0;
float nom = NdotV;
float denom = NdotV * (1.0 - k) + k;
return nom / denom;
}
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) {
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
return ggx1 * ggx2;
}
// ํ๋ ๋ฌ ๋ฐฉ์ ์
vec3 fresnelSchlick(float cosTheta, vec3 F0) {
return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0);
}
void main() {
vec3 albedo = pow(texture(albedoMap, TexCoords).rgb, vec3(2.2));
float metallic = texture(metallicMap, TexCoords).r;
float roughness = texture(roughnessMap, TexCoords).r;
float ao = texture(aoMap, TexCoords).r;
vec3 N = normalize(Normal);
vec3 V = normalize(camPos - WorldPos);
// ๊ธ์์ฑ์ ๋ฐ๋ฅธ ๊ธฐ๋ณธ ๋ฐ์ฌ์จ ๊ณ์ฐ
vec3 F0 = vec3(0.04);
F0 = mix(F0, albedo, metallic);
// ๋ฐ์ฌ ๋ฐฉ์ ์
vec3 Lo = vec3(0.0);
for(int i = 0; i < 4; ++i) {
// ๊ด์ ๋ฐฉํฅ ๊ณ์ฐ
vec3 L = normalize(lightPositions[i] - WorldPos);
vec3 H = normalize(V + L);
// ๊ฐ์ ๊ณ์ฐ
float distance = length(lightPositions[i] - WorldPos);
float attenuation = 1.0 / (distance * distance);
vec3 radiance = lightColors[i] * attenuation;
// BRDF ํญ ๊ณ์ฐ
float NDF = DistributionGGX(N, H, roughness);
float G = GeometrySmith(N, V, L, roughness);
vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0);
vec3 numerator = NDF * G * F;
float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.0001;
vec3 specular = numerator / denominator;
vec3 kS = F;
vec3 kD = vec3(1.0) - kS;
kD *= 1.0 - metallic;
// ์ต์ข
์์ ๊ณ์ฐ
float NdotL = max(dot(N, L), 0.0);
Lo += (kD * albedo / PI + specular) * radiance * NdotL;
}
// ์ฃผ๋ณ๊ด ๊ณ์ฐ
vec3 ambient = vec3(0.03) * albedo * ao;
vec3 color = ambient + Lo;
// HDR ํค ๋งคํ ๋ฐ ๊ฐ๋ง ๋ณด์
color = color / (color + vec3(1.0));
color = pow(color, vec3(1.0/2.2));
FragColor = vec4(color, 1.0);
}
)";
PBR์ ์ฃผ์ ๊ตฌ์ฑ ์์ ๐ง
- ์๋ฒ ๋(Albedo): ํ๋ฉด์ ๊ธฐ๋ณธ ์์
- ๊ธ์์ฑ(Metallic): ํ๋ฉด์ด ๊ธ์์ธ์ง ๋น๊ธ์์ธ์ง๋ฅผ ๋ํ๋ด๋ ๊ฐ
- ๊ฑฐ์น ๊ธฐ(Roughness): ํ๋ฉด์ ๋ฏธ์ธํ ๊ฑฐ์น ๊ธฐ ์ ๋
- ํ๊ฒฝ ์ฐจํ(Ambient Occlusion): ํ๋ฉด์ ํ์๋ ๊ตฌ์์์ ๋น์ด ์ฐจ๋จ๋๋ ์ ๋
- ๋ฒ์ ๋งต(Normal Map): ํ๋ฉด์ ๋ฏธ์ธํ ๊ตด๊ณก์ ํํํ๋ ๋งต
PBR์ ๋ณต์กํด ๋ณด์ด์ง๋ง, ์ผ๋จ ์ดํดํ๊ณ ๋๋ฉด ๋ค์ํ ์ฌ์ง์ ์ผ๊ด๋ ๋ฐฉ์์ผ๋ก ํํํ ์ ์์ด์ ๋งค์ฐ ์ ์ฉํด!
4. ํ๊ฒฝ ๋งคํ๊ณผ IBL
์ด๋ฏธ์ง ๊ธฐ๋ฐ ์กฐ๋ช (Image-Based Lighting, IBL)์ ํ๊ฒฝ ๋งต์ ์ฌ์ฉํด ์ฃผ๋ณ ํ๊ฒฝ์์ ์ค๋ ๋น์ ์๋ฎฌ๋ ์ด์ ํ๋ ๊ธฐ๋ฒ์ด์ผ. ํนํ PBR๊ณผ ํจ๊ป ์ฌ์ฉํ๋ฉด ๋์ฑ ์ฌ์ค์ ์ธ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์์ด.
// ํ๋ธ๋งต ๋ก๋ฉ
unsigned int loadCubemap(std::vector<:string> faces) {
unsigned int textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
int width, height, nrChannels;
for (unsigned int i = 0; i < faces.size(); i++) {
unsigned char *data = stbi_load(faces[i].c_str(), &width, &height, &nrChannels, 0);
if (data) {
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
stbi_image_free(data);
} else {
std::cerr << "ํ๋ธ๋งต ํ
์ค์ฒ ๋ก๋ฉ ์คํจ: " << faces[i] << std::endl;
stbi_image_free(data);
}
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
return textureID;
}
// IBL์ ์ํ ํ๋๊ทธ๋จผํธ ์
ฐ์ด๋ (PBR ์
ฐ์ด๋์ ์ถ๊ฐ)
uniform samplerCube irradianceMap;
uniform samplerCube prefilterMap;
uniform sampler2D brdfLUT;
// ์ฃผ๋ณ๊ด ๊ณ์ฐ ๋ถ๋ถ ์์
vec3 F = fresnelSchlickRoughness(max(dot(N, V), 0.0), F0, roughness);
vec3 kS = F;
vec3 kD = 1.0 - kS;
kD *= 1.0 - metallic;
// IBL ํ์ฐ๊ด
vec3 irradiance = texture(irradianceMap, N).rgb;
vec3 diffuse = irradiance * albedo;
// IBL ๋ฐ์ฌ๊ด
const float MAX_REFLECTION_LOD = 4.0;
vec3 prefilteredColor = textureLod(prefilterMap, reflect(-V, N), roughness * MAX_REFLECTION_LOD).rgb;
vec2 brdf = texture(brdfLUT, vec2(max(dot(N, V), 0.0), roughness)).rg;
vec3 specular = prefilteredColor * (F * brdf.x + brdf.y);
vec3 ambient = (kD * diffuse + specular) * ao;</:string>
5. ์ปดํจํธ ์ ฐ์ด๋์ GPGPU
OpenGL 4.3๋ถํฐ ๋์ ๋ ์ปดํจํธ ์ ฐ์ด๋(Compute Shader)๋ GPU์ ๋ณ๋ ฌ ์ฒ๋ฆฌ ๋ฅ๋ ฅ์ ๊ทธ๋ํฝ์ค ์ด์ธ์ ๋ชฉ์ ์ผ๋ก๋ ํ์ฉํ ์ ์๊ฒ ํด์ค. ์ด๋ฅผ GPGPU(General-Purpose computing on GPU)๋ผ๊ณ ํด.
// ์ปดํจํธ ์
ฐ์ด๋ ์์ (ํํฐํด ์์คํ
)
const char* computeShaderSource = R"(
#version 460 core
layout (local_size_x = 256, local_size_y = 1, local_size_z = 1) in;
struct Particle {
vec4 position;
vec4 velocity;
vec4 color;
float life;
float size;
};
layout(std430, binding = 0) buffer ParticleBuffer {
Particle particles[];
};
uniform float deltaTime;
uniform vec3 gravity;
uniform float randomSeed;
// ๋์ ์์ฑ ํจ์
float random(vec2 st) {
return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);
}
void main() {
uint index = gl_GlobalInvocationID.x;
if (index >= particles.length())
return;
// ํํฐํด ์
๋ฐ์ดํธ
particles[index].velocity.xyz += gravity * deltaTime;
particles[index].position.xyz += particles[index].velocity.xyz * deltaTime;
particles[index].life -= deltaTime;
// ํํฐํด ๋ฆฌ์
if (particles[index].life <= 0.0) {
float rnd1 = random(vec2(index, randomSeed));
float rnd2 = random(vec2(index, randomSeed + 1.0));
float rnd3 = random(vec2(index, randomSeed + 2.0));
particles[index].position = vec4(0.0, 0.0, 0.0, 1.0); // ์์ ์์ ์์
particles[index].velocity = vec4(
(rnd1 - 0.5) * 2.0, // x ๋ฐฉํฅ (-1 ~ 1)
rnd2 * 2.0 + 1.0, // y ๋ฐฉํฅ (1 ~ 3)
(rnd3 - 0.5) * 2.0, // z ๋ฐฉํฅ (-1 ~ 1)
0.0
);
particles[index].color = vec4(rnd1, rnd2, rnd3, 1.0);
particles[index].life = 5.0; // ์๋ช
5์ด
particles[index].size = rnd1 * 0.5 + 0.5; // ํฌ๊ธฐ (0.5 ~ 1.0)
}
}
)";
์ปดํจํธ ์ ฐ์ด๋๋ฅผ ์ฌ์ฉํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ์์ ์ GPU์์ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์์ด:
- ํํฐํด ์์คํ ์๋ฎฌ๋ ์ด์
- ๋ฌผ๋ฆฌ ์๋ฎฌ๋ ์ด์ (์ฒ, ์ ์ฒด ๋ฑ)
- ์ด๋ฏธ์ง ์ฒ๋ฆฌ ๋ฐ ํํฐ ์ ์ฉ
- ์งํ ์์ฑ ๋ฐ ๋ณํ
- ๋ฐ์ดํฐ ์์ถ ๋ฐ ์ํธํ
์ด๋ฌํ ๊ณ ๊ธ ๊ทธ๋ํฝ์ค ๊ธฐ๋ฒ๋ค์ ๋ง์คํฐํ๋ฉด ์ ๋ง ๋ฉ์ง ์๊ฐ์ ํจ๊ณผ๋ฅผ ๋ง๋ค ์ ์์ด. ํ์ง๋ง ์ฑ๋ฅ๋ ์ค์ํ๋, ๋ค์์ผ๋ก ์ต์ ํ ์ ๋ต์ ์์๋ณด์!
9. ์ฑ๋ฅ ์ต์ ํ ์ ๋ต โก
์๋ฌด๋ฆฌ ๋ฉ์ง ๊ทธ๋ํฝ์ค ํจ๊ณผ๋ ์ฑ๋ฅ์ด ๋์๋ฉด ์ฌ์ฉ์ ๊ฒฝํ์ด ๋จ์ด์ ธ. ํนํ ์ค์๊ฐ ๊ทธ๋ํฝ์ค ์ ํ๋ฆฌ์ผ์ด์ ์์๋ ์ฑ๋ฅ ์ต์ ํ๊ฐ ๋งค์ฐ ์ค์ํด. 2025๋ ํ์ฌ ๊ฐ์ฅ ํจ๊ณผ์ ์ธ ์ต์ ํ ์ ๋ต๋ค์ ์์๋ณด์!
1. ์ปฌ๋ง ๊ธฐ๋ฒ
์ปฌ๋ง(Culling)์ ํ๋ฉด์ ๋ณด์ด์ง ์๋ ๊ฐ์ฒด๋ ๋ถ๋ถ์ ๋ ๋๋งํ์ง ์๋ ๊ธฐ๋ฒ์ด์ผ. ๋ค์ํ ์ปฌ๋ง ๊ธฐ๋ฒ์ด ์์ด:
-
ํ๋ฉด ์ปฌ๋ง(Backface Culling): ์นด๋ฉ๋ผ๋ฅผ ํฅํ์ง ์๋ ํด๋ฆฌ๊ณค์ ๊ทธ๋ฆฌ์ง ์๋ ๊ธฐ๋ฒ. OpenGL์์๋ ๊ธฐ๋ณธ์ ์ผ๋ก ํ์ฑํ๋์ด ์์ด.
glEnable(GL_CULL_FACE); glCullFace(GL_BACK); glFrontFace(GL_CCW);
-
์์ผ ์ ๋์ฒด ์ปฌ๋ง(View Frustum Culling): ์นด๋ฉ๋ผ์ ์์ผ ๋ฒ์ ๋ฐ์ ์๋ ๊ฐ์ฒด๋ฅผ ๊ทธ๋ฆฌ์ง ์๋ ๊ธฐ๋ฒ.
bool isInViewFrustum(const glm::vec3& center, float radius, const glm::mat4& viewProjection) { // ๊ตฌ์ฒด๋ฅผ ์ฌ์ฉํ ๊ฐ๋จํ ์์ผ ์ ๋์ฒด ํ ์คํธ for (int i = 0; i < 6; ++i) { glm::vec4 plane = extractPlane(viewProjection, i); if (glm::dot(glm::vec3(plane), center) + plane.w < -radius) return false; } return true; }
-
ํ์ ์ปฌ๋ง(Occlusion Culling): ๋ค๋ฅธ ๊ฐ์ฒด์ ์ํด ๊ฐ๋ ค์ง ๊ฐ์ฒด๋ฅผ ๊ทธ๋ฆฌ์ง ์๋ ๊ธฐ๋ฒ. OpenGL์์๋ ์ฟผ๋ฆฌ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํด ๊ตฌํํ ์ ์์ด.
// ํ์ ์ฟผ๋ฆฌ ๊ฐ์ฒด ์์ฑ unsigned int occlusionQuery; glGenQueries(1, &occlusionQuery); // ํ์ ํ ์คํธ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // ์์ ์ถ๋ ฅ ๋นํ์ฑํ glDepthMask(GL_FALSE); // ๊น์ด ๋ฒํผ ์ฐ๊ธฐ ๋นํ์ฑํ glBeginQuery(GL_SAMPLES_PASSED, occlusionQuery); // ๊ฐ๋จํ ๋ฐ์ด๋ฉ ๋ฐ์ค ๊ทธ๋ฆฌ๊ธฐ glEndQuery(GL_SAMPLES_PASSED); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // ์์ ์ถ๋ ฅ ๋ค์ ํ์ฑํ glDepthMask(GL_TRUE); // ๊น์ด ๋ฒํผ ์ฐ๊ธฐ ๋ค์ ํ์ฑํ // ๊ฒฐ๊ณผ ํ์ธ int samplesPassed; glGetQueryObjectiv(occlusionQuery, GL_QUERY_RESULT, &samplesPassed); if (samplesPassed > 0) { // ๊ฐ์ฒด๊ฐ ๋ณด์ด๋ฏ๋ก ๋ ๋๋ง }
2. LOD (Level of Detail)
LOD๋ ๊ฐ์ฒด์ ์นด๋ฉ๋ผ ์ฌ์ด์ ๊ฑฐ๋ฆฌ์ ๋ฐ๋ผ ๋ค์ํ ์์ธ๋์ ๋ชจ๋ธ์ ์ฌ์ฉํ๋ ๊ธฐ๋ฒ์ด์ผ. ๋ฉ๋ฆฌ ์๋ ๊ฐ์ฒด๋ ๋ ์์ธํ ๋ชจ๋ธ์ ์ฌ์ฉํด ์ฑ๋ฅ์ ํฅ์์ํฌ ์ ์์ด.
// LOD ๊ตฌํ ์์
float distanceToCamera = glm::length(cameraPos - objectPos);
if (distanceToCamera < 10.0f) {
// ๊ฐ๊น์ด ๊ฑฐ๋ฆฌ: ๊ณ ํด์๋ ๋ชจ๋ธ ์ฌ์ฉ
highResModel.Draw(shader);
} else if (distanceToCamera < 50.0f) {
// ์ค๊ฐ ๊ฑฐ๋ฆฌ: ์ค๊ฐ ํด์๋ ๋ชจ๋ธ ์ฌ์ฉ
mediumResModel.Draw(shader);
} else {
// ๋จผ ๊ฑฐ๋ฆฌ: ์ ํด์๋ ๋ชจ๋ธ ์ฌ์ฉ
lowResModel.Draw(shader);
}
3. ์ธ์คํด์ฑ
์ธ์คํด์ฑ(Instancing)์ ๋์ผํ ๋ชจ๋ธ์ ์ฌ๋ฌ ๋ฒ ๊ทธ๋ฆด ๋ CPU์ GPU ์ฌ์ด์ ํต์ ์ ์ต์ํํ๋ ๊ธฐ๋ฒ์ด์ผ. ๋๋ฌด, ํ, ๋ ๋ฑ ๋์ผํ ๊ฐ์ฒด๊ฐ ๋ง์ด ๋ฐ๋ณต๋๋ ์ฅ๋ฉด์์ ๋งค์ฐ ํจ๊ณผ์ ์ด์ผ.
// ์ธ์คํด์ค ๋ฐ์ดํฐ ์ค์
glm::vec3 translations[1000];
int index = 0;
float offset = 5.0f;
for (int y = -10; y < 10; y += 2) {
for (int x = -10; x < 10; x += 2) {
glm::vec3 translation;
translation.x = (float)x / 10.0f * offset;
translation.y = (float)y / 10.0f * offset;
translation.z = 0.0f;
translations[index++] = translation;
}
}
// ์ธ์คํด์ค VBO ์ค์
unsigned int instanceVBO;
glGenBuffers(1, &instanceVBO);
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * 1000, &translations[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// VAO์ ์ธ์คํด์ค ์์ฑ ์ถ๊ฐ
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO);
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(glm::vec3), (void*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glVertexAttribDivisor(3, 1); // ์ธ์คํด์ค๋ง๋ค ํ ๋ฒ์ฉ ์์ฑ ์
๋ฐ์ดํธ
glBindVertexArray(0);
// ์ธ์คํด์ฑ ๋ ๋๋ง
glBindVertexArray(VAO);
glDrawArraysInstanced(GL_TRIANGLES, 0, 36, 1000); // 1000๊ฐ์ ํ๋ธ ๊ทธ๋ฆฌ๊ธฐ
4. ๋ฐฐ์น ์ฒ๋ฆฌ
๋ฐฐ์น ์ฒ๋ฆฌ(Batching)๋ ์ ์ฌํ ๊ฐ์ฒด๋ค์ ํ๋์ ํฐ ๊ฐ์ฒด๋ก ํฉ์ณ์ ๋๋ก์ฐ ์ฝ(Draw Call)์ ์ค์ด๋ ๊ธฐ๋ฒ์ด์ผ. ํนํ ์ ์ ์ธ ๊ฐ์ฒด๋ค์ ํจ๊ณผ์ ์ด์ผ.
// ์ฌ๋ฌ ๋ฉ์๋ฅผ ํ๋๋ก ํฉ์น๋ ํจ์
Mesh combineMeshes(const std::vector<mesh>& meshes) {
std::vector<vertex> combinedVertices;
std::vector<unsigned int> combinedIndices;
std::vector<texture> combinedTextures;
unsigned int indexOffset = 0;
for (const auto& mesh : meshes) {
// ์ ์ ๋ฐ์ดํฐ ๋ณต์ฌ
combinedVertices.insert(combinedVertices.end(), mesh.vertices.begin(), mesh.vertices.end());
// ์ธ๋ฑ์ค ๋ฐ์ดํฐ ๋ณต์ฌ (์คํ์
์ ์ฉ)
for (unsigned int index : mesh.indices) {
combinedIndices.push_back(index + indexOffset);
}
indexOffset += mesh.vertices.size();
// ํ
์ค์ฒ ๋ฐ์ดํฐ ๋ณต์ฌ (์ค๋ณต ์ ๊ฑฐ)
for (const auto& texture : mesh.textures) {
bool skip = false;
for (const auto& existingTexture : combinedTextures) {
if (texture.path == existingTexture.path) {
skip = true;
break;
}
}
if (!skip) {
combinedTextures.push_back(texture);
}
}
}
return Mesh(combinedVertices, combinedIndices, combinedTextures);
}</texture></unsigned></vertex></mesh>
5. ์ ฐ์ด๋ ์ต์ ํ
์ ฐ์ด๋๋ GPU์์ ์คํ๋๋ฏ๋ก ์ต์ ํ๊ฐ ๋งค์ฐ ์ค์ํด. ๋ค์์ ์ ฐ์ด๋ ์ต์ ํ๋ฅผ ์ํ ๋ช ๊ฐ์ง ํ์ด์ผ:
-
๊ณ์ฐ ๊ฐ์ํ: ๋ณต์กํ ์ํ ์ฐ์ฐ์ ๋ฏธ๋ฆฌ ๊ณ์ฐํ๊ฑฐ๋ ๊ทผ์ฌ๊ฐ ์ฌ์ฉํ๊ธฐ
// ๋นํจ์จ์ ์ธ ์ฝ๋ float result = pow(x, 5.0); // ์ต์ ํ๋ ์ฝ๋ float result = x * x * x * x * x;
-
๋ถ๊ธฐ๋ฌธ ์ต์ํ: GPU๋ ๋ณ๋ ฌ ์ฒ๋ฆฌ๋ฅผ ํ๋ฏ๋ก ๋ถ๊ธฐ๋ฌธ(if-else)์ด ์ฑ๋ฅ์ ํฌ๊ฒ ์ ํ์ํฌ ์ ์์ด
// ๋นํจ์จ์ ์ธ ์ฝ๋ if (intensity > 0.5) color = vec3(1.0); else color = vec3(0.0); // ์ต์ ํ๋ ์ฝ๋ color = vec3(step(0.5, intensity));
- ํ ์ค์ฒ ์ํ๋ง ์ต์ํ: ํ ์ค์ฒ ์ํ๋ง์ ๋น์ฉ์ด ํฐ ์ฐ์ฐ์ด๋ฏ๋ก ํ์ํ ๊ฒฝ์ฐ์๋ง ์ฌ์ฉํ๊ธฐ
-
์ ๋ฐ๋ ์กฐ์ : ํ์ํ ์ ๋ฐ๋๋ง ์ฌ์ฉํ๊ธฐ
// ๊ณ ์ ๋ฐ๋ (๊ธฐ๋ณธ๊ฐ) precision highp float; // ์ค๊ฐ ์ ๋ฐ๋ (์ฑ๋ฅ ํฅ์) precision mediump float; // ์ ์ ๋ฐ๋ (์ต๋ ์ฑ๋ฅ, ์ ๋ฐ๋ ๋ฎ์) precision lowp float;
์ฑ๋ฅ ์ต์ ํ๋ ํญ์ ์ธก์ ์ ๊ธฐ๋ฐ์ผ๋ก ํด์ผ ํด! ํ๋กํ์ผ๋ง ๋๊ตฌ๋ฅผ ์ฌ์ฉํด์ ์ค์ ๋ณ๋ชฉ ์ง์ ์ ์ฐพ๊ณ , ๊ทธ์ ๋ง๋ ์ต์ ํ ์ ๋ต์ ์ ์ฉํ๋ ๊ฒ์ด ์ค์ํด.
6. ๋ฉํฐ์ค๋ ๋ฉ
OpenGL ์์ฒด๋ ๋จ์ผ ์ค๋ ๋ API์ง๋ง, ๋ฆฌ์์ค ๋ก๋ฉ, ๋ฌผ๋ฆฌ ๊ณ์ฐ, AI ๋ฑ์ ์์ ์ ๋ณ๋์ ์ค๋ ๋์์ ์ฒ๋ฆฌํ ์ ์์ด. C++11๋ถํฐ ๋์ ๋ ์ค๋ ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํ์ฉํด๋ณด์:
#include <thread>
#include <mutex>
#include <vector>
std::mutex resourceMutex;
std::vector<model> modelsToLoad;
std::vector<model> loadedModels;
bool loadingComplete = false;
// ๋ฆฌ์์ค ๋ก๋ฉ ์ค๋ ๋ ํจ์
void resourceLoadingThread() {
while (!modelsToLoad.empty()) {
// ๋ค์ ๋ชจ๋ธ ๊ฐ์ ธ์ค๊ธฐ
Model* model = nullptr;
{
std::lock_guard<:mutex> lock(resourceMutex);
if (!modelsToLoad.empty()) {
model = modelsToLoad.back();
modelsToLoad.pop_back();
}
}
if (model) {
// ๋ชจ๋ธ ๋ก๋ฉ (์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆฌ๋ ์์
)
model->load();
// ๋ก๋ฉ๋ ๋ชจ๋ธ ๋ชฉ๋ก์ ์ถ๊ฐ
{
std::lock_guard<:mutex> lock(resourceMutex);
loadedModels.push_back(model);
}
}
}
// ๋ก๋ฉ ์๋ฃ ํ์
std::lock_guard<:mutex> lock(resourceMutex);
loadingComplete = true;
}
int main() {
// ๋ก๋ฉํ ๋ชจ๋ธ ์ถ๊ฐ
modelsToLoad.push_back(new Model("model1.obj"));
modelsToLoad.push_back(new Model("model2.obj"));
modelsToLoad.push_back(new Model("model3.obj"));
// ๋ก๋ฉ ์ค๋ ๋ ์์
std::thread loadingThread(resourceLoadingThread);
loadingThread.detach(); // ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์คํ
// ๋ฉ์ธ ๋ ๋๋ง ๋ฃจํ
while (!glfwWindowShouldClose(window)) {
// ๋ก๋ฉ๋ ๋ชจ๋ธ ํ์ธ ๋ฐ ๋ ๋๋ง
{
std::lock_guard<:mutex> lock(resourceMutex);
for (auto model : loadedModels) {
model->Draw(shader);
}
}
// ๋ก๋ฉ ์งํ ์ํฉ ํ์
if (!loadingComplete) {
renderLoadingScreen();
}
// ๋๋จธ์ง ๋ ๋๋ง ์ฝ๋...
}
return 0;
}</:mutex></:mutex></:mutex></:mutex></model></model>
์ด์ ์ฑ๋ฅ ์ต์ ํ ์ ๋ต์ ๋ฐฐ์ ์ผ๋, ๋ง์ง๋ง์ผ๋ก ์ค์ ํ๋ก์ ํธ๋ฅผ ํตํด ์ง๊ธ๊น์ง ๋ฐฐ์ด ๋ด์ฉ์ ์ข ํฉํด๋ณด์!
10. ์ค์ ํ๋ก์ ํธ: ๋ฏธ๋ ๊ฒ์ ์์ง ๋ง๋ค๊ธฐ ๐ฎ
์ง๊ธ๊น์ง ๋ฐฐ์ด ๋ชจ๋ ๋ด์ฉ์ ์ข ํฉํด์ ๊ฐ๋จํ ๋ฏธ๋ ๊ฒ์ ์์ง์ ๋ง๋ค์ด๋ณด์! ์ด ์์ง์ ๊ธฐ๋ณธ์ ์ธ ๋ ๋๋ง, ์ ๋ ฅ ์ฒ๋ฆฌ, ๋ฌผ๋ฆฌ, ์ค๋์ค ๋ฑ์ ๊ธฐ๋ฅ์ ํฌํจํ ๊ฑฐ์ผ.
์์ง ๊ตฌ์กฐ ์ค๊ณ
๋จผ์ ์์ง์ ์ ์ฒด์ ์ธ ๊ตฌ์กฐ๋ฅผ ์ค๊ณํด๋ณด์:
// Engine.h - ์์ง์ ์ฃผ์ ํด๋์ค
#pragma once
#include "Renderer.h"
#include "InputManager.h"
#include "SceneManager.h"
#include "ResourceManager.h"
#include "AudioManager.h"
#include "PhysicsSystem.h"
#include "EntityManager.h"
class Engine {
public:
// ์ฑ๊ธํค ์ธ์คํด์ค ์ป๊ธฐ
static Engine& getInstance() {
static Engine instance;
return instance;
}
// ์์ง ์ด๊ธฐํ
bool initialize(int width, int height, const std::string& title);
// ๊ฒ์ ๋ฃจํ ์คํ
void run();
// ์์ง ์ข
๋ฃ
void shutdown();
// ์๋ธ์์คํ
์ ๊ทผ์
Renderer& getRenderer() { return renderer; }
InputManager& getInputManager() { return inputManager; }
SceneManager& getSceneManager() { return sceneManager; }
ResourceManager& getResourceManager() { return resourceManager; }
AudioManager& getAudioManager() { return audioManager; }
PhysicsSystem& getPhysicsSystem() { return physicsSystem; }
EntityManager& getEntityManager() { return entityManager; }
private:
// ์์ฑ์ (์ฑ๊ธํค์ด๋ฏ๋ก private)
Engine() {}
// ๋ณต์ฌ ์์ฑ์์ ๋์
์ฐ์ฐ์ ์ญ์
Engine(const Engine&) = delete;
Engine& operator=(const Engine&) = delete;
// ๊ฒ์ ๋ฃจํ ์
๋ฐ์ดํธ
void update(float deltaTime);
// ์๋ธ์์คํ
Renderer renderer;
InputManager inputManager;
SceneManager sceneManager;
ResourceManager resourceManager;
AudioManager audioManager;
PhysicsSystem physicsSystem;
EntityManager entityManager;
// ์์ง ์ํ
bool running = false;
float targetFrameRate = 60.0f;
GLFWwindow* window = nullptr;
};
๋ ๋๋ฌ ๊ตฌํ
๋ ๋๋ฌ๋ ๊ทธ๋ํฝ์ค ๋ ๋๋ง์ ๋ด๋นํ๋ ํต์ฌ ์ปดํฌ๋ํธ์ผ:
// Renderer.h
#pragma once
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include "Camera.h"
#include "Shader.h"
#include "Model.h"
#include "Light.h"
#include <vector>
#include <unordered_map>
class Renderer {
public:
bool initialize(GLFWwindow* window);
void shutdown();
// ๋ ๋๋ง ์์/์ข
๋ฃ
void beginFrame();
void endFrame();
// ์นด๋ฉ๋ผ ์ค์
void setCamera(const Camera& camera);
// ์กฐ๋ช
๊ด๋ฆฌ
void addLight(const Light& light);
void removeLight(int lightId);
void updateLight(int lightId, const Light& light);
// ๋ ๋๋ง ํจ์
void renderModel(const Model& model, const glm::mat4& transform);
void renderSkybox(const Skybox& skybox);
void renderTerrain(const Terrain& terrain);
void renderParticles(const ParticleSystem& particles);
void renderUI(const UIElement& element);
// ํ์ฒ๋ฆฌ ํจ๊ณผ
void enablePostProcessing(bool enable);
void addPostProcessingEffect(const std::string& name, Shader* shader);
void removePostProcessingEffect(const std::string& name);
private:
// ๋ด๋ถ ๋ ๋๋ง ์ํ
GLFWwindow* window = nullptr;
Camera camera;
std::vector<light> lights;
// ์
ฐ์ด๋ ์บ์
std::unordered_map<:string shader> shaders;
// ํ๋ ์๋ฒํผ ๊ฐ์ฒด
unsigned int framebuffer = 0;
unsigned int colorBuffer = 0;
unsigned int depthBuffer = 0;
// ํ์ฒ๋ฆฌ ํจ๊ณผ
bool postProcessingEnabled = false;
std::unordered_map<:string shader> postProcessingEffects;
// ๋ด๋ถ ํฌํผ ํจ์
void setupFramebuffer(int width, int height);
void setupShaders();
void updateShaderUniforms(Shader* shader);
};</:string></:string></light>
์ํฐํฐ ์ปดํฌ๋ํธ ์์คํ
ํ๋์ ์ธ ๊ฒ์ ์์ง์ ๋๋ถ๋ถ ์ํฐํฐ ์ปดํฌ๋ํธ ์์คํ (ECS)์ ์ฌ์ฉํด. ์ด๋ ๊ฒ์ ๊ฐ์ฒด๋ฅผ ์ ์ฐํ๊ฒ ๊ตฌ์ฑํ ์ ์๊ฒ ํด์ค:
// Component.h - ๊ธฐ๋ณธ ์ปดํฌ๋ํธ ์ธํฐํ์ด์ค
#pragma once
#include <typeindex>
#include <typeinfo>
class Entity;
class Component {
public:
virtual ~Component() = default;
virtual void initialize() {}
virtual void update(float deltaTime) {}
virtual void render() {}
Entity* getOwner() const { return owner; }
void setOwner(Entity* entity) { owner = entity; }
// ์ปดํฌ๋ํธ ํ์
ID ์ป๊ธฐ
virtual std::type_index getType() const {
return std::type_index(typeid(*this));
}
private:
Entity* owner = nullptr;
};
// TransformComponent.h - ๋ณํ ์ปดํฌ๋ํธ
#pragma once
#include "Component.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
class TransformComponent : public Component {
public:
TransformComponent(const glm::vec3& position = glm::vec3(0.0f),
const glm::vec3& rotation = glm::vec3(0.0f),
const glm::vec3& scale = glm::vec3(1.0f))
: position(position), rotation(rotation), scale(scale) {
updateMatrix();
}
void setPosition(const glm::vec3& pos) {
position = pos;
updateMatrix();
}
void setRotation(const glm::vec3& rot) {
rotation = rot;
updateMatrix();
}
void setScale(const glm::vec3& scl) {
scale = scl;
updateMatrix();
}
const glm::vec3& getPosition() const { return position; }
const glm::vec3& getRotation() const { return rotation; }
const glm::vec3& getScale() const { return scale; }
const glm::mat4& getMatrix() const { return matrix; }
private:
glm::vec3 position;
glm::vec3 rotation;
glm::vec3 scale;
glm::mat4 matrix;
void updateMatrix() {
matrix = glm::mat4(1.0f);
matrix = glm::translate(matrix, position);
matrix = glm::rotate(matrix, glm::radians(rotation.x), glm::vec3(1.0f, 0.0f, 0.0f));
matrix = glm::rotate(matrix, glm::radians(rotation.y), glm::vec3(0.0f, 1.0f, 0.0f));
matrix = glm::rotate(matrix, glm::radians(rotation.z), glm::vec3(0.0f, 0.0f, 1.0f));
matrix = glm::scale(matrix, scale);
}
};
// MeshRendererComponent.h - ๋ฉ์ ๋ ๋๋ง ์ปดํฌ๋ํธ
#pragma once
#include "Component.h"
#include "Model.h"
#include "Shader.h"
class MeshRendererComponent : public Component {
public:
MeshRendererComponent(Model* model, Shader* shader)
: model(model), shader(shader) {}
void render() override {
if (model && shader) {
// TransformComponent ๊ฐ์ ธ์ค๊ธฐ
auto transform = getOwner()->getComponent<transformcomponent>();
if (transform) {
Engine::getInstance().getRenderer().renderModel(*model, transform->getMatrix());
}
}
}
private:
Model* model = nullptr;
Shader* shader = nullptr;
};
// Entity.h - ์ํฐํฐ ํด๋์ค
#pragma once
#include "Component.h"
#include <vector>
#include <memory>
#include <typeindex>
#include <unordered_map>
class Entity {
public:
Entity(const std::string& name = "Entity") : name(name) {}
// ์ปดํฌ๋ํธ ์ถ๊ฐ
template<typename t typename... args>
T* addComponent(Args&&... args) {
static_assert(std::is_base_of<component t>::value, "T must derive from Component");
auto component = std::make_unique<t>(std::forward<args>(args)...);
component->setOwner(this);
component->initialize();
auto type = std::type_index(typeid(T));
components[type] = std::move(component);
return static_cast<t>(components[type].get());
}
// ์ปดํฌ๋ํธ ๊ฐ์ ธ์ค๊ธฐ
template<typename t>
T* getComponent() const {
static_assert(std::is_base_of<component t>::value, "T must derive from Component");
auto type = std::type_index(typeid(T));
auto it = components.find(type);
if (it != components.end()) {
return static_cast<t>(it->second.get());
}
return nullptr;
}
// ์ปดํฌ๋ํธ ์ ๊ฑฐ
template<typename t>
void removeComponent() {
static_assert(std::is_base_of<component t>::value, "T must derive from Component");
auto type = std::type_index(typeid(T));
components.erase(type);
}
// ์
๋ฐ์ดํธ ๋ฐ ๋ ๋๋ง
void update(float deltaTime) {
for (auto& pair : components) {
pair.second->update(deltaTime);
}
}
void render() {
for (auto& pair : components) {
pair.second->render();
}
}
// ์ด๋ฆ ๊ด๋ จ
const std::string& getName() const { return name; }
void setName(const std::string& newName) { name = newName; }
private:
std::string name;
std::unordered_map<:type_index std::unique_ptr>> components;
};</:type_index></component></typename></t></component></typename></t></args></t></component></typename></transformcomponent>
๊ฒ์ ์ฅ๋ฉด ๊ด๋ฆฌ
์ฅ๋ฉด(Scene) ๊ด๋ฆฌ๋ ๊ฒ์์ ๋ค์ํ ๋ ๋ฒจ์ด๋ ์ํ๋ฅผ ๊ด๋ฆฌํ๋ ๋ฐ ์ค์ํด:
// Scene.h
#pragma once
#include "Entity.h"
#include <vector>
#include <memory>
#include <string>
class Scene {
public:
Scene(const std::string& name) : name(name) {}
virtual ~Scene() = default;
// ์ฅ๋ฉด ์๋ช
์ฃผ๊ธฐ
virtual void initialize() {}
virtual void update(float deltaTime) {
for (auto& entity : entities) {
entity->update(deltaTime);
}
}
virtual void render() {
for (auto& entity : entities) {
entity->render();
}
}
virtual void shutdown() {}
// ์ํฐํฐ ๊ด๋ฆฌ
Entity* createEntity(const std::string& name = "Entity") {
auto entity = std::make_unique<entity>(name);
auto entityPtr = entity.get();
entities.push_back(std::move(entity));
return entityPtr;
}
void destroyEntity(Entity* entity) {
auto it = std::find_if(entities.begin(), entities.end(),
[entity](const std::unique_ptr<entity>& e) { return e.get() == entity; });
if (it != entities.end()) {
entities.erase(it);
}
}
// ์ด๋ฆ ๊ด๋ จ
const std::string& getName() const { return name; }
private:
std::string name;
std::vector<:unique_ptr>> entities;
};
// SceneManager.h
#pragma once
#include "Scene.h"
#include <unordered_map>
#include <memory>
#include <string>
class SceneManager {
public:
// ์ฅ๋ฉด ๋ฑ๋ก
void registerScene(const std::string& name, std::unique_ptr<scene> scene) {
scenes[name] = std::move(scene);
}
// ์ฅ๋ฉด ์ ํ
void changeScene(const std::string& name) {
if (currentScene) {
currentScene->shutdown();
}
auto it = scenes.find(name);
if (it != scenes.end()) {
currentScene = it->second.get();
currentScene->initialize();
}
}
// ํ์ฌ ์ฅ๋ฉด ์
๋ฐ์ดํธ ๋ฐ ๋ ๋๋ง
void update(float deltaTime) {
if (currentScene) {
currentScene->update(deltaTime);
}
}
void render() {
if (currentScene) {
currentScene->render();
}
}
// ํ์ฌ ์ฅ๋ฉด ๊ฐ์ ธ์ค๊ธฐ
Scene* getCurrentScene() const {
return currentScene;
}
private:
std::unordered_map<:string std::unique_ptr>> scenes;
Scene* currentScene = nullptr;
};</:string></scene></:unique_ptr></entity></entity>
๊ฒ์ ์์ง ์ฌ์ฉ ์์
์ด์ ๋ง๋ ์์ง์ ์ฌ์ฉํด์ ๊ฐ๋จํ ๊ฒ์์ ๋ง๋ค์ด๋ณด์:
// GameApp.cpp
#include "Engine.h"
#include "GameScene.h"
#include "MenuScene.h"
int main() {
// ์์ง ์ด๊ธฐํ
auto& engine = Engine::getInstance();
if (!engine.initialize(1280, 720, "My Game")) {
return -1;
}
// ์ฅ๋ฉด ๋ฑ๋ก
auto sceneManager = &engine.getSceneManager();
sceneManager->registerScene("Menu", std::make_unique<menuscene>());
sceneManager->registerScene("Game", std::make_unique<gamescene>());
// ์์ ์ฅ๋ฉด ์ค์
sceneManager->changeScene("Menu");
// ๊ฒ์ ๋ฃจํ ์คํ
engine.run();
// ์์ง ์ข
๋ฃ
engine.shutdown();
return 0;
}
// GameScene.h
#pragma once
#include "Scene.h"
#include "Engine.h"
class GameScene : public Scene {
public:
GameScene() : Scene("GameScene") {}
void initialize() override {
auto& engine = Engine::getInstance();
auto& resourceManager = engine.getResourceManager();
// ๋ฆฌ์์ค ๋ก๋ฉ
auto playerModel = resourceManager.loadModel("assets/models/player.obj");
auto playerShader = resourceManager.loadShader("assets/shaders/pbr.vs", "assets/shaders/pbr.fs");
auto terrainModel = resourceManager.loadModel("assets/models/terrain.obj");
auto terrainShader = resourceManager.loadShader("assets/shaders/terrain.vs", "assets/shaders/terrain.fs");
// ํ๋ ์ด์ด ์ํฐํฐ ์์ฑ
auto player = createEntity("Player");
auto transform = player->addComponent<transformcomponent>(glm::vec3(0.0f, 0.0f, 0.0f));
player->addComponent<meshrenderercomponent>(playerModel, playerShader);
player->addComponent<playercontrollercomponent>();
player->addComponent<collidercomponent>(ColliderType::Capsule, 1.0f, 2.0f);
// ์งํ ์ํฐํฐ ์์ฑ
auto terrain = createEntity("Terrain");
terrain->addComponent<transformcomponent>(glm::vec3(0.0f, -1.0f, 0.0f));
terrain->addComponent<meshrenderercomponent>(terrainModel, terrainShader);
terrain->addComponent<collidercomponent>(ColliderType::Mesh);
// ์นด๋ฉ๋ผ ์ค์
auto camera = createEntity("MainCamera");
auto cameraTransform = camera->addComponent<transformcomponent>(glm::vec3(0.0f, 2.0f, 5.0f));
auto cameraComponent = camera->addComponent<cameracomponent>(60.0f, 0.1f, 1000.0f);
camera->addComponent<thirdpersoncameracomponent>(player, glm::vec3(0.0f, 2.0f, 0.0f), 5.0f);
// ์กฐ๋ช
์ค์
auto directionalLight = createEntity("DirectionalLight");
directionalLight->addComponent<transformcomponent>();
directionalLight->addComponent<lightcomponent>(LightType::Directional, glm::vec3(1.0f), 1.0f);
// ๋ฐฐ๊ฒฝ ์์
์ฌ์
engine.getAudioManager().playMusic("assets/audio/background.mp3", true);
}
void update(float deltaTime) override {
Scene::update(deltaTime);
// ๊ฒ์ ํน์ ๋ก์ง
auto& inputManager = Engine::getInstance().getInputManager();
if (inputManager.isKeyPressed(GLFW_KEY_ESCAPE)) {
Engine::getInstance().getSceneManager().changeScene("Menu");
}
}
};</lightcomponent></transformcomponent></thirdpersoncameracomponent></cameracomponent></transformcomponent></collidercomponent></meshrenderercomponent></transformcomponent></collidercomponent></playercontrollercomponent></meshrenderercomponent></transformcomponent></gamescene></menuscene>
์ด๋ ๊ฒ ๋ง๋ ๋ฏธ๋ ๊ฒ์ ์์ง์ ์ค์ ์์ฉ ๊ฒ์ ์์ง์ ๋นํ๋ฉด ๋งค์ฐ ๊ฐ๋จํ์ง๋ง, OpenGL๊ณผ C++๋ฅผ ํ์ฉํ ๊ทธ๋ํฝ์ค ํ๋ก๊ทธ๋๋ฐ์ ๊ธฐ๋ณธ ์๋ฆฌ๋ฅผ ์ดํดํ๋ ๋ฐ ํฐ ๋์์ด ๋ ๊ฑฐ์ผ!
์ฌ๋ฅ๋ท์์๋ ์ด๋ฐ ๊ฒ์ ๊ฐ๋ฐ ๊ด๋ จ ํ๋ก์ ํธ๋ฅผ ํจ๊ป ์งํํ๊ฑฐ๋ ๋ฉํ ๋ง์ ๋ฐ์ ์ ์๋ ์๋น์ค๋ ์ ๊ณตํ๊ณ ์์ด. ํผ์ ๊ณต๋ถํ๊ธฐ ์ด๋ ต๋ค๋ฉด ์ ๋ฌธ๊ฐ์ ๋์์ ๋ฐ์๋ณด๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ผ!
๋ง์น๋ฉฐ ๐
๋๋์ด OpenGL๊ณผ C++๋ฅผ ํ์ฉํ ๊ทธ๋ํฝ์ค ํ๋ก๊ทธ๋๋ฐ ํํ ๋ฆฌ์ผ์ ๋ชจ๋ ๋ง์ณค์ด! ๊ธฐ๋ณธ์ ์ธ ์ฐฝ ์์ฑ๋ถํฐ ๊ณ ๊ธ ๊ทธ๋ํฝ์ค ๊ธฐ๋ฒ, ์ฑ๋ฅ ์ต์ ํ, ๊ทธ๋ฆฌ๊ณ ๋ฏธ๋ ๊ฒ์ ์์ง ๊ฐ๋ฐ๊น์ง ๋ค์ํ ์ฃผ์ ๋ฅผ ๋ค๋ค์ง.
์ด ํํ ๋ฆฌ์ผ์์ ๋ฐฐ์ด ๋ด์ฉ์ ์ ๋ฆฌํด๋ณด๋ฉด:
- OpenGL๊ณผ C++์ ๊ธฐ๋ณธ ๊ฐ๋ ์ดํด
- ๊ฐ๋ฐ ํ๊ฒฝ ์ค์ ๋ฐฉ๋ฒ
- ๊ธฐ๋ณธ ๋ํ ๊ทธ๋ฆฌ๊ธฐ์ ์ ฐ์ด๋ ํ๋ก๊ทธ๋๋ฐ
- ํ ์ค์ฒ์ ์กฐ๋ช ํจ๊ณผ ์ ์ฉํ๊ธฐ
- 3D ๋ชจ๋ธ ๋ก๋ฉ๊ณผ ๋ ๋๋ง
- ์นด๋ฉ๋ผ ์์คํ ๊ณผ ์ฌ์ฉ์ ์ํธ์์ฉ
- ๊ณ ๊ธ ๊ทธ๋ํฝ์ค ๊ธฐ๋ฒ (PBR, ๊ทธ๋ฆผ์ ๋งคํ ๋ฑ)
- ์ฑ๋ฅ ์ต์ ํ ์ ๋ต
- ๋ฏธ๋ ๊ฒ์ ์์ง ์ค๊ณ ๋ฐ ๊ตฌํ
๊ทธ๋ํฝ์ค ํ๋ก๊ทธ๋๋ฐ์ ๋์์์ด ๋ฐ์ ํ๋ ๋ถ์ผ์ผ. 2025๋ ํ์ฌ๋ ์๋ก์ด ๊ธฐ์ ๊ณผ ๊ธฐ๋ฒ๋ค์ด ๊ณ์ ๋ฑ์ฅํ๊ณ ์์ง. ์ด ํํ ๋ฆฌ์ผ์ด ๊ทธ๋ํฝ์ค ํ๋ก๊ทธ๋๋ฐ์ ๊ธฐ์ด๋ฅผ ๋ค์ง๋ ๋ฐ ๋์์ด ๋์๊ธธ ๋ฐ๋ผ!
๊ณ์ํด์ ๊ณต๋ถํ๊ณ ์คํํ๋ฉด์ ์์ ๋ง์ ๋ฉ์ง ๊ทธ๋ํฝ์ค ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ง๋ค์ด๋ณด๊ธธ ์์ํด! ๐
๋ ๋ง์ ํ๋ก๊ทธ๋๋ฐ ํํ ๋ฆฌ์ผ๊ณผ ์ ๋ฌธ๊ฐ์ ๋์์ด ํ์ํ๋ค๋ฉด ์ฌ๋ฅ๋ท(https://www.jaenung.net)์ ๋ฐฉ๋ฌธํด๋ณด์ธ์. ๋ค์ํ ๋ถ์ผ์ ์ ๋ฌธ๊ฐ๋ค์ด ์ฌ๋ฌ๋ถ์ ํ์ต๊ณผ ํ๋ก์ ํธ๋ฅผ ๋์๋๋ฆฝ๋๋ค!
1. OpenGL๊ณผ C++์ ๊ธฐ๋ณธ ์ดํดํ๊ธฐ ๐งฉ
OpenGL(Open Graphics Library)์ 2D ๋ฐ 3D ๊ทธ๋ํฝ์ค๋ฅผ ๋ ๋๋งํ๊ธฐ ์ํ ํฌ๋ก์ค ํ๋ซํผ API์ผ. 1992๋ ์ ์ฒ์ ๋ฑ์ฅํ ์ดํ๋ก ๊ณ์ ๋ฐ์ ํด์๊ณ , 2025๋ ํ์ฌ๋ OpenGL 5.0๊น์ง ๋์์ด. C++๊ณผ ํจ๊ป ์ฌ์ฉํ๋ฉด ์ ๋ง ๊ฐ๋ ฅํ ๊ทธ๋ํฝ์ค ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ง๋ค ์ ์์ง!
OpenGL์ ํน์ง ๐ก
1. ํฌ๋ก์ค ํ๋ซํผ: Windows, macOS, Linux, ๋ชจ๋ฐ์ผ ๋ฑ ๋ค์ํ ํ๋ซํผ์์ ๋์
2. ํ๋์จ์ด ๊ฐ์: GPU๋ฅผ ์ง์ ํ์ฉํด ๋น ๋ฅธ ๋ ๋๋ง ๊ฐ๋ฅ
3. ํ์ฅ์ฑ: ๋ค์ํ ํ์ฅ ๊ธฐ๋ฅ์ ํตํด ์ต์ ๊ทธ๋ํฝ์ค ๊ธฐ์ ์ง์
4. ์ฐ์ ํ์ค: ๊ฒ์ ๊ฐ๋ฐ, CAD, ์๋ฎฌ๋ ์ด์ ๋ฑ ๋ค์ํ ๋ถ์ผ์์ ํ์ฉ
C++์ OpenGL๊ณผ ํจ๊ป ์ฌ์ฉํ๊ธฐ์ ์๋ฒฝํ ์ธ์ด์ผ. ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ์ ์ฑ๋ฅ ์ต์ ํ๊ฐ ์ค์ํ ๊ทธ๋ํฝ์ค ํ๋ก๊ทธ๋๋ฐ์์ C++์ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ๋ค์ด ๋น์ ๋ฐํ์ง. ํนํ 2025๋ ์๋ C++23 ํ์ค์ด ๋๋ฆฌ ๋ณด๊ธ๋๋ฉด์ ๋ ๊ฐ๊ฒฐํ๊ณ ํจ์จ์ ์ธ ์ฝ๋ ์์ฑ์ด ๊ฐ๋ฅํด์ก์ด.
OpenGL์ ๊ทธ๋ํฝ์ค ํ์ดํ๋ผ์ธ์ด๋ผ๋ ๊ฐ๋ ์ ์ค์ฌ์ผ๋ก ๋์ํด. ์ด ํ์ดํ๋ผ์ธ์ 3D ๋ชจ๋ธ ๋ฐ์ดํฐ๊ฐ 2D ํ๋ฉด์ ํ์๋๊ธฐ๊น์ง์ ์ฌ๋ฌ ๋จ๊ณ๋ฅผ ํฌํจํ๊ณ ์์ด. ๊ธฐ๋ณธ์ ์ธ ํ์ดํ๋ผ์ธ ๋จ๊ณ๋ ๋ค์๊ณผ ๊ฐ์:
- ์ ์ ์ฒ๋ฆฌ(Vertex Processing): 3D ๊ณต๊ฐ์ ์ ์ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌ
- ํ ์ ๋ ์ด์ (Tessellation): ํ๋ฉด์ ๋ ์์ ๊ธฐํํ์ ์์๋ก ๋ถํ
- ์ง์ค๋ฉํธ๋ฆฌ ์ฒ๋ฆฌ(Geometry Processing): ๊ธฐํํ์ ๋ฐ์ดํฐ ์ถ๊ฐ ์ฒ๋ฆฌ
- ๋์คํฐํ(Rasterization): ๋ฒกํฐ ๋ฐ์ดํฐ๋ฅผ ํฝ์ ๋ก ๋ณํ
- ํ๋๊ทธ๋จผํธ ์ฒ๋ฆฌ(Fragment Processing): ํฝ์ ๋ณ ์์ ๋ฐ ํจ๊ณผ ์ ์ฉ
- ์ถ๋ ฅ ๋ณํฉ(Output Merger): ์ต์ข ์ด๋ฏธ์ง ์์ฑ
์ด์ OpenGL๊ณผ C++์ ๊ธฐ๋ณธ ๊ฐ๋ ์ ์ดํดํ์ผ๋, ๊ฐ๋ฐ ํ๊ฒฝ์ ์ค์ ํ๋ ๋ฐฉ๋ฒ์ ์์๋ณผ๊น?
2. ๊ฐ๋ฐ ํ๊ฒฝ ์ค์ ํ๊ธฐ (2025๋ ์ต์ ๋ฒ์ ) ๐ ๏ธ
๊ทธ๋ํฝ์ค ํ๋ก๊ทธ๋๋ฐ์ ์์ํ๊ธฐ ์ ์ ๋จผ์ ๊ฐ๋ฐ ํ๊ฒฝ์ ์ ๋๋ก ์ค์ ํด์ผ ํด. 2025๋ ๊ธฐ์ค์ผ๋ก ๊ฐ์ฅ ์ต์ ์ ๋๊ตฌ๋ค์ ์ฌ์ฉํด๋ณผ๊ฒ!
ํ์ํ ๋๊ตฌ๋ค ๐งฐ
- C++ ์ปดํ์ผ๋ฌ: GCC 13.2, Clang 18.0, MSVC 19.40 ์ค ํ๋
- IDE: Visual Studio 2025, CLion 2025.1, VS Code + C++ ํ์ฅ
- OpenGL ๋ผ์ด๋ธ๋ฌ๋ฆฌ: OpenGL 5.0 ํธํ ๋๋ผ์ด๋ฒ
- ๋ณด์กฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ: GLFW, GLAD, GLM
- ๋น๋ ์์คํ : CMake 3.28 ์ด์
์ฌ๋ฅ๋ท์์๋ ๊ทธ๋ํฝ์ค ํ๋ก๊ทธ๋๋ฐ ๊ด๋ จ ๋ฉํ ๋ง ์๋น์ค๋ ์ ๊ณตํ๊ณ ์์ด. ํ๊ฒฝ ์ค์ ์ ์ด๋ ค์์ ๊ฒช๋๋ค๋ฉด ์ ๋ฌธ๊ฐ์ ๋์์ ๋ฐ์๋ณด๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ผ!
Windows์์ ํ๊ฒฝ ์ค์ ํ๊ธฐ
Windows์์๋ Visual Studio 2025 Community Edition์ ์ฌ์ฉํ๋ ๊ฒ์ด ๊ฐ์ฅ ํธ๋ฆฌํด. ๋ค์ ๋จ๊ณ๋ฅผ ๋ฐ๋ผํด๋ด:
- Visual Studio 2025 ์ค์น (C++ ๋ฐ์คํฌํฑ ๊ฐ๋ฐ ์ํฌ๋ก๋ ์ ํ)
- vcpkg ํจํค์ง ๋งค๋์ ์ค์น:
git clone https://github.com/Microsoft/vcpkg.git cd vcpkg bootstrap-vcpkg.bat vcpkg integrate install
- ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น:
vcpkg install glfw3:x64-windows vcpkg install glad:x64-windows vcpkg install glm:x64-windows
macOS์์ ํ๊ฒฝ ์ค์ ํ๊ธฐ
macOS์์๋ Xcode์ Homebrew๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์:
- Xcode ์ค์น (App Store์์)
- Homebrew ์ค์น:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
- ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น:
brew install glfw brew install glm brew install cmake
- GLAD ์ค์ (์น ์๋น์ค ์ด์ฉ):
https://glad.dav1d.de/ ์์ OpenGL ๋ฒ์ ์ ํ ํ ์์ฑ๋ ํ์ผ ๋ค์ด๋ก๋
Linux์์ ํ๊ฒฝ ์ค์ ํ๊ธฐ
Ubuntu ๊ธฐ์ค์ผ๋ก ์ค๋ช ํ ๊ฒ:
- ํ์ํ ํจํค์ง ์ค์น:
sudo apt update sudo apt install build-essential sudo apt install libglfw3-dev sudo apt install libglm-dev sudo apt install cmake
- GLAD ์ค์ (์น ์๋น์ค ์ด์ฉ):
https://glad.dav1d.de/ ์์ OpenGL ๋ฒ์ ์ ํ ํ ์์ฑ๋ ํ์ผ ๋ค์ด๋ก๋
๐ก GLAD์ด๋?
GLAD๋ OpenGL ํจ์ ํฌ์ธํฐ๋ฅผ ๋ก๋ํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ผ. OpenGL์ API๋ง ์ ์ํ๊ณ ์ค์ ๊ตฌํ์ ๊ทธ๋ํฝ ๋๋ผ์ด๋ฒ์ ์์กดํ๊ธฐ ๋๋ฌธ์, ๋ฐํ์์ ํจ์ ํฌ์ธํฐ๋ฅผ ๋ก๋ํด์ผ ํด. GLAD๊ฐ ์ด ๊ณผ์ ์ ๊ฐ์ํํด์ฃผ์ง!
CMake ํ๋ก์ ํธ ์ค์
๋ค์์ ๊ธฐ๋ณธ์ ์ธ CMakeLists.txt ํ์ผ์ด์ผ:
cmake_minimum_required(VERSION 3.28)
project(OpenGLTutorial VERSION 1.0)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(OpenGL REQUIRED)
find_package(glfw3 REQUIRED)
find_package(glm REQUIRED)
add_executable(${PROJECT_NAME} main.cpp glad.c)
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(${PROJECT_NAME} PRIVATE OpenGL::GL glfw glm::glm)
์ด์ ๊ฐ๋ฐ ํ๊ฒฝ์ด ์ค๋น๋์์ผ๋, ์ฒซ ๋ฒ์งธ OpenGL ์ฐฝ์ ๋ง๋ค์ด๋ณผ๊น?
3. ์ฒซ ๋ฒ์งธ OpenGL ์ฐฝ ๋ง๋ค๊ธฐ ๐ช
OpenGL๋ก ๋ฌด์ธ๊ฐ๋ฅผ ๊ทธ๋ฆฌ๊ธฐ ์ ์ ๋จผ์ ์ฐฝ์ ๋ง๋ค์ด์ผ ํด. GLFW ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ฉด ์ด ๊ณผ์ ์ด ๋งค์ฐ ๊ฐ๋จํด์ ธ. ๋ค์์ ๊ธฐ๋ณธ ์ฐฝ์ ์์ฑํ๋ ์ฝ๋์ผ:
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
// ์ฐฝ ํฌ๊ธฐ ๋ณ๊ฒฝ ์ฝ๋ฐฑ ํจ์
void framebuffer_size_callback(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);
}
int main() {
// GLFW ์ด๊ธฐํ
if (!glfwInit()) {
std::cerr << "GLFW ์ด๊ธฐํ ์คํจ" << std::endl;
return -1;
}
// OpenGL ๋ฒ์ ์ค์ (4.6)
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// ์ฐฝ ์์ฑ
GLFWwindow* window = glfwCreateWindow(800, 600, "์ฒซ ๋ฒ์งธ OpenGL ์ฐฝ", NULL, NULL);
if (!window) {
std::cerr << "GLFW ์ฐฝ ์์ฑ ์คํจ" << std::endl;
glfwTerminate();
return -1;
}
// ํ์ฌ ์ปจํ
์คํธ๋ก ์ค์
glfwMakeContextCurrent(window);
// ์ฐฝ ํฌ๊ธฐ ๋ณ๊ฒฝ ์ฝ๋ฐฑ ๋ฑ๋ก
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
// GLAD ๋ก๋
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
std::cerr << "GLAD ์ด๊ธฐํ ์คํจ" << std::endl;
return -1;
}
// ๋ ๋๋ง ๋ฃจํ
while (!glfwWindowShouldClose(window)) {
// ์
๋ ฅ ์ฒ๋ฆฌ
processInput(window);
// ๋ ๋๋ง
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// ๋ฒํผ ๊ต์ฒด ๋ฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ
glfwSwapBuffers(window);
glfwPollEvents();
}
// ์ข
๋ฃ
glfwTerminate();
return 0;
}
์ด ์ฝ๋๋ฅผ ์คํํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ์ฐฝ์ด ๋ํ๋ ๊ฑฐ์ผ:
์ฝ๋๋ฅผ ์์ธํ ์ดํด๋ณด์:
- GLFW ์ด๊ธฐํ:
glfwInit()
ํจ์๋ก GLFW ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด๊ธฐํํด. - OpenGL ๋ฒ์ ์ค์ :
glfwWindowHint()
ํจ์๋ก ์ฌ์ฉํ OpenGL ๋ฒ์ ์ ์ง์ ํด. - ์ฐฝ ์์ฑ:
glfwCreateWindow()
ํจ์๋ก ์ค์ ์ฐฝ์ ์์ฑํด. - OpenGL ์ปจํ
์คํธ ์ค์ :
glfwMakeContextCurrent()
ํจ์๋ก ํ์ฌ ์ค๋ ๋์ ์ปจํ ์คํธ๋ฅผ ์ค์ ํด. - GLAD ์ด๊ธฐํ: OpenGL ํจ์ ํฌ์ธํฐ๋ฅผ ๋ก๋ํด.
- ๋ ๋๋ง ๋ฃจํ: ์ฐฝ์ด ๋ซํ ๋๊น์ง ๊ณ์ํด์ ๊ทธ๋ฆฌ๊ธฐ ์์ ์ ์ํํด.
- ์ ๋ฆฌ:
glfwTerminate()
ํจ์๋ก GLFW ๋ฆฌ์์ค๋ฅผ ์ ๋ฆฌํด.
OpenGL์ ์ขํ๊ณ๋ ์ค์์ด (0,0)์ด๊ณ , ์ค๋ฅธ์ชฝ ์๊ฐ (1,1), ์ผ์ชฝ ์๋๊ฐ (-1,-1)์ธ ์ ๊ทํ๋ ์ขํ๊ณ๋ฅผ ์ฌ์ฉํด. ์ด ์ ์ ๊ธฐ์ตํด๋๋ฉด ๋์ค์ ๋ํ์ ๊ทธ๋ฆด ๋ ๋์์ด ๋ ๊ฑฐ์ผ!
์ฐฝ ์์ฑ ์ ์์ฃผ ๋ฐ์ํ๋ ๋ฌธ์ ์ ํด๊ฒฐ์ฑ ๐ง
-
๋ฌธ์ : "GLFW ์ด๊ธฐํ ์คํจ" ์ค๋ฅ
ํด๊ฒฐ์ฑ : ๊ทธ๋ํฝ ๋๋ผ์ด๋ฒ๊ฐ ์ต์ ๋ฒ์ ์ธ์ง ํ์ธํ๊ณ , ํ์ํ ์์คํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ค์น๋์ด ์๋์ง ํ์ธํด. -
๋ฌธ์ : "GLAD ์ด๊ธฐํ ์คํจ" ์ค๋ฅ
ํด๊ฒฐ์ฑ : GLAD๊ฐ ์ฌ๋ฐ๋ฅธ OpenGL ๋ฒ์ ์ ๋์์ผ๋ก ์์ฑ๋์๋์ง ํ์ธํ๊ณ , ๊ทธ๋ํฝ ์นด๋๊ฐ ํด๋น ๋ฒ์ ์ ์ง์ํ๋์ง ํ์ธํด. -
๋ฌธ์ : ์ฐฝ์ด ์ฆ์ ๋ซํ
ํด๊ฒฐ์ฑ : ๋ ๋๋ง ๋ฃจํ์ ์ค๋ฅ๊ฐ ์๋์ง ํ์ธํ๊ณ , ๋๋ฒ๊น ๋ฉ์์ง๋ฅผ ์ถ๊ฐํด์ ์ด๋์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋์ง ํ์ ํด.
์ด์ ๊ธฐ๋ณธ ์ฐฝ์ ๋ง๋ค์์ผ๋, ๋ค์ ๋จ๊ณ๋ก ๋์ด๊ฐ์ ์ค์ ๋ก ๋ํ์ ๊ทธ๋ ค๋ณผ๊น?
- ์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์ฌ๋ฅ๋ท์ ์ต์ AI ๊ธฐ์ ๊ณผ ๋ฒ๋ฅ ์ ๊ธฐ๋ฐํ์ฌ ์์ฌ์ ์ง์ ์ฌ์ฐ๊ถ์ ์ ๊ทน์ ์ผ๋ก ๋ณดํธํ๋ฉฐ,
๋ฌด๋จ ์ฌ์ฉ ๋ฐ ์นจํด ํ์์ ๋ํด ๋ฒ์ ๋์์ ํ ๊ถ๋ฆฌ๋ฅผ ๋ณด์ ํฉ๋๋ค.
ยฉ 2025 ์ฌ๋ฅ๋ท | All rights reserved.
๋๊ธ 0๊ฐ