Initialize Class

Date:     Updated:

카테고리:

태그:

Main Reference
- Learn OpenGL
- Rinthel Kwon - OpenGL Lecture


Common

#define CLASS_PTR(klassName)                            \
class klassName;                                        \
using klassName ## UPtr = std::unique_ptr<klassName>;   \
using klassName ## Ptr = std::shared_ptr<klassName>;    \
using klassName ## WPtr = std::weak_ptr<klassName>;

// ex) CLASS_PTR(Shader);
class Shader
using ShaderUPtr = std::unique_ptr<Shader>;
using ShaderPtr = std::shared_ptr<Shader>;
using ShaderWPtr = std::weak_ptr<Shader>;
  • OpenGL과 관련된 대부분의 Object들은 스마트 포인터로 관리될 테니 매크로 선언
  • ##은 왼쪽 오른쪽 붙여주는 역할


std::optional<std::string> LoadTextFile(const std::string& filename) {
    std::ifstream fin(filename);
    if (!fin.is_open()) {
        SPDLOG_ERROR("failed to open file: {}", filename);
        return {};
    }
    std::stringstream text;
    text << fin.rdbuf();
    return text.str();
}

// shader.cpp
bool Shader::LoadFile(const std::string& filename, GLenum shaderType) 
{
    auto result = LoadTextFile(filename);
    if (!result.has_value())
        return false;

    auto& code = result.value();
    const char* codePtr = code.c_str();
    int32_t codeLength = (int32_t)code.length();

  // ...
}
  • std::optional<T> : 일반적으로 T 타입을 리턴하는데, 값이 없을 수도 있음
    • result.has_value()를 통해 리턴값이 null인지 체크
    • null이 아니라면 result.value() 를 통해 값을 받아옴


Shader

class Shader {
public:
    static ShaderUPtr CreateFromFile(const std::string& filename, GLenum shaderType);
    ~Shader();
    uint32_t Get() const { return m_shader; }       

private:
    Shader() {}
    bool LoadFile(const std::string& filename, GLenum shaderType);
    uint32_t m_shader { 0 }; // shader ID
};
  • Shader 클래스의 생성자는 private으로 설정 (+ Unique_Ptr로 관리)
    • CreateFromFile() 함수 외의 다른 방식으로는 인스턴스 생성 못하게 막음
  • Get() 함수만 존재하고, Set()은 존재하지 않음
    • shader 오브젝트의 id는 생성자에서만 관리하고 건드리지 못하게 막음


Program

// program.h
CLASS_PTR(Program)
class Program {
public:
    static ProgramUPtr Create(const std::vector<ShaderPtr>& shaders);

    ~Program();
    uint32_t Get() const { return m_program; }    

private:
    Program() {}
    bool Link(const std::vector<ShaderPtr>& shaders);
    uint32_t m_program { 0 }; 
};
  • Program 클래스는 여러 개의 shader를 링킹해 하나의 실행 가능한 프로그램으로 결합
  • Shader 클래스와 마찬가지로 생성자는 private
  • shader 인스턴스는 사용하지만, 소유할 필요는 없음
    • 하나의 shader 인스턴스가 다른 프로그램에도 사용될 수 있음
    • UPtr이 아닌 shared_ptr로 사용


// program.cpp
ProgramUPtr Program::Create(const std::vector<ShaderPtr>& shaders) {
    auto program = ProgramUPtr(new Program());
    if (!program->Link(shaders))
        return nullptr;
    return std::move(program);
}

Program::~Program() {
    if (m_program) {
        glDeleteProgram(m_program);
    }
}

bool Program::Link(const std::vector<ShaderPtr>& shaders) {
    m_program = glCreateProgram();

    for (auto& shader: shaders)
        glAttachShader(m_program, shader->Get());
    glLinkProgram(m_program);

    int success = 0; 
    glGetProgramiv(m_program, GL_LINK_STATUS, &success);

    if (!success) {
        char infoLog[1024];
        glGetProgramInfoLog(m_program, 1024, nullptr, infoLog);
        SPDLOG_ERROR("failed to link program: {}", infoLog);
        return false;
    }
    return true;
}
  • GLuint glCreateProgram(void) : GLuint는 openGL에서 사용하는 unsigned int
    • openGL오브젝트를 구별하는데 사용. 여기저기 많이 쓰임
  • glCreateProgram()로 program object 생성
  • glAttachShader()로 shader들 붙이기
  • glLinkProgram()로 shader들 링킹

  • void glGetProgramiv(GLuint program, GLenum pname, GLint *params) : 맨 끝 iv는 integer-vector
    • OpenGL 오브젝트들의 상태나 속성에 대한 정보를 쿼리
    • GL_LINK_STATUS 속성으로 링크 성공여부 체크


Context

// Context.h
CLASS_PTR(Context)
class Context {
public:
    static ContextUPtr Create();
    void Render();    
    
private:
    Context() {}
    bool Init();
    ProgramUPtr m_program;
};


// Context.cpp
ContextUPtr Context::Create() {
    auto context = ContextUPtr(new Context());
    if (!context->Init())
        return nullptr; 
    return std::move(context);
}

bool Context::Init() {
    ShaderPtr vertShader = Shader::CreateFromFile("./shader/simple.vs", GL_VERTEX_SHADER);
    ShaderPtr fragShader = Shader::CreateFromFile("./shader/simple.fs", GL_FRAGMENT_SHADER);

    if (!vertShader || !fragShader)
        return false;
    SPDLOG_INFO("vertex shader id: {}", vertShader->Get());
    SPDLOG_INFO("fragment shader id: {}", fragShader->Get());

    m_program = Program::Create({fragShader, vertShader});
    SPDLOG_INFO("program id: {}", m_program->Get());

    m_program = Program::Create({fragShader, vertShader});
    if (!m_program)
        return false;
    SPDLOG_INFO("program id: {}", m_program->Get());

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

    uint32_t vao = 0;
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

    return true;
}

void Context::Render() {
    glClear(GL_COLOR_BUFFER_BIT);

    glUseProgram(m_program->Get());
    glDrawArrays(GL_POINTS, 0, 1);
}


image

  • 한 번만 설정하면 되는 내용들은 Context 클래스로 관리 (리팩토링)
  • Program 클래스도 Context 내부에서 생성
  • Shader들은 shared_ptr로 들어가야 하기 때문에 ShderPtr로 리턴 타입 캐스팅(원래는 UPtr)
  • 구체적인 렌더링 설정은 이후에 추가..
  • GLuint로 표기된 GL_Object의 id도 잘 출력됨


CmakeList

add_executable(${PROJECT_NAME} 
    src/main.cpp
    src/common.cpp src/common.h
    src/shader.cpp src/shader.h
    src/program.cpp src/program.h
    src/context.cpp src/context.h
    )
  • 당연한 이야기지만 빌드할 때 프로젝트에 추가되는 소스코드들 포함시켜줘야 함



맨 위로 이동하기

OpenGL 카테고리 내 다른 글 보러가기

댓글 남기기