이제 이놈의 지겨운 삼각형에서 벗어날 준비가 된 것 같다. 아직 모자란 부분이 많이 보이지만 이것들 하나하나 수정하다간 언제 완성할 지 모르기 때문에...
오늘은 이제 Camera 클래스와 Texture, 그리고 GUI를 위한 imgui를 임포트를 해보려고 한다.
일단 미루고 있었던 회전 행렬에 대해서 먼저 처리를 해야할 것 같아서 간략하게 나마 쿼터니온에 대해 알아봤다. 직접 써보면서 계산하지 않으면 이해가 잘 되지 않을 것 같아 일단 대략 원리만 파악했다.
복소수의 일반형 표현에 삼각함수 버전을 서로 곱한 게 2차원 회전행렬을 나타내는 것과 비슷하게
사원수의 일반형 표현과 삼각함수 표현을 서로 곱한 것이 4차원 회전행렬이 되는 것이다.
이 때 3차원 벡터에 0을 붙여서 회전 대상이 되는 벡터에 대한 사원수를 만들고, 그 사원수의 앞 뒤에 곱해주는 단위 사원수와 켤레 사원수는 원하는 축에 대한 벡터에 삼각함수 표현을 가지고 만들어서 계산해주면 되는 듯 하다.
후... 어쨌든 이 계산을 내가 하지 않고 함수로 계산할 수 있도록 directxmath.h에서 제공해주고 있길래 함수를 사용했다.
대략 이런 식이다.
READONLY_PROPERTY(dx::XMMATRIX, rotationMatrix);
GET(rotationMatrix)
{
return dx::XMMatrixRotationQuaternion(
dx::XMQuaternionRotationRollPitchYaw(
dx::XMConvertToRadians(_rotation.x),
dx::XMConvertToRadians(_rotation.y),
dx::XMConvertToRadians(_rotation.z)));
}
Transform을 수정했으니 이제 Camera Component에서 gameObject의 transform에 접근하여 transform의 값대로 현재 위치(eyePos), 바라보는 위치(현재 위치 벡터 + forward 벡터), 그리고 업 벡터( transform의 up 이용)를 가져와서 viewMatrix를 만들도록 했다.
void Camera::SetView()
{
dx::XMVECTOR eyePos = dx::XMLoadFloat3(&gameObject->transform.position);
view = dx::XMMatrixLookAtLH(eyePos, dx::XMVectorAdd(gameObject->transform.forward, eyePos), gameObject->transform.up);
}
up 벡터나 forward 벡터는 매 프레임마다 transform 컴포넌트의 update 함수에서 갱신중이다. rotationMatrix를 곱해주어 값을 변경하는 식이다.
하지만 XMFloat3와 XMVECTOR는 다른 녀석이기 때문에 Transform에서 넘겨주는 녀석이 XMVECTOR여야 하는지, 아니면 XMFloat3 여야 하는지 아직 모르겠다. 이 부분을 처리해주는 헬퍼 클래스를 만들던가 해야할 것 같다.
다음은 Projection 인데, 이런 식으로 만들었다.
void Camera::SetProjection()
{
if (isPerspective)
{
float fovRad = dx::XMConvertToRadians(fov);
projection = dx::XMMatrixPerspectiveFovLH(fovRad, aspectRatio, nearZ, farZ);
}
else
{
projection = dx::XMMatrixOrthographicLH(renderer.gfx.width, renderer.gfx.height, nearZ, farZ);
}
}
완성된 카메라 클래스의 헤더는 이런 식으로 구성되어 있다.
#include "Component.h"
class Camera : public Component
{
public:
Camera(GameObject* gameObject = nullptr);
protected:
void Update() override;
private:
void SetView();
void SetProjection();
private:
bool isPerspective = true;
float fov = 90.0f;
float aspectRatio = 16.0f/9.0f;
float nearZ = 0.1f;
float farZ = 1000.0f;
dx::XMMATRIX view;
dx::XMMATRIX projection;
};
이제 Renderer 클래스에서 해당 카메라 오브젝트를 main Camera로 두도록 하여 렌더링시에 view, projection 행렬 값을 가져오도록 만들면 된다.
이제 한번 테스트를 해보자. Scene에서 Camera 오브젝트를 다음과 같이 생성하고 renderer의 mainCamera로 설정하도록 했다.
gameObjects.insert({ "Main Camera", std::make_shared<GameObject>("Main Camera") });
auto cam = gameObjects.find("Main Camera")->second;
renderer.mainCamera = &cam->AddComponent<Camera>();
그리고 MeshRenderer에서 DrawCall을 하고 있으므로 MeshRenderer에서 코드를 다음과 같이 수정해주었다.
void MeshRenderer::Update()
{
if (!pMesh) MSG_EXCEPT("Mesh Renderer : Mesh가 없습니다!");
renderer.OnCall(
[this]()
{
//CONSOLE("MESHRENDERER : DRAW START");
pMesh->Bind();
CB_Transformation tr;
tr.transform = gameObject->transform.transformMatrix * renderer.mainCamera->viewMatrix * renderer.mainCamera->projectionMatrix;
renderer.GetCB().data = tr;
renderer.GetCB().Bind();
renderer.GetIL(vs)->Bind();
renderer.GetVS(vs)->Bind();
renderer.GetPS(ps)->Bind();
renderer.gfx.context.Draw(pMesh->verticesCount, 0);
}
);
}
음... 실행은 잘 되는데, 삼각형이 전혀 변할 기미가 없다. 아무리 위치를 뒤로 옮겨도 항상 같은 위치에 삼각형이 위치한다. 카메라를 움직여도 똑같고, 메쉬를 움직여도 똑같다. 왜 일까? 뭐가 문제인거지?
아... 병신...ㅋㅋㅋㅋㅋ
VSOut main(float3 pos : Position, float4 color : Color)
{
VSOut vso;
vso.pos = float4(pos, 1.0f);//mul(float4(pos,1.0f), transform);
vso.color = color;
return vso;
}
행렬 잘 집어넣고는 셰이더에서 주석처리를 해놓고 있으니 당연히 아무것도 안되지...

영상으로 찍기에 대단할 것도 없는 결과물이므로 회전하던 물체를 캡처해서 올린다.
이제 진짜 엔진으로 거듭나기 위해 ImGui 라이브러리를 사용해보도록 하자. 링크는 아래에 있다.
https://github.com/ocornut/imgui
여기 샘플 사진으로 커스텀 엔진 이미지가 올라와 있는데, 내 꺼도 저렇게 될 수 있다면 어떨까 상상하니 몸이 떨리기 시작했다. 정말 '간장'되는 순간이다. skeletal animation에 대한 튜토리얼 레퍼런스도 대충 찾아두었으니 어서 애니메이팅까지 모두 구현한 다음 blender에서 내 캐릭터를 가져다 임포트하여 움직이도록 하고 싶다.
자, 그러면 먼저 파일을 내 엔진 폴더에 넣어야 한다. Clone 을 하면 생기는 폴더에 있는 아래 파일들을 모두 복사하여 imgui 폴더에 넣었다.

다음으로 내 프로그램에서 지원할 api에 맞게 implement 파일을 가져와서 넣어주어야 한다.
examples 폴더를 보면 아래와 같이 지원하는 수많은 api에 대한 implements가 있다.

나는 Windows api와 directx11을 사용할 것이므로 이 2가지에 해당하는 헤더와 소스파일을 가지고 온다.
그리고 프로젝트에 임포트했는데, 임포트만 한 채로 실행하면 에러가 난다. 왜냐하면 Precompiled.h를 내가 사용하고 있으면 임포트한 소스파일들의 설정도 자동으로 미리 컴파일된 헤더 사용이 체크가 되어있기 때문이다.
imgui 파일들을 imgui 폴더에 집어넣었기 때문에 #include "../Precompiled.h"라고 해주면 될 것 같지만 안된다. 미리 컴파일된 헤더의 이름이 설정과 동일해야 하기 때문이다. 그래서 cpp 파일 모두 각각 다음과 같이 설정해주어야 한다.

하지만 다시 생각해보니 얘네가 내 헤더를 쓸 일이 없는데 굳이 해주어야 하나 싶다. 그래서 그냥 사용안함으로 변경해주었다.
그렇게 실행을 해봤더니... 뭔 폰트 아틀라스가 없다면서 실행이 안되었다. 그래서 혹시나 싶어서 폰트가 있는 폴더를 찾아봤더니 misc 폴더 내부에 폰트들이 들어있었다.
misc폴더도 옮겨주었더니 잘 동작한다.
중간에 뭐가 선언이 안되었다느니 뭔 시덥잖은 오류가 있어서 헤더파일을 조금 손보았다. 어쨌든 화면에 출력하는 데 성공했다.

근데 설정하는 과정이 꽤 귀찮다.
이걸 쓰기 위해서 해줘야 하는 과정이 먼저,
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGui::StyleColorsDark();
Context를 생성해주어야 한다는 것이다. 파라미터로 폰트 정보같은 것도 넣을 수 있던데, 어떻게 ttf 파일을 가지고 오는지 나중에 궁금하니 찾아봐야 겠다.
다음으로 윈도우 핸들을 가지고 Implement를 초기화 한다. 윈도우 생성시 HWND 값을 파라미터로 받는다. 근데 ImGui는 스태틱 함수들로 대부분 이뤄져 있기 때문에 여러 개의 윈도우 인스턴스가 존재할 경우 한 곳에서만 만들 수 있다. 이를 해결하기 위해서는 Imgui의 코드를 좀 내 입맛대로 바꿔야 할 필요성이 있다. 이건 필요성이 생기면 추후 하기로 하고 지금은 ImGui를 익히는 데 노력을 하도록 하자.
ImGui_ImplWin32_Init(_handle);
그리고 dx11 초기화 후 device와 context도 정보를 넘겨주어 Init 함수를 호출한다.
ImGui_ImplDX11_Init(pDevice.Get(), pContext.Get());
그리고 win32 implement 파일에 보면 윈도 프로시저 함수가 있는데, 여기에서 인풋 처리를 담당하고 있으므로 어떻게든 이 함수를 호출하던가 안에 있는 내용을 다른데서 실행하던가 해주어야 한다. 일단 App에 있는 함수에서 이 함수를 호출하도록 해주었다.
그 다음은 이제 실제 Update 함수에서 쓰는 것인데, 반드시 DX11_NewFrame을 먼저 호출해주어야 한다. 만약 Win32가 Dx11보다 먼저 호출되면 에러가 난다.
renderer.BeginFrame();
while (!drawCalls.empty())
{
drawCalls.front()();
drawCalls.pop();
}
//ImGUI
ImGui_ImplDX11_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
static bool demo = true;
if (demo)
ImGui::ShowDemoWindow(&demo);
ImGui::Render();
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
renderer.EndFrame();
렌더러 함수의 업데이트 함수의 정의 부분이다. 당연히 UI가 앞에 와야 하므로 모든 drawCall이 실행되고 ImGui 를 그리도록 한다.
이렇게 모든 처리가 끝나면 화면에 드디어 GUI가 나오게 된다. 하지만 문제가 생기는데...
기존 Input 코드가 제대로 동작하지 않는다. 뭐가 문제인지 모르겠지만 화면이 도중에 멈춰버린다. 즉 프리징이 걸려 버린다.
내일은 이걸 해결하고 Input 클래스를 완성하는데 중점을 두도록 하자...ㅠㅠ 후...
'진행과정 기록 > GameEngine' 카테고리의 다른 글
| 20200130 Input class - Wheel, Mouse Raw Input (0) | 2020.01.30 |
|---|---|
| 20200129 Input 클래스 수정 (0) | 2020.01.29 |
| 20200122 Vertex, InputLayout (1) | 2020.01.22 |
| 20200121 enum value를 template parameter로 사용하려면...? (1) | 2020.01.21 |
| 20200117 (0) | 2020.01.17 |