지금까지 학습한 내용을 바탕으로 게임 엔진 제작을 시작하였다.
우선 엔진의 기본 구성 요소들(Camera, Model, Mesh, Light, Material 등)은 공부 일지에서 사용했던 코드를 사용하되, 내 스타일에 맞게 여기저기 많이 고쳤다.
공부할 때 사용했던 코드는 포인터를 거의 사용하지 않았기 때문에 헤더 파일에 #include가 필요하게되어 헤더가 비대화되는 문제가 있었다.
https://ciel45.tistory.com/4 (관련 내용)
그래서 언리얼 엔진 개발을 할 때와 마찬가지로, 포인터 + 포워드 선언을 사용하는 방식으로 바꿨다.
프로퍼티 이름들이 불필요하게 길거나 직관적이지 못한 부분들도 많아서 많이 개선하였다.
지금까지의 현황은 여기서 볼 수 있다. (Clone하면 바로 실행해볼 수 있도록 라이브러리도 같이 커밋하였다.)
https://github.com/sys010611/YsEngine
기본 구성 요소를 적당히 준비한 후, 게임 엔진의 Inspector 역할을 해줄 GUI가 필요했다.
이를 위해 ImGui라는 라이브러리를 사용했다.
https://github.com/ocornut/imgui
GUI를 위한 라이브러리는 종류가 많은 걸로 알고있는데, 이게 꽤나 괜찮은 것 같다.
설치는 이 동영상을 참고하였다.
https://www.youtube.com/watch?v=VRwhNKoxUtk&t=74s&ab_channel=VictorGordan
코드 상의 세팅 방법도 제작자가 친절하게 만들어놓은 것을 볼 수 있었다.
https://github.com/ocornut/imgui/wiki/Getting-Started#example-if-you-are-using-glfw--openglwebgl
현재 상황에서, ImGui를 통해 조정할 수 있도록 할 것은 모델의 Transformation, 모델의 Material 속성, Directional Light의 방향과 속성이다.
위의 세팅 방법을 참고하여 initialize를 해준 후, 코드를 다음과 같이 짰다.
// ImGui 창을 구성한다.
void compose_imgui_frame(Model* currModel)
{
// Start the Dear ImGui frame
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
// control window
{
// Model
ImGui::Begin("Model");
ImGui::SliderFloat3("Translate", currModel->GetTranslate(), -100.f, 100.f);
ImGui::InputFloat3("Rotate", currModel->GetRotate());
ImGui::SliderFloat3("Scale", currModel->GetScale(), -10.f, 10.f);
ImGui::End();
// Directional Light
ImGui::Begin("DirectionalLight");
ImGui::SliderFloat("Ambient", &dLight_ambient, 0.f, 5.f);
ImGui::SliderFloat("Diffuse", &dLight_diffuse, 0.f, 5.f);
ImGui::End();
// Material
Material* currMaterial = currModel->GetMaterial();
ImGui::Begin("Material");
ImGui::SliderFloat("Specular", &currMaterial->specular, 0.f, 5.f);
ImGui::SliderFloat("Shininess", &currMaterial->shininess, 0.f, 512.f);
ImGui::End();
}
}
모델의 Transformation을 수정하는 창, DirectionalLight를 수정하는 창, 모델의 Material을 수정하는 창을 각각 만드는 코드이다.
이걸 main 함수의 while loop 안에서 호출해주면 된다.
main.cpp
#define STB_IMAGE_IMPLEMENTATION
#include <iostream>
#include <vector>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm\gtc\matrix_transform.hpp>
#include <glm\gtc\type_ptr.hpp>
#include <assimp/Importer.hpp>
#include "Camera.h"
#include "Mesh.h"
#include "Shader.h"
#include "Window.h"
#include "Model.h"
#include "Light.h"
#include "DirectionalLight.h"
#include "PointLight.h"
#include "Texture.h"
#include "Material.h"
#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
#define WIDTH 1600
#define HEIGHT 900
Window* mainWindow;
Camera* camera;
GLfloat deltaTime = 0.f;
GLfloat lastTime = 0.f;
// Vertex Shader
static const char* vShaderPath = "Shaders/vertex.glsl";
// Fragment Shader
static const char* fShaderPath = "Shaders/fragment.glsl";
std::vector<Shader*> shaderList;
Model* model_2B;
DirectionalLight* directionalLight;
// DirectionalLight Intensity
GLfloat dLight_ambient;
GLfloat dLight_diffuse;
// 쉐이더 변수 핸들
GLuint loc_modelMat = 0;
GLuint loc_PVM = 0;
GLuint loc_sampler = 0;
GLuint loc_normalMat = 0;
GLuint loc_eyePos = 0;
// 쉐이더 컴파일
void CreateShader()
{
Shader* shader = new Shader;
shader->CreateFromFiles(vShaderPath, fShaderPath);
shaderList.push_back(shader);
}
void GetShaderHandles()
{
// 핸들 얻어오기
loc_modelMat = shaderList[0]->GetModelMatLoc();
loc_PVM = shaderList[0]->GetPVMLoc();
loc_normalMat = shaderList[0]->GetNormalMatLoc();
loc_eyePos = shaderList[0]->GetEyePosLoc();
}
void compose_imgui_frame(Model* currModel)
{
// Start the Dear ImGui frame
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
// control window
{
// Model
ImGui::Begin("Model");
ImGui::SliderFloat3("Translate", currModel->GetTranslate(), -100.f, 100.f);
ImGui::InputFloat3("Rotate", currModel->GetRotate());
ImGui::SliderFloat3("Scale", currModel->GetScale(), -10.f, 10.f);
ImGui::End();
// Directional Light
ImGui::Begin("DirectionalLight");
ImGui::SliderFloat("Ambient", &dLight_ambient, 0.f, 5.f);
ImGui::SliderFloat("Diffuse", &dLight_diffuse, 0.f, 5.f);
ImGui::End();
// Material
Material* currMaterial = currModel->GetMaterial();
ImGui::Begin("Material");
ImGui::SliderFloat("Specular", &currMaterial->specular, 0.f, 5.f);
ImGui::SliderFloat("Shininess", &currMaterial->shininess, 0.f, 512.f);
ImGui::End();
}
}
glm::mat4 GetModelMat(Model* currModel)
{
GLfloat* translate = currModel->GetTranslate();
GLfloat* rotate = currModel->GetRotate();
GLfloat* scale = currModel->GetScale();
// model Matrix 구성
glm::mat4 T = glm::translate(glm::mat4(1.f), glm::vec3(translate[0], translate[1], translate[2]));
glm::mat4 R = glm::mat4_cast(glm::quat(glm::vec3(rotate[0], rotate[1], rotate[2])));
glm::mat4 S = glm::scale(glm::mat4(1.f), glm::vec3(scale[0], scale[1], scale[2]));
glm::mat4 modelMat = T * R * S;
return modelMat;
}
glm::mat4 GetPVM(glm :: mat4& modelMat)
{
// PVM 구성
glm::mat4 view = camera->calculateViewMatrix();
glm::mat4 projection = glm::perspective(glm::radians(45.0f),
(GLfloat)mainWindow->getBufferWidth() / mainWindow->getBufferHeight(), 0.1f, 100.0f);
glm::mat4 PVM = projection * view * modelMat;
return PVM;
}
glm::mat3 GetNormalMat(glm::mat4& modelMat)
{
return glm::mat3(glm::transpose(glm::inverse(modelMat)));
}
void MoveCamera()
{
camera->keyControl(mainWindow->GetKeys(), deltaTime);
camera->mouseControl(mainWindow->getXChange(), mainWindow->getYChange());
}
int main()
{
// GLFW 초기화
if (!glfwInit())
{
printf("GLFW 초기화 실패\n");
glfwTerminate();
return 1;
}
mainWindow = new Window(WIDTH, HEIGHT);
mainWindow->Initialise();
CreateShader();
GLfloat initialPitch = 0.f;
GLfloat initialYaw = -90.f; // 카메라가 -z축을 보고 있도록
camera = new Camera(glm::vec3(0.f, 0.f, 20.f), glm::vec3(0.f, 1.f, 0.f), initialYaw, initialPitch, 10.f, 0.3f);
dLight_ambient = 0.5f;
dLight_diffuse = 0.5f;
directionalLight = new DirectionalLight
(dLight_ambient, dLight_diffuse,
glm::vec4(1.f, 1.f, 1.f, 1.f),
glm::vec3(1.f, 1.5f, -1.f));
model_2B = new Model();
std::string modelPath = "2b_nier_automata/scene.gltf";
model_2B->LoadModel(modelPath);
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
// Setup Platform/Renderer backends
ImGui_ImplGlfw_InitForOpenGL(mainWindow->GetGLFWwindow(), true); // Second param install_callback=true will install GLFW callbacks and chain to existing ones.
ImGui_ImplOpenGL3_Init();
/////////////////////////
/// while loop
////////////////////////
while (!mainWindow->GetShouldClose())
{
GLfloat now = glfwGetTime();
deltaTime = now - lastTime;
lastTime = now;
// Get + Handle User Input
glfwPollEvents();
const auto& io = ImGui::GetIO();
if (!io.WantCaptureMouse && !io.WantCaptureKeyboard)
MoveCamera();
// Clear the window
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// -------------------------------------------------------------------
shaderList[0]->UseShader();
// 쉐이더 내부 변수 위치들 가지고오기
GetShaderHandles();
glUniform1i(loc_sampler, 0); // sampler를 0번 텍스쳐 유닛과 연결
Model* currModel = model_2B;
// imgui 창 그리기
compose_imgui_frame(currModel);
glm::mat4 modelMat = GetModelMat(currModel);
glm::mat4 PVM = GetPVM(modelMat);
glm::mat4 normalMat = GetNormalMat(modelMat);
glUniformMatrix4fv(loc_modelMat, 1, GL_FALSE, glm::value_ptr(modelMat));
glUniformMatrix4fv(loc_PVM, 1, GL_FALSE, glm::value_ptr(PVM));
glUniformMatrix3fv(loc_normalMat, 1, GL_FALSE, glm::value_ptr(normalMat));
shaderList[0]->UseDirectionalLight(directionalLight, dLight_ambient, dLight_diffuse);
glm::vec4 camPos = glm::vec4(camera->GetPosition(), 1.f);
glm::vec3 camPos_wc = modelMat * camPos;
glUniform3f(loc_eyePos, camPos_wc.x, camPos_wc.y, camPos_wc.z);
shaderList[0]->UseMaterial(model_2B->GetMaterial());
model_2B->RenderModel();
//-------------------------------------------------------------------
glUseProgram(0);
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
mainWindow->swapBuffers();
}
return 0;
}
전체 코드 : https://github.com/sys010611/YsEngine
실행 결과:
'OpenGL > 개발 일지' 카테고리의 다른 글
[OpenGL] 게임 엔진 개발 (6) - Scene Hierarchy (0) | 2024.07.30 |
---|---|
[OpenGL] 게임 엔진 개발 (5) - Skeletal Animation (3) (0) | 2024.07.26 |
[OpenGL] 게임 엔진 개발 (4) - Skeletal Animation (2) (0) | 2024.07.26 |
[OpenGL] 게임 엔진 개발 (3) - Skeletal Animation (1) (1) | 2024.07.26 |
[OpenGL] 게임 엔진 개발 (2) - GUI + Skybox (0) | 2024.07.18 |