OpenGL

대인공·2022년 7월 26일
1

OpenGL

목록 보기
1/8
post-thumbnail

OpenGL

1. OpenGL이란?

   ⚬ 3D그래픽을 화면에 표현하기 위해 필요한 기본적인 기능들을 모은 오픈 API
          
          

2. OpenGL로 할 수 있는 일들

   ⚬ Real-time 2D/3DRendering => 그래픽과 관련된 거의 모든것
          
          

3. OpenGL외 Graphics

⚬ Graphics API

   - DirectX : Microsoft(Window)
   - Metal : Apple(Mac)
   - Vulkan : Next generation of OpenGL
   

⚬ GPGPU API

   - CUDA : nVidia
   - OpenCL : Khronos
          
          
          

4. OpenGL의 특징

    Low-level API
   ⚬ Cross-platform => 기본적인 구조만 알고있으면 쉽게 적용할수 있다.
   ⚬ Standard / Spencification only
   

Graphics Pipline

1. 그림이 그려지는 순서

3D Graphics에서는 기본 입력단위가 삼각형이다.

   ⚬ 1. Application : 그리고 싶은 정점의 위치 / 색상 등을 입력
        - 예) 내가 그리고 싶은 그림의 기본단위 삼각형의 좌표를 입력해준다.
        
   ⚬ 2. Geometry Processing : 정점 단위의 처리. 정점의 위치 결정
        - 예) 가상의 카메라로 특정 각도에서 그림을 바라봤을때 화면의 어느 위치에 있느냐를 계산해준다.
        
   ⚬ 3. Rasterization : 정점 단위로 구성된 삼각형을 픽셀단위로 변경  
   
   ⚬ 4. Pixel : 픽셀단위의 처리. 픽셀의 색상의 결정
   

Application : 애플리케이션 프로그램 영역 - OpenGL함수
Geometry, Gasterization, Pixel : GPU영역 (그래픽카드 등등...)

2. Programmable Shader

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코드 내에서 빌드 / 로딩됨

3. Shader Code Loading

임의의 Shader파일을 읽은후 오브젝트를 만들고 코드를 생성한다.
그리고 컴파일후 오류가 있다면 오류를 레포팅하는 작업

⚬ Text File Loading

  • 텍스트를 읽어줄 헤더 파일 추가 , src / common.h (텍스트 파일을 로딩하는 헤더함수)
#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한다. 된 상태라면 하지 않는다.
  • 파일을 로드할 코드 작성 , src / common.cpp파일 작성

#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 Class Design

  • shader 클래스 설계
    - OpenGL Shader object를 가지고 있다.
    - 인스턴스가 생성될때 로딩할 파일명을 입력받는다.
    - 입력된 파일명으로부터 인스턴스 생성이 실패하면 메모리 할당 해제
    - C++11의 smart pointer 활용
  • smart pointer란?
    \- C++11부터 사용가능한 좀 더 안전한 포인터
    \- 메모리 할당을 받을때 소유권에 대한 정보가 있다.
    \- 명시적인 delete구문이 필요없다.

    관련 함수

    1. std :: unique_ptr : 해당 메모리 블록을 단독으로 소유
    2. std :: shared_ptr : 해당 메모리 블록의 소유권을 공유
    3. std :: weak_ptr : 해당 메모리 소유원은 없지만 접근 가능

https://www.youtube.com/watch?v=h-3090EKVv4&t=300s 참조

  • 메크로 추가 , src / common.h파일 작성
#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;

  • Shader를 저장할 헤더파일 , src / Shader.h파일 작성
#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를 리턴하기 위함
  • Shader파일 CreateFromFile() 구현 , src / Shader.cpp파일 작성
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);
  	}
}
  • Shader파일 LoadFile() 구현 , src / Shader.cpp파일 작성
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 제거

  • Shader클래스 테스트 , src / main.cpp 수정

#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 Code

가장 단순한 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 Code

가장 단순한 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파일을 사용하기 위해서 컴파일을 해주어야 하는데 그때마다 수정해주어야 한다.

⚬ 실행 결과

로딩 실패 메시지 출력 후 실행 멈춤현상 발생

  • .vscode/settings.json을 열어 디버깅환경 변수를 추가
{
  	"C_Cpp.intelliSenseEngineFallback": "Enabled",
  	"cmake.debugConfig":
  	{
  		"cwd": "${workspaceFolder}"
  	}
}

로딩 및 컴파일이 성공하면 Shader object id(정수)가 출력되는 것을 확인

4. Program Class Desing

  • Pipline을 OpenGL에서는 Program이라고 한다.
    - vertex shader, fragment shader를 연결한 pipline program
    - 이 클래스를 사용하여 최종적으로 그림을 그려준다.
  • 진행 순서
    - 두개의 shader를 입력받아서 Program을 링크(함수)시킨다.
    - 링크에 성공하면 OpenGL Program Object를 생성
    - 실패하면 메모리 할당 해제
  • Program 클래스 , src / program.h파일 작성
#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와 같은 메모리를 가지고 있다가 코드블럭을 탈출하면 할당해제된다.

  • Create() 구현 , src / program.cpp파일 작성
#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);
  	}
}
  • Link() 구현 , src / program.cpp파일 작성
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제거
  • Program 클래스 테스트 , 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);
  
    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
    • 컴파일을 해야지 사용가능

⚬ Refactoring

우선 구동되는 코드를 작성후 적절한 용도에 따라 정리하는 과정

  • 프로그램 사이클을 고려하여 코드 리팩토링
    - GLFW , OpenGL Context , GLAD 초기화
    - 그림을 그리기 위한 OpenGL objects 생성 ( shader , program )
    - 렌더링
    - OpenGL objects 제거
    - GLFW 종료 , 프로그램 종료

    -> OpenGL objects들을 관리하고 렌더링하는 코드를 분리하는 작업

⚬ Context class Design

  • Context class Design , src / context.h 생성
#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__

프로그램이 종료되기전 할당된 메모리들을 한번에 관리하여 종료하는 클래스 디자인이다

  • Create() 구현 , src / context.cpp 작성
#include "context.h"
  
ContextUPtr Context :: Create()
{
  	auto context = ContextUPtr(new Create());
  	if(!context->Init()) { return nullptr; }
  	return std :: move(context);
} 
  • Init() 구현 , src / context.cpp 작성
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);
profile
이제 막 시작하는 유니티 클라이언트

0개의 댓글

관련 채용 정보