응애맘마조
221128 강의 본문
오늘은 버퍼(Buffer)와 정점(Vertex)에 관한 클래스를 많이 작성했습니다. 코드에 대한 설명은 따로 없었기에 설명 해주시는대로 작성하겠습니다. (아마도 내일이나 늦어도 수요일에는 해주실 것 같습니다.)
#include "Renders/Resources/VertexTypes.h"
#include "Renders/Resources/ShaderBuffer.h"
#include "Renders/Resources/GlobalBuffer.h"
#include "Renders/IA/VertexBuffer.h"
#include "Renders/IA/IndexBuffer.h"
#include "Renders/IA/InputLayout.h"
먼저 Framework.h입니다. 금요일에 올린 파일에서 define 밑에 추가 되었습니다.
작성할 이름은 VertexTypes, ShadeBuffer, GlobalBuffer, VertexBuffer, IndexBuffer, InputLayout이 되겠습니다.
//VertexTypes.h
#pragma once
struct VertexColor
{
VertexColor() : position(0, 0, 0), color(0, 0, 0, 0) {}
VertexColor(Vector3 position, Color color)
:position(position), color(color) {};
Vector3 position;
Color color;
static D3D11_INPUT_ELEMENT_DESC descs[];
static const uint count = 2;
};
struct VertexTexture
{
VertexTexture() : position(0, 0, 0), uv(0, 0) {}
VertexTexture(Vector3 position, Vector2 uv)
:position(position), uv(uv) {}
Vector3 position;
Vector2 uv;
static D3D11_INPUT_ELEMENT_DESC deces[];
static const uint count = 2;
};
VertexType.h입니다. 여기서부터 위치와 색깔이 지정이 될 곳입니다.
Vector2 uv는 텍스쳐 좌표입니다.
//VertexTypes.cpp
#include "Framework.h"
#include "VertexTypes.h"
D3D11_INPUT_ELEMENT_DESC VertexColor::descs[]
{
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,D3D11_INPUT_PER_VERTEX_DATA, 0},
{"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0}
};
D3D11_INPUT_ELEMENT_DESC VertexTexture::deces[]
{
{"POSITION", 0,DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,D3D11_INPUT_PER_VERTEX_DATA, 0},
{"TEXCOORD", 0,DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0}
};
VertexType.cpp입니다. 여기서 중요한 것은 D3D11_INPUT_ELEMENT_DESC입니다. 이 구조체 안을 들어가보면
typedef struct D3D11_INPUT_ELEMENT_DESC
{
LPCSTR SemanticName;
UINT SemanticIndex;
DXGI_FORMAT Format;
UINT InputSlot;
UINT AlignedByteOffset;
D3D11_INPUT_CLASSIFICATION InputSlotClass;
UINT InstanceDataStepRate;
} D3D11_INPUT_ELEMENT_DESC;
이렇게 나오게됩니다.
LPCSTR SemanticName은 문자열 이름, 정점 셰이더에서 쓰이는 유효한 변수 이름입니다.
UINT SemanticIndex는 인덱스, 텍스쳐 좌표의 인덱스를 구분하는데 사용합니다.
DXGI_FORMAT Format은 정점의 자료형식을 구분합니다.
UINT InputSlot은 해당 자료가 공급될 정점 버퍼 슬롯의 번호입니다.
UINT AlignedByteOffset은 정점 위치 offset입니다.
D3D11_INPUT_CLASSIFICATION InputSlotClass, UINT InstanceDataStepRate는 인스턴싱에 쓰이는 부분입니다.
hlsl : High-Level Shader Language의 약자로 HLSL은 DirectX에서 프로그래밍 가능한 셰이더와 함께 사용하는 C와 유사한 상위 수준 셰이더 언어입니다. 자세한 내용은 https://learn.microsoft.com/ko-kr/windows/win32/direct3dhlsl/dx-graphics-hlsl
HLSL(High-Level Shader Language) - Win32 apps
HLSL은 DirectX에서 프로그래밍 가능한 셰이더와 함께 사용하는 C와 유사한 상위 수준 셰이더 언어입니다.
learn.microsoft.com
여기에서 확인할 수 있습니다.
//ShaderBuffer.h
#pragma once
#include "Framework.h"
class ShaderBuffer
{
public:
void SetVSBuffer(uint slot)
{
MapData();
DC->VSSetConstantBuffers(slot, 1, &buffer);
}
void SetPSBuffer(uint slot)
{
MapData();
DC->PSGetConstantBuffers(slot, 1, &buffer);
}
protected:
ShaderBuffer(void* data, uint dataSize)
:data(data),dataSize(dataSize)
{
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.ByteWidth = dataSize;
desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
HRESULT hr = DEVICE->CreateBuffer(&desc, nullptr, &buffer);
CHECK(hr);
}
virtual ~ShaderBuffer() { SAFE_RELEASE(buffer); }
private:
void MapData()
{
D3D11_MAPPED_SUBRESOURCE subResource;
HRESULT hr = DC->Map
(
buffer, 0, D3D11_MAP_WRITE_DISCARD,
0, &subResource
);
memcpy(subResource.pData, data, dataSize);
DC->Unmap(buffer, 0);
}
private:
D3D11_BUFFER_DESC desc = { 0 };
ID3D11Buffer* buffer = nullptr;
void* data = nullptr;
uint dataSize = 0;
};
ShadeBuffer.h입니다. (헤더파일만 있습니다.) 여기서 상수 버퍼인 Map과 UnMap을 사용하게 됩니다.
상수 버퍼 : 변하지 않는 버퍼입니다. 만약 변할 필요가 있으면 Map과 Unmap을 사용해야됩니다.
(강의 내용)
Map : 뚜껑 따는 것
Unmap : 뚜껑 덮는 것
이렇게 이해하기 쉽게 말씀 해주셨지만 저는 따로 정의를 해보겠습니다.
먼저 Map을 하여 상수버퍼의 리소스 주소를 먼저 얻습니다. 그 다음 memcpy를 해서 type 변수에 복사합니다. 복사를 다 완료 했으면 Unmap을 하여 해제해줘야 합니다.
//GlobalBuffer.h
#pragma once
#include "Framework.h"
class WorldBuffer : public ShaderBuffer
{
public:
WorldBuffer() : ShaderBuffer(&data, sizeof(Data))
{
D3DXMatrixIdentity(&data.world);
}
void SetWorld(Matrix world)
{
D3DXMatrixTranspose(&data.world, &world);
}
struct Data
{
Matrix world;
};
private:
Data data;
};
class VPBuffer : public ShaderBuffer
{
public:
VPBuffer() : ShaderBuffer(&data, sizeof(Data))
{
D3DXMatrixIdentity(&data.view);
D3DXMatrixIdentity(&data.projection);
}
void SetView(Matrix view)
{
D3DXMatrixTranspose(&data.view, &view);
}
void Setprog(Matrix projection)
{
D3DXMatrixTranspose(&data.projection, &projection);
}
struct Data
{
Matrix view;
Matrix projection;
};
private:
Data data;
};
GlobalBuffer.h입니다. (헤더파일만 있습니다.) 강사님은 이걸 가장 많이 보게 될 것이라고 하셨습니다.
여기서 중요한건 D3DXMatrixIdentity, D3DXMatrixTranspose입니다.
Matrix는 행렬이라는 뜻을 갖고 있습니다.
D3DXMatrixIdentity는 항등행렬입니다.
주대각선의 원소가 모두 1이며 나머지 원소는 모두 0인 정사각 행렬입니다. (단위행렬이라고도 합니다.)
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
이런 행렬의 모습이 나옵니다. 자세한 내용은 https://ko.wikipedia.org/wiki/%EB%8B%A8%EC%9C%84%ED%96%89%EB%A0%AC
단위행렬 - 위키백과, 우리 모두의 백과사전
위키백과, 우리 모두의 백과사전. 선형대수학에서 단위 행렬(영어: unit matrix) 또는 항등 행렬(영어: identity matrix)은 주대각선의 원소가 모두 1이며 나머지 원소는 모두 0인 정사각 행렬이다.[1]:100
ko.wikipedia.org
여기에서 확인할 수 있습니다.
D3DXMatrixTranspose는 전치행렬입니다.
주대각선을 축으로 하는 반사 대칭을 가하여 얻는 행렬입니다.
1 2 3 4 1 5 8 11
5 1 6 7 → 2 1 9 12
8 9 1 10 → 3 6 1 13
11 12 13 1 4 7 10 1
이런 행렬의 모습이 나옵니다. 자세한 내용은 https://ko.wikipedia.org/wiki/%EC%A0%84%EC%B9%98%ED%96%89%EB%A0%AC
전치행렬 - 위키백과, 우리 모두의 백과사전
위키백과, 우리 모두의 백과사전. 어떤 행렬의 전치 행렬은 그 행렬을 주대각선을 기준으로 하여 뒤집어 얻을 수 있다. 똑같은 방법으로 한 번 더 뒤집으면 원래 행렬로 돌아온다. 선형대수학에
ko.wikipedia.org
여기에서 확인할 수 있습니다.
//VertexBuffer.cpp
#include "Framework.h"
#include "VertexBuffer.h"
VertexBuffer::~VertexBuffer()
{
SAFE_RELEASE(buffer);
}
void VertexBuffer::SetIA()
{
DC->IASetVertexBuffers(0, 1, &buffer, &stride, &offset);
}
VertexBuffer.cpp입니다. 헤더파일은 25일에 작성 했으므로 생략하겠습니다.
여기에서 stride와 offset에 대해서 배웠습니다.
stride는 한 정점 정보의 메모리 사이즈입니다.
offset는 각 정점 정보 사이의 메모리 간격 입니다.
//IndexBuffer.h
#pragma once
class IndexBuffer
{
public:
~IndexBuffer();
void Create(const vector<uint>& indices, const D3D11_USAGE& usage = D3D11_USAGE_DEFAULT);
ID3D11Buffer* GetResources() { return buffer; }
uint GetStride() { return stride; }
uint GetOffset() { return offset; }
uint GetCount() { return count; }
void SetIA();
private:
ID3D11Buffer* buffer = nullptr;
uint stride = 0;
uint offset = 0;
uint count = 0;
};
IndexBuffer.h입니다.
private는 비트연산을 하기 때문에 0으로 초기화를 했습니다.
여기서 IndexBuffer에 대해 배웠습니다.
IndexBuffer는 Index 정보를 저장하는 버퍼입니다.
그리는 순서를 저장하지 않고 그리면 중복되는 점을 또 저장해야 해서 비효율적이 되기 때문에 사용합니다.
//IndexBuffer.cpp
#include "Framework.h"
#include "IndexBuffer.h"
IndexBuffer::~IndexBuffer()
{
SAFE_RELEASE(buffer);
}
void IndexBuffer::Create(const vector<uint>& indices, const D3D11_USAGE& usage)
{
stride = sizeof(uint);
count = indices.size();
D3D11_BUFFER_DESC desc;
desc.Usage = usage;
desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
desc.ByteWidth = stride * count;
switch (usage)
{
case D3D11_USAGE_DEFAULT:
case D3D11_USAGE_IMMUTABLE:
break;
case D3D11_USAGE_DYNAMIC:
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
break;
case D3D11_USAGE_STAGING:
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE | D3D11_CPU_ACCESS_READ;
break;
}
D3D11_SUBRESOURCE_DATA subData;
ZeroMemory(&subData, sizeof(D3D11_SUBRESOURCE_DATA));
subData.pSysMem = indices.data();
HRESULT hr = DEVICE->CreateBuffer(&desc, &subData, &buffer);
CHECK(hr);
}
void IndexBuffer::SetIA()
{
DC->IASetIndexBuffer(buffer, DXGI_FORMAT_R32_UINT, offset);
}
IndexBuffer.cpp입니다.
D3D11_BUFFER_DESC desc는 모든 버퍼를 이것으로 만듭니다.
desc.BindFlags = D3D11_BIND_INDEX_BUFFER는 바인드 플래그에서 어떤 버퍼를 사용하는지 정의합니다.
//InputLayout.h
#pragma once
class InputLayout
{
public:
~InputLayout();
void Create(D3D11_INPUT_ELEMENT_DESC* desc, uint count, ID3DBlob* blob);
void SetIA();
private:
ID3D11InputLayout* inputLayout = nullptr;
};
InputLayout.h입니다. 여기에서 중요한 것을 말하겠습니다.
InputLayout은 꼭짓점 버퍼 데이터를 IA 단계로 스트리밍 하는 방법을 정의합니다.
D3D11_INPUT_ELEMENT_DESC은 정점 구도체를 정의했다면, 그 구조체의 각 성분이 어떤 용도인지 알려줘야 하는데 그 수단이 해당 구조체입니다. (정점이 2개면 desc 원소도 2개여야 합니다.)
Blob(Binary large object data, 대용량 바이너리 데이터)은 셰이더 바이트 코드를 관리합니다.
//InputLayout.cpp
#include "Framework.h"
#include "InputLayout.h"
InputLayout::~InputLayout()
{
SAFE_RELEASE(inputLayout);
}
void InputLayout::Create(D3D11_INPUT_ELEMENT_DESC* desc, uint count, ID3DBlob* blob)
{
if (!desc || !count || !blob)
CHECK(false);
HRESULT hr = DEVICE->CreateInputLayout
(
desc,count,
blob->GetBufferPointer(),
blob->GetBufferSize(),
&inputLayout
);
}
void InputLayout::SetIA()
{
DC->IASetInputLayout(inputLayout);
}
InputLayout.cpp입니다. 여기에서는 따로 중요하게 다룬 것이 없어서 넘어가겠습니다.
다음주 월요일(12월 5일)부터 강사님이 바뀝니다. 이전과 다소 내용이 달라질 수 있는점 양해 바랍니다.
읽어주셔서 감사합니다.