⚬ 3D그래픽을 화면에 표현하기 위해 필요한 기본적인 기능들을 모은 오픈 API
⚬ Real-time 2D/3DRendering => 그래픽과 관련된 거의 모든것
- DirectX : Microsoft(Window)
- Metal : Apple(Mac)
- Vulkan : Next generation of OpenGL
- CUDA : nVidia
- OpenCL : Khronos
Low-level API
⚬ Cross-platform => 기본적인 구조만 알고있으면 쉽게 적용할수 있다.
⚬ Standard / Spencification only
3D Graphics에서는 기본 입력단위가 삼각형이다.
⚬ 1. Application : 그리고 싶은 정점의 위치 / 색상 등을 입력
- 예) 내가 그리고 싶은 그림의 기본단위 삼각형의 좌표를 입력해준다.
⚬ 2. Geometry Processing : 정점 단위의 처리. 정점의 위치 결정
- 예) 가상의 카메라로 특정 각도에서 그림을 바라봤을때 화면의 어느 위치에 있느냐를 계산해준다.
⚬ 3. Rasterization : 정점 단위로 구성된 삼각형을 픽셀단위로 변경
⚬ 4. Pixel : 픽셀단위의 처리. 픽셀의 색상의 결정
Application : 애플리케이션 프로그램 영역 - OpenGL함수
Geometry, Gasterization, Pixel : GPU영역 (그래픽카드 등등...)
Programmable Pipline : 프로그래머가 직접 GPU안에서 돌아가는 프로그램을 조작하여 처리하는것
Shader : 각 파이프라인 단계마다 GPU상에서 실행되는 작은 프로그램
⚬ 사용 언어 : GLSL(GL Shader language)라는 C기반 프로그래밍 언어로 작성
Vertex Shader : 각 Vertex에 대한 정점의 위치를 계산하는 부분
Geometry Shader :
Fragment Shader : 각각 픽셀별로 실행되는 것 (Pixel Shader라고도 함)
⚬ OpenGL은 그림을 그려내기 위해 반드시 두개의 Shader가 필요하다.
- Vertex Shader
- Fragment Shader
⚬ shader 코드는 OpenGL코드 내에서 빌드 / 로딩됨
임의의 Shader파일을 읽은후 오브젝트를 만들고 코드를 생성한다.
그리고 컴파일후 오류가 있다면 오류를 레포팅하는 작업
#ifndef __COMMON_H__
#define __COMMON_H__
#include<memory>
#include<string>
#include<optional>
#include<glad.glad.h>
#include<glfw/glfw3.h>
#include<spdlog/spdlog.h>
std :: optional<std :: string> LoadTextFile(const std :: string& filename);
#endif // __COMMON_H__
- #ifndef, #define, #endif 이 세개는 한 세트로 묶어서 생각
- #ifndef : __COMMON_H__가 define 안된상태라면 define한다. 된 상태라면 하지 않는다.
#include "common.h" //위의 미리 만들어둔 헤더파일 선언
#include <fstream>
#include <sstream>
std :: optional<std :: string> LoadTextFile<const std :: string& filename>
{
std :: ifstream fin(filename);
if(!fin.is_open()) // is_open() : c++방식의 파일 로드 방식
{
SPDLOG_ERROR("Failed to open file {}", filename)
return {}; // {} : 아무것도 없는 값이 리턴된다
}
std :: stringstream text;
text << fin.rdbuf();
return text.str(); // str() : stringstream안의 함수
}
- shader 클래스 설계
- OpenGL Shader object를 가지고 있다.
- 인스턴스가 생성될때 로딩할 파일명을 입력받는다.
- 입력된 파일명으로부터 인스턴스 생성이 실패하면 메모리 할당 해제
- C++11의 smart pointer 활용
\- C++11부터 사용가능한 좀 더 안전한 포인터
\- 메모리 할당을 받을때 소유권에 대한 정보가 있다.
\- 명시적인 delete구문이 필요없다.
관련 함수
- std :: unique_ptr : 해당 메모리 블록을 단독으로 소유
- std :: shared_ptr : 해당 메모리 블록의 소유권을 공유
- std :: weak_ptr : 해당 메모리 소유원은 없지만 접근 가능
#ifndef __COMMON_H__
#define __COMMON_H__
#include<memory>
#include<string>
#include<optional>
#include<glad.glad.h>
#include<glfw/glfw3.h>
#include<spdlog/spdlog.h>
//추가
#define CLASS_PTR(klassName) \
class klassName : \
using klassName ## UPtr = std :: unique_ptr<klassName>; \
using klassName ## Ptr = std :: sgared_ptr<klassName>; \
using klassName ## WPtr = std :: weak_ptr<klassName>;
// std :: unique_ptr <DataType>을 대신하여 UPtr사용
// ex)CLASS_PTR(ASD); -> 호출
//class ASD;
//using ASD ## UPtr = std :: unique_ptr<klassName>;
//using ASD ## Ptr = std :: sgared_ptr<klassName>;
//using ASD ## WPtr = std :: weak_ptr<klassName>;
//들이 따라온다.
// ## : 좌우를 붙여준다 (using ASDWPtr = std :: weak_ptr<klassName>;와 같은 의미)
// 사용 : ASDUPtr -> using ASD ## UPtr = std :: unique_ptr<klassName>;
std :: optional<std :: string> LoadTextFile(const std :: string& filename);
#endif // __COMMON_H__
std :: unique_ptr 을 대신하여 UPtr사용
ex) CLASS_PTR(ASD); -> 호출
class ASD;
using ASD ## UPtr = std :: unique_ptr;
using ASD ## Ptr = std :: sgared_ptr;
using ASD ## WPtr = std :: weak_ptr;
들이 따라온다.
## : 좌우를 붙여준다 (using ASDWPtr = std :: weak_ptr;와 같은 의미)
사용 : ASDUPtr -> using ASD ## UPtr = std :: unique_ptr;
#ifndef __COMMON_H__
#define __COMMON_H__
#includ "common.h"
CLASS_PTR(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 sstd :: string& filename, GLenum shaderType);
uint32_t m_shader{ 0 };
};
#endif // __COMMON_H__
설계 방식 이유
- 생성자가 private인 이유 : creatFromFile()함수 외에 다른 방식의 Shader인스턴스 생성을 막기 위함.
- Get()은 있는데 Set()이 없는 이유 : shader오브젝트의 생성 관리는 Shader 내부에서만 관리함
- LoadFile()이 bool을 리턴 하는 이유 : 생성에 실패할 경우 false를 리턴하기 위함
ShaderUPtr Shader :: CreateFromFile(const sstd :: string& filename, GLenum shaderType)
{
auto _shader = std :: unique_ptr<Shader>(new Shader()); //Shader클래스 내부함수이기 때문에 호출가능
if(!_shader -> LoadFIle(filename, shaderType))
{
return nullptr; // 리턴이 nullptr가 되면 _shader에 할당된 메모리가 해제된다.
}
return std :: move(_shader); // 할당된 메모리를 외부로 넘겨준다.
}
Shader:: ~Shader() //소멸자 : 이미 있다면 새로 생성한 m_shader를 소멸
{
if(m_shader)
{
glDeleteShader(m_shader);
}
}
bool Shader :: LoadFile(const sstd :: 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();
// create and compile shader
m_shader = glCreateShader(shaderType);
glShaderSource(m_shader, 1, (const GLchar* const*)&codeptr, &codeLegth);
glCompileShader(m_shader);
// chack compile error
int success = 0;
glGetShaderiv(m_shader, GL_COMPILE_STATUS, &success);
if(!success)
{
char infoLog[1024];
glGetShaderInfoLog(m_shader, 1024, nullptr, infoLog);
SPDLOG_ERROR("failed to compile shader : \"{}\"", filename);
SPDLOG_ERROR("reason {}", infoLog);
return false;
}
return true;
}
- create and compile shader
glCreateShader() : shader오브젝트 생성
glShaderSource() : 소스코드 입력
glCompileShader() : shader컴파일
- chack compile error
glGetShaderiv() : 컴파일 상태 조회, shader에 대한 정수형 정보를 얻어옴
glGetShaderInfoLog() : 에러로그 가져오기, shader에 대한 로그를 얻어옴
glDeleteShader() : shader object 제거
#include "common.h"
#include "context.h"
void OnFramebufferSizeChange(GLFWwindow* window, int width, int height) {
SPDLOG_INFO("framebuffer size changed: ({} x {})", width, height);
glViewport(0, 0, width, height);
}
void OnKeyEvent(GLFWwindow* window,
int key, int scancode, int action, int mods) {
SPDLOG_INFO("key: {}, scancode: {}, action: {}, mods: {}{}{}",
key, scancode,
action == GLFW_PRESS ? "Pressed" :
action == GLFW_RELEASE ? "Released" :
action == GLFW_REPEAT ? "Repeat" : "Unknown",
mods & GLFW_MOD_CONTROL ? "C" : "-",
mods & GLFW_MOD_SHIFT ? "S" : "-",
mods & GLFW_MOD_ALT ? "A" : "-");
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
glfwSetWindowShouldClose(window, true);
}
}
void Render() {
glClearColor(0.1f, 0.2f, 0.3f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
int main(int argc, const char** argv) {
// 시작을 알리는 로그
SPDLOG_INFO("Start program");
// glfw 라이브러리 초기화, 실패하면 에러 출력후 종료
SPDLOG_INFO("Initialize glfw");
if (!glfwInit()) {
const char* description = nullptr;
glfwGetError(&description);
SPDLOG_ERROR("failed to initialize glfw: {}", description);
return -1;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// glfw 윈도우 생성, 실패하면 에러 출력후 종료
SPDLOG_INFO("Create glfw window");
auto window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_NAME,
nullptr, nullptr);
if (!window) {
SPDLOG_ERROR("failed to create glfw window");
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
// glad를 활용한 OpenGL 함수 로딩
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
SPDLOG_ERROR("failed to initialize glad");
glfwTerminate();
return -1;
}
auto glVersion = glGetString(GL_VERSION);
SPDLOG_INFO("OpenGL context version: {}", glVersion);
auto vertexShader = Shader :: CreateFromFile(".shader/sample.vs", GL_VERTEX_SHADER);
auto fragmentShader = Shader :: CreateFromFile(".shader/sample.fs", GL_VERTEX_SHADER);
SPDLOG_INFO("vertex shader id: {}" vertexShader->Get());
SPDLOG_INFO("fragment shader id: {}" fragmentShader->Get());
OnFramebufferSizeChange(window, WINDOW_WIDTH, WINDOW_HEIGHT);
glfwSetFramebufferSizeCallback(window, OnFramebufferSizeChange);
glfwSetKeyCallback(window, OnKeyEvent);
auto context = Context::Create();
if (!context) {
SPDLOG_ERROR("failed to create context");
glfwTerminate();
return -1;
}
// glfw 루프 실행, 윈도우 close 버튼을 누르면 정상 종료
SPDLOG_INFO("Start main loop");
while (!glfwWindowShouldClose(window)) {
context->Render();
glfwSwapBuffers(window);
glfwPollEvents();
}
context.reset();
glfwTerminate();
return 0;
}
가장 단순한 Vertex Shader
확장자는 shader/ Sample.vs
#version 330 core
layout(location = 0) in vec3 aPos;
void main()
{
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
}
가장 단순한 Fragment Shader
확장자는 shader/ Sample.fs
#version 330 core
out vec4 fragColor;
void main()
{
fragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
CMakeListe.txt에 작성한 .cpp파일과 .h파일을 추가해준다
add_executable(${PROJECT_NAME}
src/main.cpp
src/commen.cpp src/common.h
src/shader.cpp src/shader.h
)
- .cpp파일과 .h파일을 사용하기 위해서 컴파일을 해주어야 하는데 그때마다 수정해주어야 한다.
로딩 실패 메시지 출력 후 실행 멈춤현상 발생
{
"C_Cpp.intelliSenseEngineFallback": "Enabled",
"cmake.debugConfig":
{
"cwd": "${workspaceFolder}"
}
}
로딩 및 컴파일이 성공하면 Shader object id(정수)가 출력되는 것을 확인
- Pipline을 OpenGL에서는 Program이라고 한다.
- vertex shader, fragment shader를 연결한 pipline program
- 이 클래스를 사용하여 최종적으로 그림을 그려준다.
- 진행 순서
- 두개의 shader를 입력받아서 Program을 링크(함수)시킨다.
- 링크에 성공하면 OpenGL Program Object를 생성
- 실패하면 메모리 할당 해제
#ifndef __PROGRAM_H__
#define __PROGRAM_H__
#include "common.h"
#include "shader.h"
CLASS_PRT(Program)
class Program
{
public:
static ProgramUPtr Create(const std :: vector<ShaderPtr>& shaders);
~Program();
uint32_t Get() const { return m_rogram; }
private:
Program() {}
bool Link(const std :: vector<ShaderPtr>& shaders);
uint32_t m_rogram {0};
};
#endif // __PROGRAM_H__
설계 방식 이유
- vertex, gragment shader외에 여러개의 shader를 링크할 수도 있게 함
- shader인스턴스 인자는 필요하지만 소유할 필요는 없음
- shader인스턴스는 다른 Program인스턴스를 만드는데 재사용할 수도 있음
- 따라서 shared pointer를 사용 -> shaderPtr
shared pointer (공동소유 - 소유를 하고 있는 변수를 세고 있다.)
ex1)
std :: shared_pointer a = std :: shared_pointer(new int);
std :: shared_pointer b = a;
= a와 b는 같은 메모리를 가지고 있다 /ex2)
std :: shared_pointer a = std :: shared_pointer(new int);
{
std :: shared_pointer b = a;
}
= b는 코드 블럭내에 a와 같은 메모리를 가지고 있다가 코드블럭을 탈출하면 할당해제된다.
#include "program.h"
ProgramUPtr Program :: Create(const std :: vector<ShaderPtr>& shaders)
{
auto program = ProgramUPte(new Program());
if(!program)
{
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()l
for(auto& shader : shaders)
{
glAttackShader(m_program, shader->Get());
}
glLinkProgram(m_program);
int success = 0;
glGetProgramiv(m_program, GL_LINK_STATUS, &success);
if(!success)
{
char infoLof[1024];
glGetProgramInfoLog(m_program, 1024, nullptr, ingoLog);
SPDLOG_ERROR("failed to link program: {}", ingoLog);
return false;
}
return true;
}
사용 함수 설명
- glCreateProgram() : 새로운 OpenGL Program Object생성
- glAttackShader() : Program에 Shader를 붙이기
- glLinkProgram() : Program 링크
- glGetProgramiv() : program에 대한 정수형 정보를 얻어옴
- glGetProgramInfoLog() : program에 대한 로그를 얻어옴, 링크 에러 얻어내는 용도로 사용
- glDeleteProgram() : OpenGL Program Object제거
#include "common.h"
#include "context.h"
#include "program.h"
void OnFramebufferSizeChange(GLFWwindow* window, int width, int height) {
SPDLOG_INFO("framebuffer size changed: ({} x {})", width, height);
glViewport(0, 0, width, height);
}
void OnKeyEvent(GLFWwindow* window,
int key, int scancode, int action, int mods) {
SPDLOG_INFO("key: {}, scancode: {}, action: {}, mods: {}{}{}",
key, scancode,
action == GLFW_PRESS ? "Pressed" :
action == GLFW_RELEASE ? "Released" :
action == GLFW_REPEAT ? "Repeat" : "Unknown",
mods & GLFW_MOD_CONTROL ? "C" : "-",
mods & GLFW_MOD_SHIFT ? "S" : "-",
mods & GLFW_MOD_ALT ? "A" : "-");
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
glfwSetWindowShouldClose(window, true);
}
}
void Render() {
glClearColor(0.1f, 0.2f, 0.3f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
int main(int argc, const char** argv) {
// 시작을 알리는 로그
SPDLOG_INFO("Start program");
// glfw 라이브러리 초기화, 실패하면 에러 출력후 종료
SPDLOG_INFO("Initialize glfw");
if (!glfwInit()) {
const char* description = nullptr;
glfwGetError(&description);
SPDLOG_ERROR("failed to initialize glfw: {}", description);
return -1;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// glfw 윈도우 생성, 실패하면 에러 출력후 종료
SPDLOG_INFO("Create glfw window");
auto window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_NAME,
nullptr, nullptr);
if (!window) {
SPDLOG_ERROR("failed to create glfw window");
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
// glad를 활용한 OpenGL 함수 로딩
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
SPDLOG_ERROR("failed to initialize glad");
glfwTerminate();
return -1;
}
auto glVersion = glGetString(GL_VERSION);
SPDLOG_INFO("OpenGL context version: {}", glVersion);
ShaderPtr vertShader = Shader :: CreateFromFile(".shader/sample.vs", GL_VERTEX_SHADER); // 수정사항
ShaderPtr fragShader = Shader :: CreateFromFile(".shader/sample.fs", GL_VERTEX_SHADER); // 수정사항
SPDLOG_INFO("vertex shader id: {}" vertexShader->Get()); // 수정사항
SPDLOG_INFO("fragment shader id: {}" fragmentShader->Get()); // 수정사항
auto program = Program :: Create({fragShader, vertShader}); // 수정사항
SPDLOG_INFO("program id: {}" program->Get()); // 수정사항
OnFramebufferSizeChange(window, WINDOW_WIDTH, WINDOW_HEIGHT);
glfwSetFramebufferSizeCallback(window, OnFramebufferSizeChange);
glfwSetKeyCallback(window, OnKeyEvent);
// glfw 루프 실행, 윈도우 close 버튼을 누르면 정상 종료
SPDLOG_INFO("Start main loop");
while (!glfwWindowShouldClose(window)) {
glClear(GL_COLOR_BUFFER_BIT);
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
- CMakeListe.txt에 작성한 .cpp파일과 .h파일을 추가해준다
- src/program.cpp src/program.h
- 컴파일을 해야지 사용가능
우선 구동되는 코드를 작성후 적절한 용도에 따라 정리하는 과정
프로그램 사이클을 고려하여 코드 리팩토링
- GLFW , OpenGL Context , GLAD 초기화
- 그림을 그리기 위한 OpenGL objects 생성 ( shader , program )
- 렌더링
- OpenGL objects 제거
- GLFW 종료 , 프로그램 종료
-> OpenGL objects들을 관리하고 렌더링하는 코드를 분리하는 작업
#ifndef __CONTEXT_H__
#define __CONTEXT_H__
#include "common.h"
#include "shader.h"
#include "program.h"
CLASS_PTR(Context)
class Context
{
public:
static ContextUPtr Create();
void Render();
private:
Context() {}
bool Init();
ProgramUPtr m_programl
};
#endif // __CONTEXT_H__
프로그램이 종료되기전 할당된 메모리들을 한번에 관리하여 종료하는 클래스 디자인이다
#include "context.h"
ContextUPtr Context :: Create()
{
auto context = ContextUPtr(new Create());
if(!context->Init()) { return nullptr; }
return std :: move(context);
}
bool Context :: Init()
{
ShaderPtr vertShader = Shader :: CreateFromFile(".shader/sample.vs", GL_VERTEX_SHADER);
ShaderPtr fragShader = Shader :: CreateFromFile(".shader/sample.fs", GL_VERTEX_SHADER);
if(!vertShader || !fragShader) { return false; }
SPDLOG_INFO("vertex shader id: {}" vertexShader->Get());
SPDLOG_INFO("fragment shader id: {}" fragmentShader->Get());
m_program = Program :: Create({fragShader, vertShader});
if(!m_program) { return false; }
SPDLOG_INFO("program id: {}" m_program->Get());
glClearColor(0.0f, 0.1f, 0.2f, 0.0f);
return true;
}
src/main.cpp에 있던 테스트하던 내용을 그대로 가져온다.
- Render() 구현 , src / context.cpp 작성
void Context :: Render()
{
glClear(GL_COLOR_BUFFER_BIT);
}
- Context 클래스 테스트 , src / main.cpp 수정
#include "common.h"
#include "context.h"
#include "program.h"
void OnFramebufferSizeChange(GLFWwindow* window, int width, int height) {
SPDLOG_INFO("framebuffer size changed: ({} x {})", width, height);
glViewport(0, 0, width, height);
}
void OnKeyEvent(GLFWwindow* window,
int key, int scancode, int action, int mods) {
SPDLOG_INFO("key: {}, scancode: {}, action: {}, mods: {}{}{}",
key, scancode,
action == GLFW_PRESS ? "Pressed" :
action == GLFW_RELEASE ? "Released" :
action == GLFW_REPEAT ? "Repeat" : "Unknown",
mods & GLFW_MOD_CONTROL ? "C" : "-",
mods & GLFW_MOD_SHIFT ? "S" : "-",
mods & GLFW_MOD_ALT ? "A" : "-");
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
glfwSetWindowShouldClose(window, true);
}
}
void Render() {
glClearColor(0.1f, 0.2f, 0.3f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
int main(int argc, const char** argv) {
// 시작을 알리는 로그
SPDLOG_INFO("Start program");
// glfw 라이브러리 초기화, 실패하면 에러 출력후 종료
SPDLOG_INFO("Initialize glfw");
if (!glfwInit()) {
const char* description = nullptr;
glfwGetError(&description);
SPDLOG_ERROR("failed to initialize glfw: {}", description);
return -1;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// glfw 윈도우 생성, 실패하면 에러 출력후 종료
SPDLOG_INFO("Create glfw window");
auto window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_NAME,
nullptr, nullptr);
if (!window) {
SPDLOG_ERROR("failed to create glfw window");
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
// glad를 활용한 OpenGL 함수 로딩
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
SPDLOG_ERROR("failed to initialize glad");
glfwTerminate();
return -1;
}
auto glVersion = glGetString(GL_VERSION);
SPDLOG_INFO("OpenGL context version: {}", glVersion);
OnFramebufferSizeChange(window, WINDOW_WIDTH, WINDOW_HEIGHT);
glfwSetFramebufferSizeCallback(window, OnFramebufferSizeChange);
glfwSetKeyCallback(window, OnKeyEvent);
auto context = Context::Create(); // 수정사항
if (!context) {
SPDLOG_ERROR("failed to create context");
glfwTerminate();
return -1;
}
// glfw 루프 실행, 윈도우 close 버튼을 누르면 정상 종료
SPDLOG_INFO("Start main loop");
while (!glfwWindowShouldClose(window)) {
context->Render(); // 수정사항
glfwSwapBuffers(window);
glfwPollEvents();
}
context.reset(); // 수정사항
glfwTerminate();
return 0;
}
테스트를 위해 일부 코드를 임시로 추가한다.
Context :: Init()에 vertex array object를 생성한다.
uint32_t vao = 0; glGenVertesArrays(1, &vao); glBindVertexArray(vao);
COntext : Render()에 draw call을 추가한다.
glUseProgram(m_program->Get()); glDrawArrays(GL_POINTS, 0, 1);
- CMakeListe.txt에 작성한 .cpp파일과 .h파일을 추가해준다
- src/program.cpp src/program.h
- 컴파일을 해야지 사용가능
추가사항
정점의 위치 변경 , shader/simple.vs
gl_Position = vec4(0.5, 0.5, 0.0, 1.0);
정점의 색상 변경 , shader/simple.fs
fragColor = vec4(1.0, 1.0, 1.0, 1.0);