1. SDL3

SDL์€ Simple Direct Media Layer์˜ ์•ฝ์ž๋กœ, ๊ฒŒ์ž„์ด๋‚˜ ๋ฉ€ํ‹ฐ๋ฏธ๋””์–ด ํ”„๋กœ๊ทธ๋žจ์„ ๋งŒ๋“ค ๋•Œ ํ•„์š”ํ•œ ์šด์˜์ฒด์ œ ์˜์กด ๊ธฐ๋Šฅ์„ ์ถ”์ƒํ™”ํ•ด์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋‹ค. SDL์ด ๋„์™€์ฃผ๋Š” ๋Œ€ํ‘œ์ ์ธ ์ผ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • ์ฐฝ ์ƒ์„ฑ

  • ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ

  • ํ‚ค๋ณด๋“œ / ๋งˆ์šฐ์Šค / ๊ฒŒ์ž„ํŒจ๋“œ ์ž…๋ ฅ

  • ์˜ค๋””์˜ค

  • ํƒ€์ด๋จธ

  • OpenGL, Vulkan, Metal ๊ฐ™์€ ๊ทธ๋ž˜ํ”ฝ์Šค API์™€ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” window ์ค€๋น„

SDL ์ž์ฒด์—๋„ ๊ฐ„๋‹จํ•œ ๋ Œ๋”๋ง ๊ธฐ๋Šฅ์ด ํฌํ•จ๋˜์–ด ์žˆ๊ธฐ๋Š” ํ•˜์ง€๋งŒ, SDL์˜ ์ฃผ๋œ ์—ญํ• ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • OpenGL์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ฐฝ ์ƒ์„ฑ

  • OpenGL Context ์ƒ์„ฑ

  • ์ด๋ฒคํŠธ ๋ฃจํ”„ ์ œ๊ณต

  • ๋งค ํ”„๋ ˆ์ž„ back buffer๋ฅผ front buffer๋กœ swap

1.1. SDL3์˜ ์ฃผ์š” ํ•จ์ˆ˜

  • SDL_Init

    SDL์˜ ํŠน์ • subsystem์„ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค. ์ฐฝ๊ณผ OpenGL Context๋ฅผ ๋งŒ๋“ค๋ ค๋ฉด video subsystem์ด ํ•„์š”ํ•˜๋‹ค.

    if (!SDL_Init(SDL_INIT_VIDEO))
    {
        SDL_Log("SDL_Init failed: %s", SDL_GetError());
        return -1;
    }
    

    SDL3์˜ ๋งŽ์€ ํ•จ์ˆ˜๋“ค์€ ์‹คํŒจ ์‹œ false๋‚˜ nullptr์„ ๋ฐ˜ํ™˜ํ•˜๊ณ , ์ž์„ธํ•œ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋Š” SDL_GetError()๋กœ ํ™•์ธํ•œ๋‹ค.

  • SDL_GL_SetAttribute

    OpenGL Context๋ฅผ ๋งŒ๋“ค๊ธฐ ์ „์— ์›ํ•˜๋Š” ์†์„ฑ์„ ์š”์ฒญํ•˜๋Š” ํ•จ์ˆ˜. OpenGL 4.6 Core Profile์„ ์š”์ฒญํ•˜๋ ค๋ฉด ์ด๋Ÿฐ ์‹์˜ ์„ค์ •์ด ํ•„์š”ํ•˜๋‹ค.

    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
    

    OpenGL Context๋Š” ์ƒ์„ฑ ์‹œ์ ์— ์†์„ฑ์ด ๊ฒฐ์ •๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ด ํ•จ์ˆ˜๋Š” ๋ฐ˜๋“œ์‹œ ์ฐฝ๊ณผ context๋ฅผ ๋งŒ๋“ค๊ธฐ ์ „์— ํ˜ธ์ถœํ•ด์•ผ ํ•œ๋‹ค. ํ•˜๋‚˜ ์ฃผ์˜ํ•  ์ ์€ ์ด ํ•จ์ˆ˜๋Š” ๊ทธ์ € ์š”์ฒญ์ด๋ผ๋Š” ์ ์œผ๋กœ, ๋“œ๋ผ์ด๋ฒ„๋‚˜ ํ”Œ๋žซํผ์ด ์ •ํ™•ํžˆ ๊ฐ™์€ ์†์„ฑ์„ ๋ณด์žฅํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค.

    context ์ƒ์„ฑ ํ›„ SDL_GL_GetAttribute๋กœ ์‹ค์ œ ์ƒ์„ฑ๋œ ๊ฐ’์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

  • SDL_CreateWindow

    ์šด์˜์ฒด์ œ ์ฐฝ์„ ๋งŒ๋“œ๋Š” ํ•จ์ˆ˜. OpenGL์„ ์‚ฌ์šฉํ•  ์ฐฝ์ด๋ผ๋ฉด ๋ฐ˜๋“œ์‹œ SDL_WINDOW_OPENGL flag๊ฐ€ ํ•„์š”ํ•˜๋‹ค. SDL_WINDOW_OPENGL์ด ๋น ์ง€๋ฉด SDL_GL_CreateContext๊ฐ€ ์‹คํŒจํ•  ์ˆ˜ ์žˆ๋‹ค.

    SDL_Window* window = SDL_CreateWindow(
        "Cookie Engine",
        1280,
        720,
        SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE
    );
    
    if (!window)
    {
        SDL_Log("SDL_CreateWindow failed: %s", SDL_GetError());
        return -1;
    }
    

    SDL_WINDOW_RESIZABLE์€ ์ฐฝ ํฌ๊ธฐ ๋ณ€๊ฒฝ์„ ํ—ˆ์šฉํ•œ๋‹ค. ๊ฒŒ์ž„ ์—”์ง„ ์—๋””ํ„ฐ์—์„œ๋Š” ์ฐฝ ํฌ๊ธฐ ๋ณ€๊ฒฝ์— ๋Œ€์‘ํ•ด์•ผ ํ•˜๋ฏ€๋กœ ์ดˆ๋ฐ˜๋ถ€ํ„ฐ ์ผœ๋‘๋Š” ํŽธ์ด ์ข‹๋‹ค.

  • SDL_GL_CreateContext

    SDL window์— ์—ฐ๊ฒฐ๋œ OpenGL Context๋ฅผ ๋งŒ๋“ ๋‹ค. OpenGL Context๋Š” ํ˜„์žฌ OpenGL ํ˜ธ์ถœ๋“ค์ด ์–ด๋–ค GPU ์ƒํƒœ์™€ ๋ฆฌ์†Œ์Šค ์ง‘ํ•ฉ์— ๋Œ€ํ•ด ์‹คํ–‰๋˜๋Š”๊ฐ€๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ์‹คํ–‰ ๋ฌธ๋งฅ์ด๋‹ค.

    SDL_GLContext context = SDL_GL_CreateContext(window);
    if (!context)
    {
        SDL_Log("SDL_GL_CreateContext failed: %s", SDL_GetError());
        return -1;
    }
    

    OpenGL ํ•จ์ˆ˜๋Š” context ์—†์ด ์ œ๋Œ€๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค. GLAD๋ฅผ ๋กœ๋”ฉํ•˜๋Š” ๊ฒƒ๋„ context ์ƒ์„ฑ ์ดํ›„์— ํ•ด์•ผ ํ•œ๋‹ค.

  • SDL_GL_MakeCurrent

    OpenGL Context๋Š” ์ƒ์„ฑ๋งŒ ํ–ˆ๋‹ค๊ณ  ๋์ด ์•„๋‹ˆ๊ณ , ํ˜„์žฌ ์Šค๋ ˆ๋“œ์—์„œ ์–ด๋–ค context๋ฅผ ์‚ฌ์šฉํ• ์ง€ ์ง€์ •ํ•ด์•ผ ํ•œ๋‹ค. main ์Šค๋ ˆ๋“œ ํ•˜๋‚˜๋งŒ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ๋‚ด์šฉ์ด ํฌ๊ฒŒ ๋ณต์žกํ•˜์ง€ ์•Š๋‹ค. ํ•˜์ง€๋งŒ ๋‚˜์ค‘์— ๋ Œ๋” ์Šค๋ ˆ๋“œ, ๋ฆฌ์†Œ์Šค ๋กœ๋”ฉ ์Šค๋ ˆ๋“œ, shared context ๊ฐ™์€ ๊ฐœ๋…์œผ๋กœ ๋‚˜๋ˆ„์–ด์ง€๋ฉด ์ด ํ•จ์ˆ˜์˜ ์˜๋ฏธ๊ฐ€ ์ค‘์š”ํ•ด์ง„๋‹ค.

    if (!SDL_GL_MakeCurrent(window, context))
    {
        SDL_Log("SDL_GL_MakeCurrent failed: %s", SDL_GetError());
        return -1;
    }
    
  • SDL_GL_SetSwapInterval

    VSync ์„ค์ •์— ์‚ฌ์šฉํ•œ๋‹ค. ๋งค๊ฐœ ๋ณ€์ˆ˜๋กœ 1์„ ๋„˜๊ฒจ์ฃผ๋ฉด ๋ชจ๋‹ˆํ„ฐ์˜ refresh์— ๋งž์ถ”์–ด swapํ•œ๋‹ค๋Š” ๋œป์ด๋‹ค.

    SDL_GL_SetSwapInterval(1);
    

    ๋Œ€๋žต์ ์œผ๋กœ ํ™”๋ฉด ์ฐข์–ด์ง์„ ์ค„์ด๋Š” ๋Œ€์‹  FPS๊ฐ€ ๋ชจ๋‹ˆํ„ฐ ์ฃผ์‚ฌ์œจ์— ๋ฌถ์ผ ์ˆ˜ ์žˆ๋‹ค.

  • SDL_PollEvent

    ์ด๋ฒคํŠธ ํ์—์„œ ์ด๋ฒคํŠธ๋ฅผ ํ•˜๋‚˜์”ฉ ๊บผ๋‚ธ๋‹ค. ์ด๋ฒคํŠธ๋Š” ์šด์˜์ฒด์ œ๋‚˜ SDL์ด ํ”„๋กœ๊ทธ๋žจ์— ์ „๋‹ฌํ•˜๋Š” ์ž…๋ ฅ / ์ƒํƒœ ๋ณ€ํ™” ๋ฉ”์‹œ์ง€๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

    SDL_Event event;
    while (SDL_PollEvent(&event))
    {
        if (event.type == SDL_EVENT_QUIT)
        {
            running = false;
        }
    }
    

    ๊ฒŒ์ž„ ์—”์ง„์—์„œ๋Š” ์ด ์ด๋ฒคํŠธ๋“ค์„ Input System์œผ๋กœ ๋ณ€ํ™˜ํ•˜๊ฑฐ๋‚˜, Editor UI๋กœ ์ „๋‹ฌํ•˜๊ฑฐ๋‚˜, Viewport Client์— dispatchํ•˜๋Š” ๊ตฌ์กฐ๋กœ ๋ฐœ์ „์‹œํ‚จ๋‹ค.

  • SDL_GL_SwapWindow

    OpenGL์€ ๋ณดํ†ต double buffering์„ ์‚ฌ์šฉํ•œ๋‹ค. ํ™”๋ฉด์— ๋ณด์ด๋Š” front buffer์™€, ๋‹ค์Œ ํ”„๋ ˆ์ž„์„ ๊ทธ๋ฆฌ๋Š” back buffer๊ฐ€ ๋”ฐ๋กœ ์žˆ๋Š”๋ฐ, ์šฐ๋ฆฌ๋Š” back buffer์— ๋ Œ๋”๋งํ•˜๊ณ , ํ”„๋ ˆ์ž„ ๋งˆ์ง€๋ง‰์— swapํ•œ๋‹ค.

    SDL_GL_SwapWindow(window);
    

    ์ด ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ๋ฐฉ๊ธˆ OpenGL๋กœ ๊ทธ๋ฆฐ ๊ฒฐ๊ณผ๊ฐ€ ์‹ค์ œ ์ฐฝ์— ํ‘œ์‹œ๋œ๋‹ค.

  • SDL_DestroyWindow, SDL_GL_DestroyContext, SDL_Quit

    ํ”„๋กœ๊ทธ๋žจ ์ข…๋ฃŒ ์‹œ์—๋Š” ์ƒ์„ฑํ•œ ๋ฆฌ์†Œ์Šค๋ฅผ ์ •๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.

    SDL_GL_DestroyContext(context);
    SDL_DestroyWindow(window);
    SDL_Quit();
    

    ์ด ๋ถ€๋ถ„์€ C++ RAII ๊ตฌ์กฐ๋กœ ๊ฐ์‹ธ๋ฉด ๋‚˜์ค‘์— ์ž๋™ํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค.

2. OpenGL

OpenGL์€ GPU์—๊ฒŒ ๋ Œ๋”๋ง ์ž‘์—…์„ ์ง€์‹œํ•˜๋Š” ๊ทธ๋ž˜ํ”ฝ์Šค API๋‹ค. OpenGL ์ž์ฒด๋Š” ์—”์ง„๋„ ์•„๋‹ˆ๊ณ , ์ˆ˜ํ•™ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋„ ์•„๋‹ˆ๋‹ค.

OpenGL์ด ํ•ด์ฃผ๋Š” ์ผ์€ ๋” ๋‚ฎ์€ ์ˆ˜์ค€์ด๋‹ค.

  • GPU buffer ์ƒ์„ฑ

  • GPU texture ์ƒ์„ฑ

  • shader program ์ƒ์„ฑ

  • vertex ๋ฐ์ดํ„ฐ๋ฅผ GPU pipeline์— ์ž…๋ ฅ

  • draw call ์‹คํ–‰

  • framebuffer์— ํ”ฝ์…€ ๊ฒฐ๊ณผ ๊ธฐ๋ก

์ฆ‰, OpenGL์€ ์—”์ง„์˜ Renderer Backend์— ํ•ด๋‹นํ•œ๋‹ค.

2.1. ๊ธฐ๋ณธ ํ๋ฆ„

OpenGL์ด ํ•œ ํ”„๋ ˆ์ž„์„ ๊ทธ๋ฆฌ๋Š” ๊ธฐ๋ณธ ํ๋ฆ„์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

1. viewport ์„ค์ •

2. color/depth buffer clear

3. shader program ์‚ฌ์šฉ

4. VAO bind

5. draw call ํ˜ธ์ถœ

6. SDL_GL_SwapWindow๋กœ ํ™”๋ฉด ํ‘œ์‹œ

์ฝ”๋“œ๋กœ ๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ˜•ํƒœ๊ฐ€ ๋œ๋‹ค

glViewport(0, 0, width, height);

glClearColor(0.1f, 0.15f, 0.25f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glUseProgram(shaderProgram);
glBindVertexArray(sphereVao);
glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, nullptr);

SDL_GL_SwapWindow(window);

2.2. OpenGL ๋ Œ๋”๋ง ํŒŒ์ดํ”„๋ผ์ธ

OpenGL์—์„œ sphere ํ•˜๋‚˜๊ฐ€ ๊ทธ๋ ค์ง€๋Š” ๊ณผ์ •์„ ์•„์ฃผ ๋‹จ์ˆœํ™”ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

CPU ๋ฉ”๋ชจ๋ฆฌ์˜ ์ •์  ๋ฐฐ์—ด
    -> VBO๋กœ GPU์— ์—…๋กœ๋“œ
    -> EBO๋กœ ์ธ๋ฑ์Šค ์—…๋กœ๋“œ
    -> VAO์— ์ •์  ํ•ด์„ ๋ฐฉ์‹ ์ €์žฅ
    -> vertex shader ์‹คํ–‰
    -> clipping / NDC ๋ณ€ํ™˜
    -> rasterization
    -> fragment shader ์‹คํ–‰
    -> depth test
    -> color buffer์— ํ”ฝ์…€ ๊ธฐ๋ก
  • CPU๋Š” sphere๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ์ •์  ๋ชฉ๋ก๊ณผ ์‚ผ๊ฐํ˜• ๋ชฉ๋ก์„ ๋งŒ๋“ ๋‹ค

  • VBO๋Š” ์ •์  ๋ฐ์ดํ„ฐ๋ฅผ GPU์— ์ €์žฅํ•œ๋‹ค

  • EBO๋Š” ์–ด๋–ค ์ •์ ๋“ค์„ ๋ฌถ์–ด ์‚ผ๊ฐํ˜•์„ ๋งŒ๋“ค์ง€ ์ €์žฅํ•œ๋‹ค

  • VAO๋Š” VBO ์•ˆ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์–ด๋–ป๊ฒŒ ์ฝ์„์ง€ ์ €์žฅํ•œ๋‹ค

  • vertex shader๋Š” ๊ฐ ์ •์ ์˜ ์ตœ์ข… ์œ„์น˜๋ฅผ ๊ณ„์‚ฐํ•œ๋‹ค

  • rasterizer๋Š” ์‚ผ๊ฐํ˜•์„ ํ”ฝ์…€ ํ›„๋ณด๋“ค๋กœ ๋ฐ”๊พผ๋‹ค

  • fragment shader๋Š” ๊ฐ ํ”ฝ์…€์˜ ์ƒ‰์„ ๊ณ„์‚ฐํ•œ๋‹ค

  • depth test๋Š” ๋” ๊ฐ€๊นŒ์šด ํ”ฝ์…€๋งŒ ๋‚จ๊ธด๋‹ค

  • color buffer์— ์ตœ์ข… ์ƒ‰์ด ๊ธฐ๋ก๋œ๋‹ค

2.3. OpenGL Context

OpenGL Context๋Š” OpenGL ์ƒํƒœ์™€ ๋ฆฌ์†Œ์Šค๋ฅผ ๋‹ด๋Š” ์‹คํ–‰ ๋ฌธ๋งฅ์ด๋‹ค. OpenGL ํ•จ์ˆ˜๋“ค์€ ๋Œ€๋ถ€๋ถ„ ์ „์—ญ ํ•จ์ˆ˜์ฒ˜๋Ÿผ ๋ณด์ธ๋‹ค.

glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);

ํ•˜์ง€๋งŒ ์ด ํ•จ์ˆ˜๋“ค์€ ์‹ค์ œ๋กœ๋Š” ํ˜„์žฌ ์Šค๋ ˆ๋“œ์˜ current๋กœ ์ง€์ •๋œ OpenGL Context์— ๋Œ€ํ•ด ๋™์ž‘ํ•œ๋‹ค. ๊ทธ๋ž˜์„œ context ์ƒ์„ฑ๊ณผ current ์ง€์ •์ด ์ค‘์š”ํ•˜๋‹ค.

2.4. State Machine

OpenGL์„ ์ฒ˜์Œ ๋ณผ ๋•Œ ๊ฐ€์žฅ ์ž์ฃผ ํ—ท๊ฐˆ๋ฆฌ๋Š” ๋ถ€๋ถ„์ด bind ๊ธฐ๋ฐ˜ ์ƒํƒœ ๋ชจ๋ธ์ด๋‹ค. OpenGL์€ state machine์ ์ธ ์„ฑ๊ฒฉ์ด ๊ฐ•ํ•˜๋‹ค. ์–ด๋–ค ๊ฐ์ฒด๋ฅผ bindํ•˜๋ฉด, ์ดํ›„ ํ•จ์ˆ˜ ํ˜ธ์ถœ์ด ๊ทธ bind๋œ ๊ฐ์ฒด์— ๋Œ€ํ•ด ๋™์ž‘ํ•œ๋‹ค.

GLuint vbo = 0;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);

์œ„ ์ฝ”๋“œ์—์„œ glBufferData๋Š” ์ธ์ž๋กœ vbo๋ฅผ ์ง์ ‘ ๋ฐ›๋Š” ๋Œ€์‹  ํ˜„์žฌ GL_ARRAY_BUFFER์— bind๋œ buffer์— ๋ฐ์ดํ„ฐ๋ฅผ ์—…๋กœ๋“œํ•œ๋‹ค.

OpenGL 4.5 ์ด์ƒ์˜ DSA(Direct State Access)๋ฅผ ์“ฐ๋ฉด bind๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.

2.5. OpenGL Loader and GLAD

C++์—์„œ OpenGL ํ•จ์ˆ˜๋ฅผ ๋ฐ”๋กœ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์ง€๋งŒ, ์‹ค์ œ๋กœ๋Š” ๋งŽ์€ OpenGL ํ•จ์ˆ˜๊ฐ€ ๋Ÿฐํƒ€์ž„์— ๋“œ๋ผ์ด๋ฒ„์—์„œ ๋กœ๋”ฉ๋˜์–ด์•ผ ํ•œ๋‹ค. ํŠนํžˆ Windows์—์„œ๋Š” OpenGL 1.1 ์ดํ›„์— ์ถ”๊ฐ€๋œ ํ•จ์ˆ˜๋“ค์ด ๊ธฐ๋ณธ์ ์œผ๋กœ ๋…ธ์ถœ๋˜์ง€ ์•Š๋Š” ์—ญ์‚ฌ์  ๋ฌธ์ œ๊ฐ€ ์žˆ๊ณ , Linux์—์„œ๋„ ํ™•์žฅ / ๋ฒ„์ „๋ณ„ ํ•จ์ˆ˜ ํฌ์ธํ„ฐ ๋กœ๋”ฉ ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค.


์—ฌ๋‹ด์œผ๋กœ, ์ด๋Ÿฐ ๊ตฌ์กฐ๊ฐ€ ๋œ ์ด์œ ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. Windows๋Š” ์•„์ฃผ ์˜ค๋ž˜์ „์— OpenGL์„ ์‹œ์Šคํ…œ API๋กœ ๋„ฃ์—ˆ๋‹ค. ๊ทธ๋•Œ Windows์—์„œ ๊ธฐ๋ณธ ์ œ๊ณตํ•œ OpenGL ๋ฒ„์ „์ด OpenGL 1.1์ด์˜€๋‹ค.

๋ฌธ์ œ๋Š” ๊ทธ ์ดํ›„์— ๋ฐœ์ƒํ–ˆ๋Š”๋ฐ, OpenGL์€ ๊ณ„์† ๋ฐœ์ „ํ–ˆ์ง€๋งŒ Windows์˜ ๊ธฐ๋ณธ opengl32.dll์€ ํ˜„๋Œ€ OpenGL ํ•จ์ˆ˜๋“ค์„ ๊ณ„์† ์ง์ ‘ exportํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์—…๋ฐ์ดํŠธ๋˜์ง€ ์•Š์•˜๋‹ค.

๋Œ€์‹  Windows์—์„œ๋Š” ํ˜„๋Œ€ OpenGL ํ•จ์ˆ˜๋“ค์„ GPU vendor๋“ค์˜ ICD(Installable Client Driver)๋ฅผ ์ด์šฉํ•˜์—ฌ ์ œ๊ณตํ•œ๋‹ค. ์ฆ‰ ์‹ค์ œ ํ˜„๋Œ€ OpenGL ํ•จ์ˆ˜๋“ค์˜ ๊ตฌํ˜„์€ Microsoft์˜ opengl32.dll ์•ˆ์— ์žˆ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ NVIDIA / AMD / Intel ๋“œ๋ผ์ด๋ฒ„๊ฐ€ ์ œ๊ณตํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

๊ทธ๋ž˜์„œ ํ˜„๋Œ€ OpenGL ํ•จ์ˆ˜๋“ค์„ ์–ป๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋Ÿฐํƒ€์ž„์— ํ•จ์ˆ˜ ์ฃผ์†Œ๋ฅผ ๊ฐ€์ ธ์™€์•ผ ํ•œ๋‹ค. Windows์—์„œ๋Š” OpenGL context ์‹œ์Šคํ…œ์ธ WGL์—์„œ ์‚ฌ์šฉํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•จ์ˆ˜๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.

wglGetProcAddress("glCreateShader")

GLAD๋Š” ์ด ์ž‘์—…์„ ๋Œ€์‹ ํ•ด์ฃผ๋Š” loader-generator๋‹ค. GLAD 2๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ณดํ†ต ๋‹ค์Œ ํ๋ฆ„์ด ๋œ๋‹ค.

#include <glad/gl.h>

int version = gladLoadGL((GLADloadfunc) SDL_GL_GetProcAddress);
if (version == 0)
{
    SDL_Log("Failed to load OpenGL");
    return -1;
}

SDL_Log("Loaded OpenGL %d.%d",
    GLAD_VERSION_MAJOR(version),
    GLAD_VERSION_MINOR(version));

SDL_GL_CreateContext์™€ SDL_GL_MakeCurrent ์ดํ›„์— GLAD๋ฅผ ๋กœ๋”ฉํ•ด์•ผ ํ•œ๋‹ค๋Š” ์ ์„ ์œ ์˜ํ•œ๋‹ค. ์ „์ฒด์ ์ธ ํ˜ธ์ถœ ์ˆœ์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์•„์•ผ ํ•œ๋‹ค.

SDL_Init
    -> SDL_GL_SetAttribute
    -> SDL_CreateWindow
    -> SDL_GL_CreateContext
    -> SDL_GL_MakeCurrent
    -> gladLoadGL
    -> OpenGL ํ•จ์ˆ˜ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

2.6. OpenGL Profile

OpenGL 4.6์„ ์‚ฌ์šฉํ•  ๋•Œ Core Profile๊ณผ Compatibility Profile์„ ๊ตฌ๋ถ„ํ•ด์•ผ ํ•œ๋‹ค. Core Profile์€ ํ˜„๋Œ€ OpenGL ๊ธฐ๋Šฅ๋งŒ ์ œ๊ณตํ•˜๊ณ , Compatibility Profile์€ ์˜›๋‚  OpenGL ๊ธฐ๋Šฅ๊นŒ์ง€ ํฌํ•จํ•œ๋‹ค.

Core Profile์—์„œ๋Š” ๋ฐ˜๋“œ์‹œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

  • VAO

  • VBO

  • shader program

  • vertex attribute

  • draw call

2.7. ์ฃผ์š” ํ•จ์ˆ˜

  • glGetString

    OpenGL vendor, renderer, version์„ ํ™•์ธํ•œ๋‹ค.

    SDL_Log("OpenGL Vendor: %s", glGetString(GL_VENDOR));
    SDL_Log("OpenGL Renderer: %s", glGetString(GL_RENDERER));
    SDL_Log("OpenGL Version: %s", glGetString(GL_VERSION));
    
  • glViewport

    NDC๋ฅผ window pixel ์˜์—ญ์œผ๋กœ ๋ณ€ํ™˜ํ•  viewport๋ฅผ ์„ค์ •ํ•œ๋‹ค.

    glViewport(0, 0, width, height);
    
  • glClearColor, glClear

    ํ™”๋ฉด์„ ์ง€์šด๋‹ค. glClearColor๋ฅผ ํ†ตํ•ด ์–ด๋–ค ์ƒ‰์œผ๋กœ clearํ•  ์ง€ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

    glClearColor(0.1f, 0.15f, 0.25f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
  • glEnable

    OpenGL ๊ธฐ๋Šฅ์„ ์ผ ๋‹ค.

    glEnable(GL_DEPTH_TEST);
    
  • glGenVertexArrays, glBindVertexArray

    VAO๋ฅผ ๋งŒ๋“ค๊ณ  bindํ•œ๋‹ค.

    GLuint vao = 0;
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);
    
  • glGenBuffers, glBindBuffer, glBufferData

    GPU buffer๋ฅผ ๋งŒ๋“ค๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ์—…๋กœ๋“œํ•œ๋‹ค.

    GLuint vbo = 0;
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
    
  • glVertexAttribPointer, glEnableVertexAttribArray

    vertex ์ž์ฒด์˜ attribute ํ•ด์„ ๊ทœ์น™์„ ์„ค์ •ํ•œ๋‹ค.

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
    glEnableVertexAttribArray(0);
    
  • glCreateShader, glShaderSource, glCompileShader

    shader object๋ฅผ ๋งŒ๋“ค๊ณ  ์ปดํŒŒ์ผํ•œ๋‹ค.

    GLuint vs = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vs, 1, &vertexSource, nullptr);
    glCompileShader(vs);
    
  • glCreateProgram, glAttachShader, glLinkProgram, glUseProgram

    shader program์„ ๋งŒ๋“ ๋‹ค

    GLuint program = glCreateProgram();
    glAttachShader(program, vs);
    glAttachShader(program, fs);
    glLinkProgram(program);
    glUseProgram(program);
    
  • glDrawElements

    ํ˜„์žฌ VAO์™€ shader program์„ ๊ธฐ์ค€์œผ๋กœ index buffer ๊ธฐ๋ฐ˜ draw call์„ ์‹คํ–‰ํ•œ๋‹ค. ์ฐธ๊ณ ๋กœ index buffer ๋Œ€์‹  vertex buffer ๊ธฐ๋ฐ˜์œผ๋กœ draw call์„ ์‹คํ–‰ํ•  ๋•Œ๋Š” glDraw๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

    glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, nullptr);
    
  • glDelete*

    OpenGL object๋ฅผ ์‚ญ์ œํ•œ๋‹ค.

    glDeleteProgram(program);
    glDeleteShader(vs);
    glDeleteShader(fs);
    glDeleteBuffers(1, &vbo);
    glDeleteBuffers(1, &ebo);
    glDeleteVertexArrays(1, &vao);
    

    OpenGL object๋„ ๋ฆฌ์†Œ์Šค์ด๋ฏ€๋กœ ์ˆ˜๋ช… ๊ด€๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๋‹ค. ๋‚˜์ค‘์—๋Š” C++ RAII ํŒจํ„ด์„ ์ด์šฉํ•˜๋Š” wrapper๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

2.8. VAO, VBO, EBO

  • VBO(Vertex Buffer Object)

    ์ •์  ๋ฐ์ดํ„ฐ๋ฅผ GPU ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ•˜๋Š” OpenGL object๋‹ค.

    GLuint vbo = 0;
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, vertexDataSize, vertices, GL_STATIC_DRAW);
    

    ์—ฌ๊ธฐ์„œ GL_STATIC_DRAW๋Š” ๋ฐ์ดํ„ฐ ์‚ฌ์šฉ ํŒจํ„ด์— ๋Œ€ํ•œ ํžŒํŠธ๋‹ค. static draw๋Š” ์ž์ฃผ ๋ฐ”๋€Œ์ง€ ์•Š๊ณ  ๋ Œ๋”๋ง์— ์ฃผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๋ฐ์ดํ„ฐ๋ผ๋Š” ๋œป์ด๋‹ค.

  • EBO(Element Buffer Object)

    Index Buffer๋ผ๊ณ ๋„ ๋ถ€๋ฅธ๋‹ค. GL_ELEMENT_ARRAY_BUFFER์— bind๋œ buffer๊ฐ€ glDrawElements์—์„œ ์‚ฌ์šฉ๋œ๋‹ค.

    GLuint ebo = 0;
    glGenBuffers(1, &ebo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexDataSize, indices, GL_STATIC_DRAW);
    
  • VAO(Vertex Array Object)

    ์ •์  ๋ฐ์ดํ„ฐ๋ฅผ ์–ด๋–ป๊ฒŒ ํ•ด์„ํ• ์ง€๋ฅผ ์ €์žฅํ•œ๋‹ค.

    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);
    
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glVertexAttribPointer(
        0,              // shader layout location
        3,              // vec3
        GL_FLOAT,       // float
        GL_FALSE,       // normalized ์•„๋‹˜
        sizeof(Vertex), // stride
        (void*) 0        // offset
    );
    glEnableVertexAttribArray(0);
    
    • ์–ด๋–ค VBO๋ฅผ ์‚ฌ์šฉํ• ์ง€

    • position attribute๊ฐ€ ๋ช‡ ๋ฒˆ์งธ location์ธ์ง€

    • position์ด float ๋ช‡ ๊ฐœ๋กœ ๊ตฌ์„ฑ๋˜๋Š”์ง€

    • vertex ํ•˜๋‚˜์˜ stride๊ฐ€ ๋ช‡ byte์ธ์ง€

    • position์ด vertex ๊ตฌ์กฐ์ฒด ์•ˆ์—์„œ ๋ช‡ byte offset์— ์žˆ๋Š”์ง€

2.9. WSL Debian์—์„œ OpenGL์ด ์‹คํ–‰๋˜๋Š” ๊ตฌ์กฐ

WSL Debian์—์„œ OpenGL์„ ์‚ฌ์šฉํ•˜๋ฉด, ์ผ๋ฐ˜์ ์ธ Linux-native ํ™˜๊ฒฝ๊ณผ ์กฐ๊ธˆ ๋‹ค๋ฅด๊ฒŒ ๋™์ž‘ํ•œ๋‹ค. Windows 11 + WSLg ํ™˜๊ฒฝ์—์„œ๋Š” ๋Œ€๋žต ๋‹ค์Œ ๊ตฌ์กฐ๋กœ ์‹คํ–‰๋œ๋‹ค.

SDL3 Window
    -> OpenGL ํ˜ธ์ถœ
    -> Mesa libGL
    -> Mesa Gallium d3d12 backend
    -> Direct3D 12
    -> Windows GPU Driver
    -> GPU

์—ฌ๊ธฐ์„œ Mesa๋Š” OpenGL์˜ ๋Œ€ํ‘œ์ ์ธ ์˜คํ”ˆ์†Œ์Šค ๊ตฌํ˜„์ฒด๋‹ค.

OpenGL์€ ํ‘œ์ค€ API์ด๊ณ , Mesa๋Š” ๊ทธ ํ‘œ์ค€์„ ์‹ค์ œ๋กœ ๊ตฌํ˜„ํ•œ ๋Ÿฐํƒ€์ž„ / ๋“œ๋ผ์ด๋ฒ„ ์Šคํƒ์ด๋‹ค. WSL์—์„œ๋Š” Mesa๊ฐ€ ์ง์ ‘ Linux GPU ๋“œ๋ผ์ด๋ฒ„๋ฅผ ํƒ€๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, D3D12 backend๋ฅผ ํ†ตํ•ด Windows์˜ Direct3D 12 ๊ฒฝ๋กœ๋กœ GPU๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ๊ทธ๋ž˜์„œ glxinfo ๊ฒฐ๊ณผ๊ฐ€ ์ •์ƒ์ด๋ผ๋ฉด ๋ณดํ†ต ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‚˜์˜จ๋‹ค.

OpenGL vendor string: Microsoft Corporation
OpenGL renderer string: D3D12 (Intel(R) Arc(TM) Graphics)
OpenGL core profile version string: 4.6 (Core Profile) Mesa ...

๋ฐ˜๋Œ€๋กœ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‚˜์˜ค๋ฉด GPU ๋Œ€์‹  CPU๋ฅผ ์‚ฌ์šฉํ•˜๋Š” software rendering ์ƒํƒœ๋‹ค.

OpenGL renderer string: llvmpipe (...)

WSL์—์„œ ์ฒ˜์Œ ๋ณ„๋„์˜ ์„ค์ •์„ ํ•ด์ฃผ์ง€ ์•Š์œผ๋ฉด ๊ธฐ๋ณธ ์ƒํƒœ๊ฐ€ CPU software rendering์ธ ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค. ๋‹ค์Œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ D3D12 backend๋ฅผ ๊ฐ•์ œํ•  ์ˆ˜ ์žˆ๋‹ค.

GALLIUM_DRIVER=d3d12

3. CMake ์—ฐ๋™

C++ ํ”„๋กœ์ ํŠธ๋Š” ๋‹จ์ˆœํžˆ .cpp ํŒŒ์ผ๋งŒ ์ปดํŒŒ์ผํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋๋‚˜์ง€ ์•Š๊ณ , ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋‹ค์–‘ํ•œ ์š”์†Œ๋“ค์„ ํ•จ๊ป˜ ๋นŒ๋“œํ•ด์•ผ ํ•œ๋‹ค.

  • ๋‚ด ์—”์ง„ ์ฝ”๋“œ

  • SDL3

  • GLAD์—์„œ ์ƒ์„ฑํ•œ gl.c

  • ImGui

  • ์‹œ์Šคํ…œ OpenGL ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

์ด๋Ÿฐ ๊ฒƒ๋“ค์„ ๋งค๋ฒˆ ์ง์ ‘ clang++ ๋ช…๋ น์œผ๋กœ ๋‚˜์—ดํ•˜๋ฉด ๊ธˆ๋ฐฉ ๋ณต์žกํ•ด์ง€๊ธฐ ๋•Œ๋ฌธ์— CMake๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. CMake๋Š” ์‰ฝ๊ฒŒ ๋งํ•ด ์ด ํ”„๋กœ์ ํŠธ๋Š” ์–ด๋–ค ์†Œ์Šค ํŒŒ์ผ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๊ณ , ์–ด๋–ค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋งํฌํ•ด์•ผ ํ•˜๋ฉฐ, ์–ด๋–ค include path๊ฐ€ ํ•„์š”ํ•œ์ง€๋ฅผ ์„ ์–ธํ•˜๋Š” ๋„๊ตฌ๋‹ค.

3.1 Source Tree & Build Tree

Source Tree๋Š” ๋‚ด๊ฐ€ ์ž‘์„ฑํ•œ ์ฝ”๋“œ๊ฐ€ ์žˆ๋Š” ๋””๋ ‰ํ† ๋ฆฌ๊ณ , Build Tree๋Š” CMake๊ฐ€ ์ƒ์„ฑํ•œ ๋นŒ๋“œ ์‚ฐ์ถœ๋ฌผ์ด ๋“ค์–ด๊ฐ€๋Š” ๋””๋ ‰ํ† ๋ฆฌ๋‹ค.

cookie-engine/
    build/
    external/
    src/
    CMakeLists.txt

๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋นŒ๋“œํ•œ๋‹ค.

cmake -S . -B build
cmake --build build

-S .๋Š” ํ˜„์žฌ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ์†Œ์Šค ๋””๋ ‰ํ† ๋ฆฌ๋กœ ์‚ฌ์šฉํ•œ๋‹ค๋Š” ๋œป์ด๊ณ , -B build๋Š” build ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ๋นŒ๋“œ ๋””๋ ‰ํ† ๋ฆฌ๋กœ ์‚ฌ์šฉํ•œ๋‹ค๋Š” ๋œป์ด๋‹ค.

3.2 Target

CMake์—์„œ๋Š” ์‹คํ–‰ ํŒŒ์ผ์ด๋‚˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ target์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

  • ์‹คํ–‰ ํŒŒ์ผ target

    add_executable(cookie-engine
        src/main.cpp
    )
    
  • ์ •์  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ target

    add_library(glad STATIC
        external/glad/src/gl.c
    )
    

3.3 Include Directory

C++์—์„œ #include <...>๋ฅผ ํ•˜๋ ค๋ฉด ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ํ—ค๋” ํŒŒ์ผ์„ ์–ด๋””์„œ ์ฐพ์„์ง€ ์•Œ์•„์•ผ ํ•œ๋‹ค. GLAD ํ—ค๋”๊ฐ€ external/glad/include/glad/gl.h์— ์žˆ๋‹ค๋ฉด, include directory๋Š” external/glad/include๊ฐ€ ๋œ๋‹ค.

target_include_directories(glad PUBLIC
    external/glad/include
)

์†Œ์Šค ํŒŒ์ผ์„ ์ปดํŒŒ์ผํ•˜๋ฉด object file์ด ์ƒ๊ธฐ๋Š”๋ฐ, ์‹ค์ œ ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ํ”„๋กœ๊ทธ๋žจ์„ ๋นŒ๋“œํ•˜๋ ค๋ฉด ์—ฌ๋Ÿฌ object file๊ณผ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ•˜๋‚˜๋กœ linkํ•˜๋Š” ์ž‘์—…์ด ํ•„์š”ํ•˜๋‹ค. CMake์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด target๋ผ๋ฆฌ ์—ฐ๊ฒฐํ•œ๋‹ค.

target_link_libraries(cookie-engine PRIVATE
    SDL3::SDL3
    glad
    GL
)

Linux์—์„œ๋Š” OpenGL ์‹œ์Šคํ…œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋ณดํ†ต GL๋กœ ๋งํฌํ•˜๊ณ , Windows native์—์„œ๋Š” ๋ณดํ†ต opengl32๋ฅผ ๋งํฌํ•œ๋‹ค.