๐ŸŽฎ ์˜คํ”ˆGL๋กœ ์‹œ์ž‘ํ•˜๋Š” C++ 3D ๊ทธ๋ž˜ํ”ฝ์Šค ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์™„์ „์ •๋ณต ํŠœํ† ๋ฆฌ์–ผ (2025๋…„ ์ตœ์‹  ๋ฒ„์ „)

์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐ŸŽฎ ์˜คํ”ˆGL๋กœ ์‹œ์ž‘ํ•˜๋Š” C++ 3D ๊ทธ๋ž˜ํ”ฝ์Šค ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์™„์ „์ •๋ณต ํŠœํ† ๋ฆฌ์–ผ (2025๋…„ ์ตœ์‹  ๋ฒ„์ „)

 

 

์•ˆ๋…•, ๊ทธ๋ž˜ํ”ฝ์Šค ํ”„๋กœ๊ทธ๋ž˜๋ฐ์— ๊ด€์‹ฌ ์žˆ๋Š” ์นœ๊ตฌ์•ผ! ๐Ÿ–๏ธ ์˜ค๋Š˜์€ C++๊ณผ OpenGL์„ ํ™œ์šฉํ•œ ๊ทธ๋ž˜ํ”ฝ์Šค ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ์„ธ๊ณ„๋กœ ํ•จ๊ป˜ ์—ฌํ–‰์„ ๋– ๋‚˜๋ณผ๊นŒ? 2025๋…„ ํ˜„์žฌ ๊ฒŒ์ž„ ๊ฐœ๋ฐœ๋ถ€ํ„ฐ ์‹œ๋ฎฌ๋ ˆ์ด์…˜, ๋ฐ์ดํ„ฐ ์‹œ๊ฐํ™”๊นŒ์ง€ ๋‹ค์–‘ํ•œ ๋ถ„์•ผ์—์„œ ํ™œ์šฉ๋˜๋Š” OpenGL์˜ ๋ชจ๋“  ๊ฒƒ์„ ์•Œ๋ ค์ค„๊ฒŒ. ์ฝ”๋“œ ํ•œ ์ค„ ํ•œ ์ค„์ด ํ™”๋ฉด์—์„œ ์‚ด์•„ ์›€์ง์ด๋Š” ๋งˆ๋ฒ• ๊ฐ™์€ ๊ฒฝํ—˜์„ ํ•จ๊ป˜ ํ•ด๋ณด์ž!

์ด ํŠœํ† ๋ฆฌ์–ผ์„ ๋งˆ์Šคํ„ฐํ•˜๋ฉด ๋„ˆ๋„ ๋ฉ‹์ง„ 3D ๊ทธ๋ž˜ํ”ฝ์Šค ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด!

๐Ÿ“š ๋ชฉ์ฐจ

  1. OpenGL๊ณผ C++์˜ ๊ธฐ๋ณธ ์ดํ•ดํ•˜๊ธฐ
  2. ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์„ค์ •ํ•˜๊ธฐ (2025๋…„ ์ตœ์‹  ๋ฒ„์ „)
  3. ์ฒซ ๋ฒˆ์งธ OpenGL ์ฐฝ ๋งŒ๋“ค๊ธฐ
  4. ๊ธฐ๋ณธ ๋„ํ˜• ๊ทธ๋ฆฌ๊ธฐ์™€ ์…ฐ์ด๋” ํ”„๋กœ๊ทธ๋ž˜๋ฐ
  5. ํ…์Šค์ฒ˜์™€ ์กฐ๋ช… ํšจ๊ณผ ์ ์šฉํ•˜๊ธฐ
  6. 3D ๋ชจ๋ธ ๋กœ๋”ฉ๊ณผ ๋ Œ๋”๋ง
  7. ์นด๋ฉ”๋ผ ์‹œ์Šคํ…œ๊ณผ ์‚ฌ์šฉ์ž ์ƒํ˜ธ์ž‘์šฉ
  8. ๊ณ ๊ธ‰ ๊ทธ๋ž˜ํ”ฝ์Šค ๊ธฐ๋ฒ• ํƒ๊ตฌ
  9. ์„ฑ๋Šฅ ์ตœ์ ํ™” ์ „๋žต
  10. ์‹ค์ „ ํ”„๋กœ์ ํŠธ: ๋ฏธ๋‹ˆ ๊ฒŒ์ž„ ์—”์ง„ ๋งŒ๋“ค๊ธฐ

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 ๊ทธ๋ž˜ํ”ฝ์Šค ๋ Œ๋”๋ง API ํ•˜๋“œ์›จ์–ด ๊ฐ€์† ์…ฐ์ด๋” ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํฌ๋กœ์Šค ํ”Œ๋žซํผ C++ ๊ฐ์ฒด์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฉ”๋ชจ๋ฆฌ ์ง์ ‘ ๊ด€๋ฆฌ ๊ณ ์„ฑ๋Šฅ ์‹คํ–‰ ํ…œํ”Œ๋ฆฟ ๋ฉ”ํƒ€ํ”„๋กœ๊ทธ๋ž˜๋ฐ +

OpenGL์€ ๊ทธ๋ž˜ํ”ฝ์Šค ํŒŒ์ดํ”„๋ผ์ธ์ด๋ผ๋Š” ๊ฐœ๋…์„ ์ค‘์‹ฌ์œผ๋กœ ๋™์ž‘ํ•ด. ์ด ํŒŒ์ดํ”„๋ผ์ธ์€ 3D ๋ชจ๋ธ ๋ฐ์ดํ„ฐ๊ฐ€ 2D ํ™”๋ฉด์— ํ‘œ์‹œ๋˜๊ธฐ๊นŒ์ง€์˜ ์—ฌ๋Ÿฌ ๋‹จ๊ณ„๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ์–ด. ๊ธฐ๋ณธ์ ์ธ ํŒŒ์ดํ”„๋ผ์ธ ๋‹จ๊ณ„๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์•„:

  1. ์ •์  ์ฒ˜๋ฆฌ(Vertex Processing): 3D ๊ณต๊ฐ„์˜ ์ •์  ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌ
  2. ํ…Œ์…€๋ ˆ์ด์…˜(Tessellation): ํ‘œ๋ฉด์„ ๋” ์ž‘์€ ๊ธฐํ•˜ํ•™์  ์š”์†Œ๋กœ ๋ถ„ํ• 
  3. ์ง€์˜ค๋ฉ”ํŠธ๋ฆฌ ์ฒ˜๋ฆฌ(Geometry Processing): ๊ธฐํ•˜ํ•™์  ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€ ์ฒ˜๋ฆฌ
  4. ๋ž˜์Šคํ„ฐํ™”(Rasterization): ๋ฒกํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ํ”ฝ์…€๋กœ ๋ณ€ํ™˜
  5. ํ”„๋ž˜๊ทธ๋จผํŠธ ์ฒ˜๋ฆฌ(Fragment Processing): ํ”ฝ์…€๋ณ„ ์ƒ‰์ƒ ๋ฐ ํšจ๊ณผ ์ ์šฉ
  6. ์ถœ๋ ฅ ๋ณ‘ํ•ฉ(Output Merger): ์ตœ์ข… ์ด๋ฏธ์ง€ ์ƒ์„ฑ

์ด์ œ OpenGL๊ณผ C++์˜ ๊ธฐ๋ณธ ๊ฐœ๋…์„ ์ดํ•ดํ–ˆ์œผ๋‹ˆ, ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์„ ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณผ๊นŒ?

2. ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์„ค์ •ํ•˜๊ธฐ (2025๋…„ ์ตœ์‹  ๋ฒ„์ „) ๐Ÿ› ๏ธ

๊ทธ๋ž˜ํ”ฝ์Šค ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ์‹œ์ž‘ํ•˜๊ธฐ ์ „์— ๋จผ์ € ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์„ ์ œ๋Œ€๋กœ ์„ค์ •ํ•ด์•ผ ํ•ด. 2025๋…„ ๊ธฐ์ค€์œผ๋กœ ๊ฐ€์žฅ ์ตœ์‹ ์˜ ๋„๊ตฌ๋“ค์„ ์‚ฌ์šฉํ•ด๋ณผ๊ฒŒ!

ํ•„์š”ํ•œ ๋„๊ตฌ๋“ค ๐Ÿงฐ

  1. C++ ์ปดํŒŒ์ผ๋Ÿฌ: GCC 13.2, Clang 18.0, MSVC 19.40 ์ค‘ ํ•˜๋‚˜
  2. IDE: Visual Studio 2025, CLion 2025.1, VS Code + C++ ํ™•์žฅ
  3. OpenGL ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ: OpenGL 5.0 ํ˜ธํ™˜ ๋“œ๋ผ์ด๋ฒ„
  4. ๋ณด์กฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ: GLFW, GLAD, GLM
  5. ๋นŒ๋“œ ์‹œ์Šคํ…œ: CMake 3.28 ์ด์ƒ

์žฌ๋Šฅ๋„ท์—์„œ๋Š” ๊ทธ๋ž˜ํ”ฝ์Šค ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ด€๋ จ ๋ฉ˜ํ† ๋ง ์„œ๋น„์Šค๋„ ์ œ๊ณตํ•˜๊ณ  ์žˆ์–ด. ํ™˜๊ฒฝ ์„ค์ •์— ์–ด๋ ค์›€์„ ๊ฒช๋Š”๋‹ค๋ฉด ์ „๋ฌธ๊ฐ€์˜ ๋„์›€์„ ๋ฐ›์•„๋ณด๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ!

Windows์—์„œ ํ™˜๊ฒฝ ์„ค์ •ํ•˜๊ธฐ

Windows์—์„œ๋Š” Visual Studio 2025 Community Edition์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ํŽธ๋ฆฌํ•ด. ๋‹ค์Œ ๋‹จ๊ณ„๋ฅผ ๋”ฐ๋ผํ•ด๋ด:

  1. Visual Studio 2025 ์„ค์น˜ (C++ ๋ฐ์Šคํฌํ†ฑ ๊ฐœ๋ฐœ ์›Œํฌ๋กœ๋“œ ์„ ํƒ)
  2. vcpkg ํŒจํ‚ค์ง€ ๋งค๋‹ˆ์ € ์„ค์น˜:
    git clone https://github.com/Microsoft/vcpkg.git
    cd vcpkg
    bootstrap-vcpkg.bat
    vcpkg integrate install
  3. ํ•„์š”ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜:
    vcpkg install glfw3:x64-windows
    vcpkg install glad:x64-windows
    vcpkg install glm:x64-windows

macOS์—์„œ ํ™˜๊ฒฝ ์„ค์ •ํ•˜๊ธฐ

macOS์—์„œ๋Š” Xcode์™€ Homebrew๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์•„:

  1. Xcode ์„ค์น˜ (App Store์—์„œ)
  2. Homebrew ์„ค์น˜:
    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
  3. ํ•„์š”ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜:
    brew install glfw
    brew install glm
    brew install cmake
  4. GLAD ์„ค์ • (์›น ์„œ๋น„์Šค ์ด์šฉ):

    https://glad.dav1d.de/ ์—์„œ OpenGL ๋ฒ„์ „ ์„ ํƒ ํ›„ ์ƒ์„ฑ๋œ ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ

Linux์—์„œ ํ™˜๊ฒฝ ์„ค์ •ํ•˜๊ธฐ

Ubuntu ๊ธฐ์ค€์œผ๋กœ ์„ค๋ช…ํ• ๊ฒŒ:

  1. ํ•„์š”ํ•œ ํŒจํ‚ค์ง€ ์„ค์น˜:
    sudo apt update
    sudo apt install build-essential
    sudo apt install libglfw3-dev
    sudo apt install libglm-dev
    sudo apt install cmake
  2. 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;
}

์ด ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฐฝ์ด ๋‚˜ํƒ€๋‚  ๊ฑฐ์•ผ:

์ฒซ ๋ฒˆ์งธ OpenGL ์ฐฝ (0,1) (1,0) (0,-1) (-1,0) (0,0)

์ฝ”๋“œ๋ฅผ ์ž์„ธํžˆ ์‚ดํŽด๋ณด์ž:

  1. GLFW ์ดˆ๊ธฐํ™”: glfwInit() ํ•จ์ˆ˜๋กœ GLFW ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ดˆ๊ธฐํ™”ํ•ด.
  2. OpenGL ๋ฒ„์ „ ์„ค์ •: glfwWindowHint() ํ•จ์ˆ˜๋กœ ์‚ฌ์šฉํ•  OpenGL ๋ฒ„์ „์„ ์ง€์ •ํ•ด.
  3. ์ฐฝ ์ƒ์„ฑ: glfwCreateWindow() ํ•จ์ˆ˜๋กœ ์‹ค์ œ ์ฐฝ์„ ์ƒ์„ฑํ•ด.
  4. OpenGL ์ปจํ…์ŠคํŠธ ์„ค์ •: glfwMakeContextCurrent() ํ•จ์ˆ˜๋กœ ํ˜„์žฌ ์Šค๋ ˆ๋“œ์˜ ์ปจํ…์ŠคํŠธ๋ฅผ ์„ค์ •ํ•ด.
  5. GLAD ์ดˆ๊ธฐํ™”: OpenGL ํ•จ์ˆ˜ ํฌ์ธํ„ฐ๋ฅผ ๋กœ๋“œํ•ด.
  6. ๋ Œ๋”๋ง ๋ฃจํ”„: ์ฐฝ์ด ๋‹ซํž ๋•Œ๊นŒ์ง€ ๊ณ„์†ํ•ด์„œ ๊ทธ๋ฆฌ๊ธฐ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ด.
  7. ์ •๋ฆฌ: glfwTerminate() ํ•จ์ˆ˜๋กœ GLFW ๋ฆฌ์†Œ์Šค๋ฅผ ์ •๋ฆฌํ•ด.

OpenGL์˜ ์ขŒํ‘œ๊ณ„๋Š” ์ค‘์•™์ด (0,0)์ด๊ณ , ์˜ค๋ฅธ์ชฝ ์œ„๊ฐ€ (1,1), ์™ผ์ชฝ ์•„๋ž˜๊ฐ€ (-1,-1)์ธ ์ •๊ทœํ™”๋œ ์ขŒํ‘œ๊ณ„๋ฅผ ์‚ฌ์šฉํ•ด. ์ด ์ ์„ ๊ธฐ์–ตํ•ด๋‘๋ฉด ๋‚˜์ค‘์— ๋„ํ˜•์„ ๊ทธ๋ฆด ๋•Œ ๋„์›€์ด ๋  ๊ฑฐ์•ผ!

์ฐฝ ์ƒ์„ฑ ์‹œ ์ž์ฃผ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ์™€ ํ•ด๊ฒฐ์ฑ… ๐Ÿ”ง

  1. ๋ฌธ์ œ: "GLFW ์ดˆ๊ธฐํ™” ์‹คํŒจ" ์˜ค๋ฅ˜
    ํ•ด๊ฒฐ์ฑ…: ๊ทธ๋ž˜ํ”ฝ ๋“œ๋ผ์ด๋ฒ„๊ฐ€ ์ตœ์‹  ๋ฒ„์ „์ธ์ง€ ํ™•์ธํ•˜๊ณ , ํ•„์š”ํ•œ ์‹œ์Šคํ…œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์„ค์น˜๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ด.
  2. ๋ฌธ์ œ: "GLAD ์ดˆ๊ธฐํ™” ์‹คํŒจ" ์˜ค๋ฅ˜
    ํ•ด๊ฒฐ์ฑ…: GLAD๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ OpenGL ๋ฒ„์ „์„ ๋Œ€์ƒ์œผ๋กœ ์ƒ์„ฑ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ , ๊ทธ๋ž˜ํ”ฝ ์นด๋“œ๊ฐ€ ํ•ด๋‹น ๋ฒ„์ „์„ ์ง€์›ํ•˜๋Š”์ง€ ํ™•์ธํ•ด.
  3. ๋ฌธ์ œ: ์ฐฝ์ด ์ฆ‰์‹œ ๋‹ซํž˜
    ํ•ด๊ฒฐ์ฑ…: ๋ Œ๋”๋ง ๋ฃจํ”„์— ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ , ๋””๋ฒ„๊น… ๋ฉ”์‹œ์ง€๋ฅผ ์ถ”๊ฐ€ํ•ด์„œ ์–ด๋””์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š”์ง€ ํŒŒ์•…ํ•ด.

์ด์ œ ๊ธฐ๋ณธ ์ฐฝ์„ ๋งŒ๋“ค์—ˆ์œผ๋‹ˆ, ๋‹ค์Œ ๋‹จ๊ณ„๋กœ ๋„˜์–ด๊ฐ€์„œ ์‹ค์ œ๋กœ ๋„ํ˜•์„ ๊ทธ๋ ค๋ณผ๊นŒ?

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;
}

์ด ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์‚ผ๊ฐํ˜•์ด ๊ทธ๋ ค์งˆ ๊ฑฐ์•ผ:

์ปฌ๋Ÿฌ ์‚ผ๊ฐํ˜• (-0.5,-0.5) (0.5,-0.5) (0.0,0.5)

์…ฐ์ด๋”๋ž€ ๋ฌด์—‡์ธ๊ฐ€? ๐Ÿค”

์…ฐ์ด๋”(Shader)๋Š” GPU์—์„œ ์‹คํ–‰๋˜๋Š” ์ž‘์€ ํ”„๋กœ๊ทธ๋žจ์ด์•ผ. OpenGL์—์„œ๋Š” ์ฃผ๋กœ ๋‘ ๊ฐ€์ง€ ์ข…๋ฅ˜์˜ ์…ฐ์ด๋”๋ฅผ ์‚ฌ์šฉํ•ด:

  1. ์ •์  ์…ฐ์ด๋”(Vertex Shader): ๊ฐ ์ •์ ์— ๋Œ€ํ•ด ์‹คํ–‰๋˜๋ฉฐ, ์ฃผ๋กœ ์ •์ ์˜ ์œ„์น˜ ๋ณ€ํ™˜์„ ๋‹ด๋‹นํ•ด.
    void main() {
        gl_Position = vec4(aPos, 1.0); // ์ •์  ์œ„์น˜ ์„ค์ •
        vertexColor = aColor;          // ์ƒ‰์ƒ ์ •๋ณด ์ „๋‹ฌ
    }
  2. ํ”„๋ž˜๊ทธ๋จผํŠธ ์…ฐ์ด๋”(Fragment Shader): ๊ฐ ํ”ฝ์…€(ํ”„๋ž˜๊ทธ๋จผํŠธ)์— ๋Œ€ํ•ด ์‹คํ–‰๋˜๋ฉฐ, ํ”ฝ์…€์˜ ์ตœ์ข… ์ƒ‰์ƒ์„ ๊ฒฐ์ •ํ•ด.
    void main() {
        FragColor = vec4(vertexColor, 1.0); // RGBA ์ƒ‰์ƒ ์ถœ๋ ฅ
    }

์…ฐ์ด๋”๋Š” GLSL(OpenGL Shading Language)์ด๋ผ๋Š” C์™€ ์œ ์‚ฌํ•œ ์–ธ์–ด๋กœ ์ž‘์„ฑ๋ผ. 2025๋…„ ํ˜„์žฌ๋Š” GLSL 4.6์ด ๊ฐ€์žฅ ๋„๋ฆฌ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์–ด!

์ •์  ๋ฒ„ํผ ๊ฐ์ฒด(VBO)์™€ ์ •์  ๋ฐฐ์—ด ๊ฐ์ฒด(VAO)

OpenGL์—์„œ ๊ทธ๋ž˜ํ”ฝ์Šค ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ์ค‘์š”ํ•œ ๊ฐœ๋…์ด์•ผ:

  1. ์ •์  ๋ฒ„ํผ ๊ฐ์ฒด(VBO): GPU ๋ฉ”๋ชจ๋ฆฌ์— ์ •์  ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” ๋ฒ„ํผ์•ผ. CPU์—์„œ GPU๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ์ „์†กํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค˜.
  2. ์ •์  ๋ฐฐ์—ด ๊ฐ์ฒด(VAO): ์ •์  ์†์„ฑ ํฌ์ธํ„ฐ์™€ ๊ด€๋ จ VBO๋ฅผ ์ €์žฅํ•˜๋Š” ๊ฐ์ฒด์•ผ. ์—ฌ๋Ÿฌ ์„ค์ •์„ ํ•œ ๋ฒˆ์— ์ €์žฅํ•˜๊ณ  ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์žˆ์–ด์„œ ์ฝ”๋“œ๋ฅผ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์ค˜.
CPU ๋ฉ”๋ชจ๋ฆฌ ์ •์  ๋ฐ์ดํ„ฐ ์œ„์น˜, ์ƒ‰์ƒ, ํ…์Šค์ฒ˜ ์ขŒํ‘œ ๋“ฑ GPU ๋ฉ”๋ชจ๋ฆฌ VBO ์ •์  ๋ฐ์ดํ„ฐ ์ €์žฅ VAO (์ •์  ์†์„ฑ ํฌ์ธํ„ฐ) glBufferData()

์‚ฌ๊ฐํ˜• ๊ทธ๋ฆฌ๊ธฐ (์ธ๋ฑ์Šค ๋ฒ„ํผ ์‚ฌ์šฉ)

์ด๋ฒˆ์—๋Š” ์ธ๋ฑ์Šค ๋ฒ„ํผ ๊ฐ์ฒด(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)์€ ์˜ค๋ฅธ์ชฝ ์œ„๋ฅผ ๋‚˜ํƒ€๋‚ด.

(0,0) (1,0) (0,1) (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 ์กฐ๋ช… ๋ชจ๋ธ์ด ์ ์šฉ๋œ ํ๋ธŒ ๋ฐ˜์‚ฌ๊ด‘

Phong ์กฐ๋ช… ๋ชจ๋ธ์˜ ๊ตฌ์„ฑ ์š”์†Œ ๐Ÿ’ก

  1. ์ฃผ๋ณ€๊ด‘(Ambient): ์žฅ๋ฉด ์ „์ฒด์— ๊ท ์ผํ•˜๊ฒŒ ์ ์šฉ๋˜๋Š” ๊ธฐ๋ณธ ์กฐ๋ช…. ์™„์ „ํ•œ ์–ด๋‘ ์„ ๋ฐฉ์ง€ํ•ด.
  2. ํ™•์‚ฐ๊ด‘(Diffuse): ๋น›์ด ๋ฌผ์ฒด ํ‘œ๋ฉด์— ๋ถ€๋”ชํ˜€ ๋ชจ๋“  ๋ฐฉํ–ฅ์œผ๋กœ ๊ณ ๋ฅด๊ฒŒ ๋ฐ˜์‚ฌ๋˜๋Š” ํšจ๊ณผ. ๋ฌผ์ฒด์˜ ํ˜•ํƒœ๋ฅผ ๋“œ๋Ÿฌ๋‚ด๋Š” ์ฃผ์š” ์š”์†Œ์•ผ.
  3. ๋ฐ˜์‚ฌ๊ด‘(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 ๋ชจ๋ธ ํŒŒ์ผ ํ˜•์‹์ด ์žˆ๋Š”๋ฐ, ๊ฐ๊ฐ ์žฅ๋‹จ์ ์ด ์žˆ์–ด:

  1. OBJ: ๊ฐ„๋‹จํ•˜๊ณ  ๋„๋ฆฌ ์ง€์›๋˜๋Š” ํ˜•์‹. ํ…์Šค์ฒ˜์™€ ๊ธฐ๋ณธ ์žฌ์งˆ ์ •๋ณด๋ฅผ ํฌํ•จํ•  ์ˆ˜ ์žˆ์–ด.
  2. FBX: Autodesk์—์„œ ๊ฐœ๋ฐœํ•œ ํ˜•์‹์œผ๋กœ, ์• ๋‹ˆ๋ฉ”์ด์…˜๊ณผ ๋ณต์žกํ•œ ์žฌ์งˆ ์ •๋ณด๋ฅผ ํฌํ•จํ•  ์ˆ˜ ์žˆ์–ด.
  3. COLLADA (DAE): XML ๊ธฐ๋ฐ˜ ํ˜•์‹์œผ๋กœ, ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จํ•  ์ˆ˜ ์žˆ์–ด.
  4. 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>

์นด๋ฉ”๋ผ ์‹œ์Šคํ…œ์˜ ์ฃผ์š” ๊ฐœ๋… ๐Ÿ“ธ

  1. ์œ„์น˜(Position): 3D ๊ณต๊ฐ„์—์„œ ์นด๋ฉ”๋ผ์˜ ์œ„์น˜๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฒกํ„ฐ
  2. ๋ฐฉํ–ฅ(Direction): ์นด๋ฉ”๋ผ๊ฐ€ ๋ฐ”๋ผ๋ณด๋Š” ๋ฐฉํ–ฅ์„ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฒกํ„ฐ
  3. ์˜ค์ผ๋Ÿฌ ๊ฐ(Euler Angles): ์นด๋ฉ”๋ผ์˜ ํšŒ์ „์„ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฐ๋„
    • Yaw(์š”): ์ขŒ์šฐ ํšŒ์ „ (์ˆ˜ํ‰ ํšŒ์ „)
    • Pitch(ํ”ผ์น˜): ์ƒํ•˜ ํšŒ์ „ (์ˆ˜์ง ํšŒ์ „)
    • Roll(๋กค): ์นด๋ฉ”๋ผ ์ถ•์„ ์ค‘์‹ฌ์œผ๋กœ ํ•œ ํšŒ์ „ (์ด ์˜ˆ์ œ์—์„œ๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ)
  4. ๋ทฐ ํ–‰๋ ฌ(View Matrix): ์›”๋“œ ๊ณต๊ฐ„์˜ ์ขŒํ‘œ๋ฅผ ์นด๋ฉ”๋ผ ๊ณต๊ฐ„์˜ ์ขŒํ‘œ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ํ–‰๋ ฌ
X Y Z Front Up Right Pitch Yaw

FPS ์นด๋ฉ”๋ผ์™€ ๊ถค๋„ ์นด๋ฉ”๋ผ

3D ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ฃผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๋‘ ๊ฐ€์ง€ ์นด๋ฉ”๋ผ ์œ ํ˜•์ด ์žˆ์–ด:

  1. FPS ์นด๋ฉ”๋ผ(First-Person Camera): 1์ธ์นญ ์‹œ์ ์œผ๋กœ, ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ์ง์ ‘ ์„ธ๊ณ„๋ฅผ ํƒ์ƒ‰ํ•˜๋Š” ๋Š๋‚Œ์„ ์ค˜. ์œ„์—์„œ ๊ตฌํ˜„ํ•œ ์นด๋ฉ”๋ผ๊ฐ€ ์ด ์œ ํ˜•์— ํ•ด๋‹นํ•ด.
  2. ๊ถค๋„ ์นด๋ฉ”๋ผ(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);
// ์žฅ๋ฉด ๋ Œ๋”๋ง

์„€๋„์šฐ ๋งคํ•‘์˜ ์›๋ฆฌ ๐Ÿ”

  1. ๊ด‘์› ์‹œ์ ์—์„œ ์žฅ๋ฉด์„ ๋ Œ๋”๋งํ•˜๊ณ  ๊นŠ์ด ์ •๋ณด๋ฅผ ํ…์Šค์ฒ˜์— ์ €์žฅํ•ด.
  2. ์นด๋ฉ”๋ผ ์‹œ์ ์—์„œ ์žฅ๋ฉด์„ ๋ Œ๋”๋งํ•  ๋•Œ, ๊ฐ ํ”ฝ์…€์˜ ์œ„์น˜๋ฅผ ๊ด‘์› ์‹œ์ ์˜ ์ขŒํ‘œ๋กœ ๋ณ€ํ™˜ํ•ด.
  3. ๋ณ€ํ™˜๋œ ์ขŒํ‘œ์˜ ๊นŠ์ด ๊ฐ’๊ณผ ์ €์žฅ๋œ ๊นŠ์ด ๋งต์˜ ๊ฐ’์„ ๋น„๊ตํ•ด.
  4. ์ €์žฅ๋œ ๊นŠ์ด ๊ฐ’๋ณด๋‹ค ํ˜„์žฌ ๊นŠ์ด ๊ฐ’์ด ํฌ๋ฉด ํ•ด๋‹น ํ”ฝ์…€์€ ๊ทธ๋ฆผ์ž ์•ˆ์— ์žˆ๋Š” ๊ฒƒ์œผ๋กœ ํŒ๋‹จํ•ด.
๊ด‘์› ๋ฌผ์ฒด ๊ทธ๋ฆผ์ž ๊นŠ์ด ๋งต

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์˜ ์ฃผ์š” ๊ตฌ์„ฑ ์š”์†Œ ๐Ÿ”ง

  1. ์•Œ๋ฒ ๋„(Albedo): ํ‘œ๋ฉด์˜ ๊ธฐ๋ณธ ์ƒ‰์ƒ
  2. ๊ธˆ์†์„ฑ(Metallic): ํ‘œ๋ฉด์ด ๊ธˆ์†์ธ์ง€ ๋น„๊ธˆ์†์ธ์ง€๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฐ’
  3. ๊ฑฐ์น ๊ธฐ(Roughness): ํ‘œ๋ฉด์˜ ๋ฏธ์„ธํ•œ ๊ฑฐ์น ๊ธฐ ์ •๋„
  4. ํ™˜๊ฒฝ ์ฐจํ(Ambient Occlusion): ํ‘œ๋ฉด์˜ ํ‹ˆ์ƒˆ๋‚˜ ๊ตฌ์„์—์„œ ๋น›์ด ์ฐจ๋‹จ๋˜๋Š” ์ •๋„
  5. ๋ฒ•์„  ๋งต(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์—์„œ ํšจ์œจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด:

  1. ํŒŒํ‹ฐํด ์‹œ์Šคํ…œ ์‹œ๋ฎฌ๋ ˆ์ด์…˜
  2. ๋ฌผ๋ฆฌ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ (์ฒœ, ์œ ์ฒด ๋“ฑ)
  3. ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ๋ฐ ํ•„ํ„ฐ ์ ์šฉ
  4. ์ง€ํ˜• ์ƒ์„ฑ ๋ฐ ๋ณ€ํ˜•
  5. ๋ฐ์ดํ„ฐ ์••์ถ• ๋ฐ ์•”ํ˜ธํ™”

์ด๋Ÿฌํ•œ ๊ณ ๊ธ‰ ๊ทธ๋ž˜ํ”ฝ์Šค ๊ธฐ๋ฒ•๋“ค์„ ๋งˆ์Šคํ„ฐํ•˜๋ฉด ์ •๋ง ๋ฉ‹์ง„ ์‹œ๊ฐ์  ํšจ๊ณผ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด. ํ•˜์ง€๋งŒ ์„ฑ๋Šฅ๋„ ์ค‘์š”ํ•˜๋‹ˆ, ๋‹ค์Œ์œผ๋กœ ์ตœ์ ํ™” ์ „๋žต์„ ์•Œ์•„๋ณด์ž!

9. ์„ฑ๋Šฅ ์ตœ์ ํ™” ์ „๋žต โšก

์•„๋ฌด๋ฆฌ ๋ฉ‹์ง„ ๊ทธ๋ž˜ํ”ฝ์Šค ํšจ๊ณผ๋„ ์„ฑ๋Šฅ์ด ๋‚˜์˜๋ฉด ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์ด ๋–จ์–ด์ ธ. ํŠนํžˆ ์‹ค์‹œ๊ฐ„ ๊ทธ๋ž˜ํ”ฝ์Šค ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” ์„ฑ๋Šฅ ์ตœ์ ํ™”๊ฐ€ ๋งค์šฐ ์ค‘์š”ํ•ด. 2025๋…„ ํ˜„์žฌ ๊ฐ€์žฅ ํšจ๊ณผ์ ์ธ ์ตœ์ ํ™” ์ „๋žต๋“ค์„ ์•Œ์•„๋ณด์ž!

1. ์ปฌ๋ง ๊ธฐ๋ฒ•

์ปฌ๋ง(Culling)์€ ํ™”๋ฉด์— ๋ณด์ด์ง€ ์•Š๋Š” ๊ฐ์ฒด๋‚˜ ๋ถ€๋ถ„์„ ๋ Œ๋”๋งํ•˜์ง€ ์•Š๋Š” ๊ธฐ๋ฒ•์ด์•ผ. ๋‹ค์–‘ํ•œ ์ปฌ๋ง ๊ธฐ๋ฒ•์ด ์žˆ์–ด:

  1. ํ›„๋ฉด ์ปฌ๋ง(Backface Culling): ์นด๋ฉ”๋ผ๋ฅผ ํ–ฅํ•˜์ง€ ์•Š๋Š” ํด๋ฆฌ๊ณค์„ ๊ทธ๋ฆฌ์ง€ ์•Š๋Š” ๊ธฐ๋ฒ•. OpenGL์—์„œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์–ด.
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);
    glFrontFace(GL_CCW);
  2. ์‹œ์•ผ ์ ˆ๋‘์ฒด ์ปฌ๋ง(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;
    }
  3. ํ์ƒ‰ ์ปฌ๋ง(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);
}
์นด๋ฉ”๋ผ ๊ณ ํ•ด์ƒ๋„ 1000 ํด๋ฆฌ๊ณค ์ค‘๊ฐ„ ํ•ด์ƒ๋„ 500 ํด๋ฆฌ๊ณค ์ €ํ•ด์ƒ๋„ 100 ํด๋ฆฌ๊ณค 10m 50m 100m

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์—์„œ ์‹คํ–‰๋˜๋ฏ€๋กœ ์ตœ์ ํ™”๊ฐ€ ๋งค์šฐ ์ค‘์š”ํ•ด. ๋‹ค์Œ์€ ์…ฐ์ด๋” ์ตœ์ ํ™”๋ฅผ ์œ„ํ•œ ๋ช‡ ๊ฐ€์ง€ ํŒ์ด์•ผ:

  1. ๊ณ„์‚ฐ ๊ฐ„์†Œํ™”: ๋ณต์žกํ•œ ์ˆ˜ํ•™ ์—ฐ์‚ฐ์„ ๋ฏธ๋ฆฌ ๊ณ„์‚ฐํ•˜๊ฑฐ๋‚˜ ๊ทผ์‚ฌ๊ฐ’ ์‚ฌ์šฉํ•˜๊ธฐ
    // ๋น„ํšจ์œจ์ ์ธ ์ฝ”๋“œ
    float result = pow(x, 5.0);
    
    // ์ตœ์ ํ™”๋œ ์ฝ”๋“œ
    float result = x * x * x * x * x;
  2. ๋ถ„๊ธฐ๋ฌธ ์ตœ์†Œํ™”: GPU๋Š” ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋ฏ€๋กœ ๋ถ„๊ธฐ๋ฌธ(if-else)์ด ์„ฑ๋Šฅ์„ ํฌ๊ฒŒ ์ €ํ•˜์‹œํ‚ฌ ์ˆ˜ ์žˆ์–ด
    // ๋น„ํšจ์œจ์ ์ธ ์ฝ”๋“œ
    if (intensity > 0.5)
        color = vec3(1.0);
    else
        color = vec3(0.0);
    
    // ์ตœ์ ํ™”๋œ ์ฝ”๋“œ
    color = vec3(step(0.5, intensity));
  3. ํ…์Šค์ฒ˜ ์ƒ˜ํ”Œ๋ง ์ตœ์†Œํ™”: ํ…์Šค์ฒ˜ ์ƒ˜ํ”Œ๋ง์€ ๋น„์šฉ์ด ํฐ ์—ฐ์‚ฐ์ด๋ฏ€๋กœ ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋งŒ ์‚ฌ์šฉํ•˜๊ธฐ
  4. ์ •๋ฐ€๋„ ์กฐ์ •: ํ•„์š”ํ•œ ์ •๋ฐ€๋„๋งŒ ์‚ฌ์šฉํ•˜๊ธฐ
    // ๊ณ ์ •๋ฐ€๋„ (๊ธฐ๋ณธ๊ฐ’)
    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++๋ฅผ ํ™œ์šฉํ•œ ๊ทธ๋ž˜ํ”ฝ์Šค ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํŠœํ† ๋ฆฌ์–ผ์„ ๋ชจ๋‘ ๋งˆ์ณค์–ด! ๊ธฐ๋ณธ์ ์ธ ์ฐฝ ์ƒ์„ฑ๋ถ€ํ„ฐ ๊ณ ๊ธ‰ ๊ทธ๋ž˜ํ”ฝ์Šค ๊ธฐ๋ฒ•, ์„ฑ๋Šฅ ์ตœ์ ํ™”, ๊ทธ๋ฆฌ๊ณ  ๋ฏธ๋‹ˆ ๊ฒŒ์ž„ ์—”์ง„ ๊ฐœ๋ฐœ๊นŒ์ง€ ๋‹ค์–‘ํ•œ ์ฃผ์ œ๋ฅผ ๋‹ค๋ค˜์ง€.

์ด ํŠœํ† ๋ฆฌ์–ผ์—์„œ ๋ฐฐ์šด ๋‚ด์šฉ์„ ์ •๋ฆฌํ•ด๋ณด๋ฉด:

  1. OpenGL๊ณผ C++์˜ ๊ธฐ๋ณธ ๊ฐœ๋… ์ดํ•ด
  2. ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์„ค์ • ๋ฐฉ๋ฒ•
  3. ๊ธฐ๋ณธ ๋„ํ˜• ๊ทธ๋ฆฌ๊ธฐ์™€ ์…ฐ์ด๋” ํ”„๋กœ๊ทธ๋ž˜๋ฐ
  4. ํ…์Šค์ฒ˜์™€ ์กฐ๋ช… ํšจ๊ณผ ์ ์šฉํ•˜๊ธฐ
  5. 3D ๋ชจ๋ธ ๋กœ๋”ฉ๊ณผ ๋ Œ๋”๋ง
  6. ์นด๋ฉ”๋ผ ์‹œ์Šคํ…œ๊ณผ ์‚ฌ์šฉ์ž ์ƒํ˜ธ์ž‘์šฉ
  7. ๊ณ ๊ธ‰ ๊ทธ๋ž˜ํ”ฝ์Šค ๊ธฐ๋ฒ• (PBR, ๊ทธ๋ฆผ์ž ๋งคํ•‘ ๋“ฑ)
  8. ์„ฑ๋Šฅ ์ตœ์ ํ™” ์ „๋žต
  9. ๋ฏธ๋‹ˆ ๊ฒŒ์ž„ ์—”์ง„ ์„ค๊ณ„ ๋ฐ ๊ตฌํ˜„

๊ทธ๋ž˜ํ”ฝ์Šค ํ”„๋กœ๊ทธ๋ž˜๋ฐ์€ ๋Š์ž„์—†์ด ๋ฐœ์ „ํ•˜๋Š” ๋ถ„์•ผ์•ผ. 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 ๊ทธ๋ž˜ํ”ฝ์Šค ๋ Œ๋”๋ง API ํ•˜๋“œ์›จ์–ด ๊ฐ€์† ์…ฐ์ด๋” ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํฌ๋กœ์Šค ํ”Œ๋žซํผ C++ ๊ฐ์ฒด์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฉ”๋ชจ๋ฆฌ ์ง์ ‘ ๊ด€๋ฆฌ ๊ณ ์„ฑ๋Šฅ ์‹คํ–‰ ํ…œํ”Œ๋ฆฟ ๋ฉ”ํƒ€ํ”„๋กœ๊ทธ๋ž˜๋ฐ +

OpenGL์€ ๊ทธ๋ž˜ํ”ฝ์Šค ํŒŒ์ดํ”„๋ผ์ธ์ด๋ผ๋Š” ๊ฐœ๋…์„ ์ค‘์‹ฌ์œผ๋กœ ๋™์ž‘ํ•ด. ์ด ํŒŒ์ดํ”„๋ผ์ธ์€ 3D ๋ชจ๋ธ ๋ฐ์ดํ„ฐ๊ฐ€ 2D ํ™”๋ฉด์— ํ‘œ์‹œ๋˜๊ธฐ๊นŒ์ง€์˜ ์—ฌ๋Ÿฌ ๋‹จ๊ณ„๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ์–ด. ๊ธฐ๋ณธ์ ์ธ ํŒŒ์ดํ”„๋ผ์ธ ๋‹จ๊ณ„๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์•„:

  1. ์ •์  ์ฒ˜๋ฆฌ(Vertex Processing): 3D ๊ณต๊ฐ„์˜ ์ •์  ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌ
  2. ํ…Œ์…€๋ ˆ์ด์…˜(Tessellation): ํ‘œ๋ฉด์„ ๋” ์ž‘์€ ๊ธฐํ•˜ํ•™์  ์š”์†Œ๋กœ ๋ถ„ํ• 
  3. ์ง€์˜ค๋ฉ”ํŠธ๋ฆฌ ์ฒ˜๋ฆฌ(Geometry Processing): ๊ธฐํ•˜ํ•™์  ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€ ์ฒ˜๋ฆฌ
  4. ๋ž˜์Šคํ„ฐํ™”(Rasterization): ๋ฒกํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ํ”ฝ์…€๋กœ ๋ณ€ํ™˜
  5. ํ”„๋ž˜๊ทธ๋จผํŠธ ์ฒ˜๋ฆฌ(Fragment Processing): ํ”ฝ์…€๋ณ„ ์ƒ‰์ƒ ๋ฐ ํšจ๊ณผ ์ ์šฉ
  6. ์ถœ๋ ฅ ๋ณ‘ํ•ฉ(Output Merger): ์ตœ์ข… ์ด๋ฏธ์ง€ ์ƒ์„ฑ

์ด์ œ OpenGL๊ณผ C++์˜ ๊ธฐ๋ณธ ๊ฐœ๋…์„ ์ดํ•ดํ–ˆ์œผ๋‹ˆ, ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์„ ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณผ๊นŒ?

2. ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์„ค์ •ํ•˜๊ธฐ (2025๋…„ ์ตœ์‹  ๋ฒ„์ „) ๐Ÿ› ๏ธ

๊ทธ๋ž˜ํ”ฝ์Šค ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ์‹œ์ž‘ํ•˜๊ธฐ ์ „์— ๋จผ์ € ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์„ ์ œ๋Œ€๋กœ ์„ค์ •ํ•ด์•ผ ํ•ด. 2025๋…„ ๊ธฐ์ค€์œผ๋กœ ๊ฐ€์žฅ ์ตœ์‹ ์˜ ๋„๊ตฌ๋“ค์„ ์‚ฌ์šฉํ•ด๋ณผ๊ฒŒ!

ํ•„์š”ํ•œ ๋„๊ตฌ๋“ค ๐Ÿงฐ

  1. C++ ์ปดํŒŒ์ผ๋Ÿฌ: GCC 13.2, Clang 18.0, MSVC 19.40 ์ค‘ ํ•˜๋‚˜
  2. IDE: Visual Studio 2025, CLion 2025.1, VS Code + C++ ํ™•์žฅ
  3. OpenGL ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ: OpenGL 5.0 ํ˜ธํ™˜ ๋“œ๋ผ์ด๋ฒ„
  4. ๋ณด์กฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ: GLFW, GLAD, GLM
  5. ๋นŒ๋“œ ์‹œ์Šคํ…œ: CMake 3.28 ์ด์ƒ

์žฌ๋Šฅ๋„ท์—์„œ๋Š” ๊ทธ๋ž˜ํ”ฝ์Šค ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ด€๋ จ ๋ฉ˜ํ† ๋ง ์„œ๋น„์Šค๋„ ์ œ๊ณตํ•˜๊ณ  ์žˆ์–ด. ํ™˜๊ฒฝ ์„ค์ •์— ์–ด๋ ค์›€์„ ๊ฒช๋Š”๋‹ค๋ฉด ์ „๋ฌธ๊ฐ€์˜ ๋„์›€์„ ๋ฐ›์•„๋ณด๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ!

Windows์—์„œ ํ™˜๊ฒฝ ์„ค์ •ํ•˜๊ธฐ

Windows์—์„œ๋Š” Visual Studio 2025 Community Edition์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ํŽธ๋ฆฌํ•ด. ๋‹ค์Œ ๋‹จ๊ณ„๋ฅผ ๋”ฐ๋ผํ•ด๋ด:

  1. Visual Studio 2025 ์„ค์น˜ (C++ ๋ฐ์Šคํฌํ†ฑ ๊ฐœ๋ฐœ ์›Œํฌ๋กœ๋“œ ์„ ํƒ)
  2. vcpkg ํŒจํ‚ค์ง€ ๋งค๋‹ˆ์ € ์„ค์น˜:
    git clone https://github.com/Microsoft/vcpkg.git
    cd vcpkg
    bootstrap-vcpkg.bat
    vcpkg integrate install
  3. ํ•„์š”ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜:
    vcpkg install glfw3:x64-windows
    vcpkg install glad:x64-windows
    vcpkg install glm:x64-windows

macOS์—์„œ ํ™˜๊ฒฝ ์„ค์ •ํ•˜๊ธฐ

macOS์—์„œ๋Š” Xcode์™€ Homebrew๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์•„:

  1. Xcode ์„ค์น˜ (App Store์—์„œ)
  2. Homebrew ์„ค์น˜:
    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
  3. ํ•„์š”ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜:
    brew install glfw
    brew install glm
    brew install cmake
  4. GLAD ์„ค์ • (์›น ์„œ๋น„์Šค ์ด์šฉ):

    https://glad.dav1d.de/ ์—์„œ OpenGL ๋ฒ„์ „ ์„ ํƒ ํ›„ ์ƒ์„ฑ๋œ ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ

Linux์—์„œ ํ™˜๊ฒฝ ์„ค์ •ํ•˜๊ธฐ

Ubuntu ๊ธฐ์ค€์œผ๋กœ ์„ค๋ช…ํ• ๊ฒŒ:

  1. ํ•„์š”ํ•œ ํŒจํ‚ค์ง€ ์„ค์น˜:
    sudo apt update
    sudo apt install build-essential
    sudo apt install libglfw3-dev
    sudo apt install libglm-dev
    sudo apt install cmake
  2. 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;
}

์ด ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฐฝ์ด ๋‚˜ํƒ€๋‚  ๊ฑฐ์•ผ:

์ฒซ ๋ฒˆ์งธ OpenGL ์ฐฝ (0,1) (1,0) (0,-1) (-1,0) (0,0)

์ฝ”๋“œ๋ฅผ ์ž์„ธํžˆ ์‚ดํŽด๋ณด์ž:

  1. GLFW ์ดˆ๊ธฐํ™”: glfwInit() ํ•จ์ˆ˜๋กœ GLFW ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ดˆ๊ธฐํ™”ํ•ด.
  2. OpenGL ๋ฒ„์ „ ์„ค์ •: glfwWindowHint() ํ•จ์ˆ˜๋กœ ์‚ฌ์šฉํ•  OpenGL ๋ฒ„์ „์„ ์ง€์ •ํ•ด.
  3. ์ฐฝ ์ƒ์„ฑ: glfwCreateWindow() ํ•จ์ˆ˜๋กœ ์‹ค์ œ ์ฐฝ์„ ์ƒ์„ฑํ•ด.
  4. OpenGL ์ปจํ…์ŠคํŠธ ์„ค์ •: glfwMakeContextCurrent() ํ•จ์ˆ˜๋กœ ํ˜„์žฌ ์Šค๋ ˆ๋“œ์˜ ์ปจํ…์ŠคํŠธ๋ฅผ ์„ค์ •ํ•ด.
  5. GLAD ์ดˆ๊ธฐํ™”: OpenGL ํ•จ์ˆ˜ ํฌ์ธํ„ฐ๋ฅผ ๋กœ๋“œํ•ด.
  6. ๋ Œ๋”๋ง ๋ฃจํ”„: ์ฐฝ์ด ๋‹ซํž ๋•Œ๊นŒ์ง€ ๊ณ„์†ํ•ด์„œ ๊ทธ๋ฆฌ๊ธฐ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ด.
  7. ์ •๋ฆฌ: glfwTerminate() ํ•จ์ˆ˜๋กœ GLFW ๋ฆฌ์†Œ์Šค๋ฅผ ์ •๋ฆฌํ•ด.

OpenGL์˜ ์ขŒํ‘œ๊ณ„๋Š” ์ค‘์•™์ด (0,0)์ด๊ณ , ์˜ค๋ฅธ์ชฝ ์œ„๊ฐ€ (1,1), ์™ผ์ชฝ ์•„๋ž˜๊ฐ€ (-1,-1)์ธ ์ •๊ทœํ™”๋œ ์ขŒํ‘œ๊ณ„๋ฅผ ์‚ฌ์šฉํ•ด. ์ด ์ ์„ ๊ธฐ์–ตํ•ด๋‘๋ฉด ๋‚˜์ค‘์— ๋„ํ˜•์„ ๊ทธ๋ฆด ๋•Œ ๋„์›€์ด ๋  ๊ฑฐ์•ผ!

์ฐฝ ์ƒ์„ฑ ์‹œ ์ž์ฃผ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ์™€ ํ•ด๊ฒฐ์ฑ… ๐Ÿ”ง

  1. ๋ฌธ์ œ: "GLFW ์ดˆ๊ธฐํ™” ์‹คํŒจ" ์˜ค๋ฅ˜
    ํ•ด๊ฒฐ์ฑ…: ๊ทธ๋ž˜ํ”ฝ ๋“œ๋ผ์ด๋ฒ„๊ฐ€ ์ตœ์‹  ๋ฒ„์ „์ธ์ง€ ํ™•์ธํ•˜๊ณ , ํ•„์š”ํ•œ ์‹œ์Šคํ…œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์„ค์น˜๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ด.
  2. ๋ฌธ์ œ: "GLAD ์ดˆ๊ธฐํ™” ์‹คํŒจ" ์˜ค๋ฅ˜
    ํ•ด๊ฒฐ์ฑ…: GLAD๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ OpenGL ๋ฒ„์ „์„ ๋Œ€์ƒ์œผ๋กœ ์ƒ์„ฑ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ , ๊ทธ๋ž˜ํ”ฝ ์นด๋“œ๊ฐ€ ํ•ด๋‹น ๋ฒ„์ „์„ ์ง€์›ํ•˜๋Š”์ง€ ํ™•์ธํ•ด.
  3. ๋ฌธ์ œ: ์ฐฝ์ด ์ฆ‰์‹œ ๋‹ซํž˜
    ํ•ด๊ฒฐ์ฑ…: ๋ Œ๋”๋ง ๋ฃจํ”„์— ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ , ๋””๋ฒ„๊น… ๋ฉ”์‹œ์ง€๋ฅผ ์ถ”๊ฐ€ํ•ด์„œ ์–ด๋””์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š”์ง€ ํŒŒ์•…ํ•ด.

์ด์ œ ๊ธฐ๋ณธ ์ฐฝ์„ ๋งŒ๋“ค์—ˆ์œผ๋‹ˆ, ๋‹ค์Œ ๋‹จ๊ณ„๋กœ ๋„˜์–ด๊ฐ€์„œ ์‹ค์ œ๋กœ ๋„ํ˜•์„ ๊ทธ๋ ค๋ณผ๊นŒ?