응애맘마조
221129 강의 본문
오늘은 수학에 대한 개념을 배웠습니다. 아직 정확하게 다 배우지는 않았지만 3D를 하거나 조금 더 나중에 자세하게 배우게 될 예정입니다. 먼저 수학부터 보겠습니다.
좌표에서 스크린까지 나오는 순서는
로컬 좌표 → 월드 좌표 → 뷰 좌표 → 프로젝션 → 스크린 순서입니다.
로컬 좌표는 플레이어 기준으로 아래쪽 가운데 좌표를 (0, 0)으로 잡은 것입니다. (오른쪽은 Right 벡터, 앞은 Look 벡터(3D에서 사용), 위로 가는게 Up 벡터입니다.)
로컬 좌표를 월드 좌표로 변환하기 위해서는 SRT(Scale, Rotation, Translation)을 계산해야됩니다.
위 그림을 현재의 행렬이라고 나타내겠습니다.
먼저 Scale은 크기를 나타냅니다.
해당 표시는 Scale의 행렬입니다.
Rotation은 회전을 나타냅니다. (행렬은 따로 없습니다.)
Translation은 이동을 나타냅니다.
해당 표시는 Translation의 행렬입니다.
월드 좌표는 말 그대로 월드에 두면 변환이 되는데 위에 작성한 것 처럼 SRT를 계산하면 됩니다.
(만약 월드 좌표에서 로컬 좌표로 돌아가려면 역행렬을 곱하면 됩니다.)
뷰 좌표는 카메라를 기준으로 두는 좌표입니다.
카메라는 영점에 고정 되어있고, 플레이어가 움직여도 움직이는게 아니라 월드가 움직입니다.
프로젝션은 투영을 합니다.
실제로 카메라는 절두체로 비춰지고 있습니다. 원근 투영은카메라에서부터 가까운 곳이 near(크게), far(작게) 보입니다.
직교 투영은 카메라가 멀리 있던 작게 있던 상관없이 오브젝트의 크기가 똑같고 변화하지 않습니다.
스크린은 실행 중인 창의 크기를 임의로 변경해도 비율이 변하지 않습니다.
다음은 삼각함수입니다.
삼각함수를 사용하기 위해선 먼저 직각삼각형이 필요합니다.
위 그림을 직각삼각형이라고 하겠습니다.
표기를 하면 밑변이 x, 높이가 y, 빗변이 z, 축과 이루는 각을 θ(세타)라고 합니다.
각각 길이를 구한다고 하면
X = Z × cosθ
Y= Z × sinθ
Z² = X² + Y²
∴ Z = √X + Y
이렇게 길이를 구할 수 있습니다. 원으로 봤을땐 Z는 반지름이므로 Z 대신에 r이라고 표시 하는 경우도 있습니다.
다음은 오늘 작성한 코드입니다. Render 하위폴더의 Shaders와 Geometries 폴더 안에 작성하였습니다.
//IShader.h
#pragma once
class IShader
{
public:
virtual void Create(const wstring path, const string entryName) = 0;
virtual void Clear() = 0;
virtual void SetShader() = 0;
protected:
void CompileShader(wstring path, string entryName, string profile, ID3D10Blob** blob);
wstring path = L"";
string entryName = "";
private:
void CheckShaderError(HRESULT hr, ID3DBlob* error);
};
IShader.h입니다.
virtual void Clear() = 0은 셰이더 객체를 제거합니다.
void CheckShaderError(HRESULT hr, ID3DBlob* error)는 셰이더 에러가 발생하면 어디서 발생했는지 확인합니다.
//IShader.cpp
#include "Framework.h"
#include "IShader.h"
void IShader::CompileShader(wstring path, string entryName, string profile, ID3D10Blob** blob)
{
ID3DBlob* error = nullptr;
HRESULT hr = D3DCompileFromFile
(
path.c_str(),
nullptr,
nullptr,
entryName.c_str(),
profile.c_str(),
D3DCOMPILE_ENABLE_STRICTNESS,
0,
blob,
&error
);
CheckShaderError(hr, error);
SAFE_RELEASE(error);
}
void IShader::CheckShaderError(HRESULT hr, ID3DBlob* error)
{
if (FAILED(hr))
{
if (error)
{
string str = (const char*)error->GetBufferPointer();
MessageBoxA(handle, str.c_str(), "Shader Error", MB_OK);
}
CHECK(false);
}
}
IShader.cpp입니다. 여기서 중요한 부분은 D3DCompileFromFile입니다. 안에는 HRESULT로 되어 있는데 이름을 보면
HRESULT WINAPI
D3DCompileFromFile(_In_ LPCWSTR pFileName,
_In_reads_opt_(_Inexpressible_(pDefines->Name != NULL)) CONST D3D_SHADER_MACRO* pDefines,
_In_opt_ ID3DInclude* pInclude,
_In_ LPCSTR pEntrypoint,
_In_ LPCSTR pTarget,
_In_ UINT Flags1,
_In_ UINT Flags2,
_Out_ ID3DBlob** ppCode,
_Always_(_Outptr_opt_result_maybenull_) ID3DBlob** ppErrorMsgs);
이렇게 나와 있습니다. 매개변수를 살펴보겠습니다.
pFileName은 컴파일할 파일의 이름입니다.
pDefines는 셰이더 매크로를 정의하는 선택적 배열입니다.
pInclude는 컴파일러가 include 파일을 처리하는데 사용하는 ID3Include 인터페이스에 대한 선택적 포인터입니다.
pEntrypoint는 셰이더 함수의 진입점입니다.
pTarget은 컴파일 할 컴파일러입니다.
Flags1은 or 연산을 사용하여 만드는 컴파일 옵션입니다.
Flags2는 위와 동일합니다.
ppCode는 컴파일된 코드에 엑세스 하는데 사용할 수 있는 ID3Blob를 받습니다.
ppErrorMsgs는 컴파일러 오류 메시지를 엑세스 할 수 있습니다. (오류가 없으면 NULL을 반환합니다.)
다음은 줄임말입니다.
ds : DomainShader
vs : VertexShader
gs : GeometryShader
hs : HullShader
ps : PixelShader
//VertexShader.h
#pragma once
class VertexShader : public IShader
{
public:
~VertexShader();
virtual void Create(const wstring path, const string entryName) override;
virtual void Clear() override;
virtual void SetShader() override;
ID3DBlob* GetBlob() { return blob; }
ID3D11VertexShader* GetResource() { return shader; }
private:
ID3D11VertexShader* shader = nullptr;
ID3DBlob* blob = nullptr;
};
////////////////////////////////////////////////////////////////////////////////////
//VertexShader.cpp
#include "Framework.h"
#include "IShader.h"
#include "VertexShader.h"
VertexShader::~VertexShader()
{
Clear();
}
void VertexShader::Create(const wstring path, const string entryName)
{
this->path = path;
this->entryName = entryName;
CompileShader(path, entryName, "vs_5_0", &blob);
HRESULT hr = DEVICE->CreateVertexShader
(
blob->GetBufferPointer(),
blob->GetBufferSize(),
nullptr,
&shader
);
CHECK(hr);
}
void VertexShader::Clear()
{
SAFE_RELEASE(blob);
SAFE_RELEASE(shader);
}
void VertexShader::SetShader()
{
DC->VSSetShader(shader, nullptr, 0);
}
VertexShader입니다. h와 cpp가 짧아서 한꺼번에 썼습니다. (솔루션에는 따로 분리 되어 있습니다.)
렌더링 파이프라인 할 때 쓰는 두번째 단계입니다.
따로 코드에 대한 설명은 없었습니다.
//PixelShader.h
#pragma once
class PixelShader : public IShader
{
public:
~PixelShader();
virtual void Create(const wstring path, const string entryName) override;
virtual void Clear() override;
virtual void SetShader() override;
ID3DBlob* GetBlob() { return blob; }
ID3D11PixelShader* GetResource() { return shader; }
private:
ID3D11PixelShader* shader = nullptr;
ID3DBlob* blob = nullptr;
};
////////////////////////////////////////////////////////////////////////////////////
//PixelShader.cpp
#include "Framework.h"
#include "IShader.h"
#include "PixelShader.h"
PixelShader::~PixelShader()
{
Clear();
}
void PixelShader::Create(const wstring path, const string entryName)
{
this->path = path;
this->entryName = entryName;
CompileShader(path, entryName, "ps_5_0", &blob);
HRESULT hr = DEVICE->CreatePixelShader
(
blob->GetBufferPointer(),
blob->GetBufferSize(),
nullptr,
&shader
);
CHECK(hr);
}
void PixelShader::Clear()
{
SAFE_RELEASE(blob);
SAFE_RELEASE(shader);
}
void PixelShader::SetShader()
{
DC->PSSetShader(shader, nullptr, 0);
}
PixelShader입니다. h와 cpp가 짧아서 한꺼번에 썼습니다. (솔루션에는 따로 분리 되어 있습니다.)
렌더링 파이프라인 할 때 쓰는 네번째 단계입니다.(3D로 넘어가면 이 전에 다른 과정이 있지만 2D에서는 생략됩니다.)
따로 코드에 대한 설명은 없었습니다.
//TransformBuffer.h
#pragma once
#include "Framework.h"
class TransformBuffer : public ShaderBuffer
{
public:
TransformBuffer()
:ShaderBuffer(&data, sizeof(Data)) {}
struct Data
{
D3DMATRIX world;
D3DMATRIX view;
D3DMATRIX projection;
};
void SetWorld(Matrix world)
{
D3DXMatrixTranspose(&data.world, &world);
}
void SetVP(Matrix view, Matrix projection)
{
D3DXMatrixTranspose(&data.view, &view);
D3DXMatrixTranspose(&data.projection, &projection);
}
private:
Data data;
};
Geometry 폴더 안에 생성한 TransformBuffer.h입니다. (헤더파일만 있습니다.)
여기에서 맨 위에 작성한 월드 좌표와, 뷰 좌표, 프로젝션이 들어갑니다.
코드에 대한 설명은 따로 없었습니다.
//Rect.h
#pragma once
class Rect
{
private:
vector<VertexColor> vertices;
VertexBuffer* vb = nullptr;
vector<UINT> indices;
IndexBuffer* ib = nullptr;
InputLayout* il = nullptr;
VertexShader* vs = nullptr;
PixelShader* ps = nullptr;
Matrix world;
Matrix S, R, T;
Vector3 position;
Vector3 size;
float rotation;
Color color = Color(1, 0, 0, 1);
WorldBuffer* wb = nullptr;
bool bOpen = true;
};
Rect.h입니다. cpp파일은 내일 작성할 것 같지만 여기에서 모니터로 출력이 될 사각형 모양을 그릴 것 같습니다.
어제 잊어버리고 안 썼지만 이번주 토요일(12월 3일)에 AGF 행사에 참여합니다. 2019년도를 마지막으로 코로나 때문에 3년만에 열려서 기대가 큽니다. 후기는 따로 작성 하겠습니다.
읽어주셔서 감사합니다.