Initialize Class
카테고리: OpenGL
태그: Graphics
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);
}
- 한 번만 설정하면 되는 내용들은 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
)
- 당연한 이야기지만 빌드할 때 프로젝트에 추가되는 소스코드들 포함시켜줘야 함
댓글 남기기