본문 바로가기
-----ETC-----/C++ 게임 개발 시리즈

[C++ 게임 개발 시리즈] Day 22: 3D 그래픽 기초

by cogito21_cpp 2024. 8. 1.
반응형

3D 그래픽 기초

3D 그래픽은 2D 그래픽보다 복잡하지만, 게임을 더욱 몰입감 있게 만들어 줍니다. 오늘은 3D 그래픽의 기본 개념과 함께 OpenGL을 사용하여 간단한 3D 장면을 렌더링하는 방법을 학습하겠습니다.

3D 그래픽의 기본 요소

3D 그래픽은 3차원 공간에서 객체를 표현하고, 이를 2차원 화면에 렌더링하는 과정입니다. 3D 그래픽의 주요 요소는 다음과 같습니다:

  1. 좌표 시스템(Coordinate System): 3D 공간에서 객체의 위치를 나타내는 방법입니다.
  2. 카메라(Camera): 3D 장면을 관찰하는 가상 카메라입니다.
  3. 모델(Model): 3D 객체의 형태와 구조를 정의하는 데이터입니다.
  4. 광원(Lighting): 3D 장면에 조명을 추가하여 현실감을 더합니다.
  5. 쉐이더(Shader): 그래픽 처리 파이프라인에서 객체의 색상, 조명 등을 계산하는 프로그램입니다.

OpenGL을 사용한 3D 그래픽

OpenGL은 크로스 플랫폼 3D 그래픽 API로, 다양한 그래픽 기능을 제공합니다. 다음 예제에서는 OpenGL과 GLFW를 사용하여 간단한 3D 장면을 렌더링하는 방법을 설명합니다.

GLFW 설치 및 설정

GLFW는 OpenGL을 쉽게 사용할 수 있게 해주는 라이브러리입니다.

  1. GLFW 설치:
    • GLFW를 공식 사이트에서 다운로드하거나 패키지 관리자를 통해 설치합니다.
  2. GLFW 초기화 및 창 생성:
    • GLFW를 초기화하고, OpenGL 컨텍스트를 포함하는 창을 생성합니다.

OpenGL 초기화 및 3D 장면 렌더링

다음은 OpenGL과 GLFW를 사용하여 간단한 3D 장면을 렌더링하는 코드입니다.

#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>

// 정점 셰이더 소스 코드
const char* vertexShaderSource = R"glsl(
#version 330 core
layout (location = 0) in vec3 aPos;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);
}
)glsl";

// 프래그먼트 셰이더 소스 코드
const char* fragmentShaderSource = R"glsl(
#version 330 core
out vec4 FragColor;

void main()
{
    FragColor = vec4(1.0, 0.5, 0.2, 1.0);
}
)glsl";

// 셰이더 컴파일 함수
GLuint compileShader(GLenum type, const char* source) {
    GLuint shader = glCreateShader(type);
    glShaderSource(shader, 1, &source, NULL);
    glCompileShader(shader);

    GLint success;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
    if (!success) {
        char infoLog[512];
        glGetShaderInfoLog(shader, 512, NULL, infoLog);
        std::cerr << "ERROR::SHADER::COMPILATION_FAILED\n" << infoLog << std::endl;
    }

    return shader;
}

// 셰이더 프로그램 생성 함수
GLuint createShaderProgram() {
    GLuint vertexShader = compileShader(GL_VERTEX_SHADER, vertexShaderSource);
    GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, fragmentShaderSource);

    GLuint shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    GLint success;
    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
    if (!success) {
        char infoLog[512];
        glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
        std::cerr << "ERROR::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
    }

    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    return shaderProgram;
}

int main() {
    // GLFW 초기화
    if (!glfwInit()) {
        std::cerr << "Failed to initialize GLFW" << std::endl;
        return -1;
    }

    // OpenGL 버전 설정 (3.3)
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    // 창 생성
    GLFWwindow* window = glfwCreateWindow(800, 600, "3D Graphics with OpenGL", NULL, NULL);
    if (!window) {
        std::cerr << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }

    // OpenGL 컨텍스트 설정
    glfwMakeContextCurrent(window);

    // GLEW 초기화
    glewExperimental = GL_TRUE;
    if (glewInit() != GLEW_OK) {
        std::cerr << "Failed to initialize GLEW" << std::endl;
        return -1;
    }

    // 셰이더 프로그램 생성
    GLuint shaderProgram = createShaderProgram();

    // 정점 데이터
    float vertices[] = {
         0.5f,  0.5f, -0.5f,
        -0.5f,  0.5f, -0.5f,
        -0.5f, -0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f,  0.5f,
        -0.5f, -0.5f,  0.5f,
         0.5f, -0.5f,  0.5f
    };

    unsigned int indices[] = {
        0, 1, 3, 1, 2, 3, // 앞
        4, 5, 7, 5, 6, 7, // 뒤
        0, 1, 5, 0, 4, 5, // 위
        2, 3, 7, 2, 6, 7, // 아래
        1, 2, 6, 1, 5, 6, // 왼쪽
        0, 3, 7, 0, 4, 7  // 오른쪽
    };

    GLuint 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, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    // OpenGL 상태 설정
    glEnable(GL_DEPTH_TEST);

    // 메인 루프
    while (!glfwWindowShouldClose(window)) {
        // 입력 처리
        if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
            glfwSetWindowShouldClose(window, true);
        }

        // 화면 지우기
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // 셰이더 프로그램 사용
        glUseProgram(shaderProgram);

        // 변환 행렬 설정
        glm::mat4 model = glm::mat4(1.0f);
        glm::mat4 view = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -3.0f));
        glm::mat4 projection = glm::perspective(glm::radians(45.0f), 800.0f / 600.0f, 0.1f, 100.0f);

        GLint modelLoc = glGetUniformLocation(shaderProgram, "model");
        GLint viewLoc = glGetUniformLocation(shaderProgram, "view");
        GLint projectionLoc = glGetUniformLocation(shaderProgram, "projection");

        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection));

        // 객체 그리기


        glBindVertexArray(VAO);
        glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
        glBindVertexArray(0);

        // 버퍼 교환
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    // 자원 해제
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteBuffers(1, &EBO);

    glfwTerminate();
    return 0;
}

 

결론

오늘은 3D 그래픽의 기본 개념과 OpenGL을 사용하여 간단한 3D 장면을 렌더링하는 방법을 학습했습니다. 이를 통해 3D 객체를 렌더링하고, 변환 행렬을 사용하여 객체를 이동, 회전, 스케일링하는 방법을 배웠습니다. 질문이나 추가적인 피드백이 있으면 언제든지 댓글로 남겨 주세요. 내일은 "Day 23: 3D 모델링과 텍스처링"에 대해 학습하겠습니다.

반응형