응애맘마조

221128 강의 본문

공부/2D강의

221128 강의

TH.Wert 2022. 11. 29. 01:05

오늘은 버퍼(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일)부터 강사님이 바뀝니다. 이전과 다소 내용이 달라질 수 있는점 양해 바랍니다.
읽어주셔서 감사합니다.

'공부 > 2D강의' 카테고리의 다른 글

221130 강의  (2) 2022.12.01
221129 강의  (0) 2022.11.30
221125 강의  (0) 2022.11.26
221124 강의  (0) 2022.11.25
221123 강의  (0) 2022.11.24
Comments