[DirectX] Texture 리소스 사용하기
in Study on WinAPI&DirectX
이미지(Texture) 리소스를 활용하기
CTexture 클래스 구현
// 헤더파일
#pragma once
#include "CRes.h"
#include <DirectXTex\DirectXTex.h>
#ifdef _DEBUG
#pragma comment(lib, "DirectXTex//DirectXTex_debug")
#else
#pragma comment(lib, "DirectXTex//DirectXTex")
#endif
class CTexture :
public CRes
{
private:
ComPtr<ID3D11Texture2D> m_Tex2D;
ComPtr<ID3D11ShaderResourceView> m_SRV;
D3D11_TEXTURE2D_DESC m_Desc;
ScratchImage m_Image;
private:
virtual int Load(const wstring& _strFilePath) override;
public:
virtual int Save(const wstring& _strRelativePath) override;
public:
void UpdateData(int _iRegisterNum);
private:
virtual void UpdateData() override;
public:
CTexture();
~CTexture();
};
// cpp파일
#include "pch.h"
#include "CTexture.h"
#include "CDevice.h"
CTexture::CTexture()
: CRes(RES_TYPE::TEXTURE)
, m_Desc{}
{
}
CTexture::~CTexture()
{
}
void CTexture::UpdateData()
{
}
void CTexture::UpdateData(int _iRegisterNum)
{
CONTEXT->PSSetShaderResources(_iRegisterNum, 1, m_SRV.GetAddressOf());
}
int CTexture::Load(const wstring& _strFilePath)
{
wchar_t szExt[50] = L"";
_wsplitpath_s(_strFilePath.c_str(), nullptr, 0, nullptr, 0, nullptr, 0, szExt, 50);
wstring strExt = szExt;
HRESULT hr = S_OK;
if (L".dds" == strExt || L".DDS" == strExt)
{
// dds, DDS
hr = LoadFromDDSFile(_strFilePath.c_str(), DDS_FLAGS::DDS_FLAGS_NONE, nullptr, m_Image);
}
else if (L".tga" == strExt || L".TGA" == strExt)
{
// tga, TGA
hr = LoadFromTGAFile(_strFilePath.c_str(), nullptr, m_Image);
}
else
{
// png, jpg, jpeg, bmp
hr = LoadFromWICFile(_strFilePath.c_str(), WIC_FLAGS::WIC_FLAGS_NONE, nullptr, m_Image);
}
if (FAILED(hr))
{
MessageBox(nullptr, L"리소스 로딩 실패", L"텍스쳐 로딩 실패", MB_OK);
return E_FAIL;
}
hr = CreateShaderResourceView(DEVICE
, m_Image.GetImages()
, m_Image.GetImageCount()
, m_Image.GetMetadata()
, m_SRV.GetAddressOf());
if (FAILED(hr))
{
MessageBox(nullptr, L"ShaderResourceView 생성 실패", L"텍스쳐 로딩 실패", MB_OK);
return E_FAIL;
}
m_SRV->GetResource((ID3D11Resource**)m_Tex2D.GetAddressOf());
m_Tex2D->GetDesc(&m_Desc);
return S_OK;
}
int CTexture::Save(const wstring& _strRelativePath)
{
return S_OK;
}
- _wsplitpath_s : 경로에 따라서 원하는 문자열 값을 출력해주는 함수
- LoadFromDDSFile : DDS 포맷을 로딩
- LoadFromTGAFile : TGA 포맷을 로딩
LoadFromWICFile : 우리에게 알려진 포맷(png, jpg, bmp 등)을 로딩
- GetMetadata().width : 메모리에 로딩된 값 중 너비를 가져옴
- GetMetadata().hieght : 메모리에 로딩된 값 중 높이를 가져옴
GetImageCount() : 이미지의 개수 가져옴
- CreateShaderResourceView() : 메모리에 로딩된 이미지 데이터를 가져옴. 리턴 값으로 쉐이더 리소스 뷰를 받아옴.
- m_SRV->GetReSource() : 쉐이더 리소스 뷰의 Texture2D를 받아옴.
- m_Tex2D->GetDesc() : Description 정보를 얻어옴.
그리고, Microsoft에서 텍스쳐 관련 라이브러리를 따로 제공한다.
원래 DirectX11에서 초창기에는 d3d11 헤더에서 같이 제공했었는데 지금은 분리되었다.
마이크로소프트 깃허브 (텍스쳐) 에서 제공을 하며, 여기서 프로젝트를 받아서 빌드하면 라이브러리가 생성된다.
ScratchImage
지정된 위치로부터 시스템 메모리로 이미지를 가져오는 역할을 한다.
파일 로딩과 세이브를 위한 클래스의 변화
CResMgr 클래스
// 헤더파일
#pragma once
#include "CSingleton.h"
#include "ptr.h"
#include "CMesh.h"
#include "CTexture.h"
#include "CGraphicsShader.h"
class CResMgr :
public CSingleton<CResMgr>
{
SINGLE(CResMgr)
private:
map<wstring, Ptr<CRes>> m_arrRes[(UINT)RES_TYPE::END];
public:
void init();
private:
void CreateDefaultMesh();
void CreateDefaultGraphicsShader();
void LoadDefaultTexture();
public:
template<typename T>
Ptr<T> FindRes(const wstring& _strKey);
template<typename T>
void AddRes(const wstring& _strKey, Ptr<T>& _Res);
template<typename T>
Ptr<T> Load(const wstring& _strKey, const wstring& _strRelativePath);
};
template<typename T>
RES_TYPE GetResType()
{
const type_info& mesh = typeid(CMesh);
//const type_info& meshdata = typeid(CMeshData);
//const type_info& material = typeid(CMaterial);
const type_info& texture = typeid(CTexture);
//const type_info& sound = typeid(CSound);
//const type_info& prefab = typeid(CPrefab);
const type_info& gs = typeid(CGraphicsShader);
//const type_info& cs = typeid(CComputeShader);
if (typeid(T).hash_code() == mesh.hash_code())
return RES_TYPE::MESH;
if (typeid(T).hash_code() == gs.hash_code())
return RES_TYPE::GRAPHICS_SHADER;
if (typeid(T).hash_code() == texture.hash_code())
return RES_TYPE::TEXTURE;
return RES_TYPE::END;
}
template<typename T>
inline Ptr<T> CResMgr::FindRes(const wstring& _strKey)
{
RES_TYPE type = GetResType<T>();
map<wstring, Ptr<CRes>>::iterator iter = m_arrRes[(UINT)type].find(_strKey);
if (iter == m_arrRes[(UINT)type].end())
return nullptr;
return (T*)iter->second.Get();
}
template<typename T>
inline void CResMgr::AddRes(const wstring& _strKey, Ptr<T>& _Res)
{
// 중복키로 리소스 추가하려는 경우
assert( ! FindRes<T>(_strKey).Get() );
RES_TYPE type = GetResType<T>();
m_arrRes[(UINT)type].insert(make_pair(_strKey, _Res.Get()));
_Res->SetKey(_strKey);
}
template<typename T>
inline Ptr<T> CResMgr::Load(const wstring& _strKey, const wstring& _strRelativePath)
{
Ptr<CRes> pRes = FindRes<T>(_strKey).Get();
// 이미 해당 키로 리소스가 있다면, 반환
if (nullptr != pRes)
return (T*)pRes.Get();
pRes = new T;
pRes->SetKey(_strKey);
pRes->SetRelativePath(_strRelativePath);
wstring strFilePath = CPathMgr::GetInst()->GetContentPath();
strFilePath += _strRelativePath;
if (FAILED(pRes->Load(strFilePath)))
{
assert(nullptr);
}
RES_TYPE type = GetResType<T>();
m_arrRes[(UINT)type].insert(make_pair(_strKey, pRes));
return (T*)pRes.Get();
}
// cpp파일
#include "pch.h"
#include "CResMgr.h"
#include "CPathMgr.h"
CResMgr::CResMgr()
{
}
CResMgr::~CResMgr()
{
}
void CResMgr::init()
{
CreateDefaultMesh();
CreateDefaultGraphicsShader();
LoadDefaultTexture();
}
void CResMgr::CreateDefaultMesh()
{
vector<Vtx> vecVtx;
vector<UINT> vecIdx;
Vtx v;
Ptr<CMesh> pMesh = nullptr;
// =============
// RectMesh 생성
// =============
// 0 --- 1
// | \ |
// 3 --- 2
v.vPos = Vec3(-0.5f, 0.5f, 0.5f);
v.vColor = Vec4(1.f, 0.f, 0.f, 1.f);
v.vUV = Vec2(0.f, 0.f);
vecVtx.push_back(v);
v.vPos = Vec3(0.5f, 0.5f, 0.5f);
v.vColor = Vec4(0.f, 1.f, 0.f, 1.f);
v.vUV = Vec2(1.f, 0.f);
vecVtx.push_back(v);
v.vPos = Vec3(0.5f, -0.5f, 0.5f);
v.vColor = Vec4(0.f, 0.f, 1.f, 1.f);
v.vUV = Vec2(1.f, 1.f);
vecVtx.push_back(v);
v.vPos = Vec3(-0.5f, -0.5f, 0.5f);
v.vColor = Vec4(0.f, 0.f, 0.f, 1.f);
v.vUV = Vec2(0.f, 1.f);
vecVtx.push_back(v);
vecIdx.push_back(0);
vecIdx.push_back(2);
vecIdx.push_back(3);
vecIdx.push_back(0);
vecIdx.push_back(1);
vecIdx.push_back(2);
pMesh = new CMesh;
pMesh->Create(vecVtx.data(), (UINT)vecVtx.size(), vecIdx.data(), (UINT)vecIdx.size());
AddRes(L"RectMesh", pMesh);
}
void CResMgr::CreateDefaultGraphicsShader()
{
Ptr<CGraphicsShader> pShader = nullptr;
// ===========
// Test Shader
// ===========
pShader = new CGraphicsShader;
pShader->SetKey(L"TestShader");
pShader->CreateVertexShader(L"shader\\test.fx", "VS_Test");
pShader->CreatePixelShader(L"shader\\test.fx", "PS_Test");
AddRes(L"TestShader", pShader);
}
void CResMgr::LoadDefaultTexture()
{
/*wstring strContent = CPathMgr::GetInst()->GetContentPath();
wstring strFilePath = strContent + L"texture\\Fighter.bmp";
Ptr<CRes> pTexture = new CTexture;
pTexture->SetKey(L"PlayerTex");
pTexture->SetRelativePath(L"texture\\Fighter.bmp");
pTexture->Load(strFilePath);
m_arrRes[(UINT)RES_TYPE::TEXTURE].insert(make_pair(pTexture->GetKey(), pTexture.Get()));*/
Ptr<CTexture> pTexture = Load<CTexture>(L"PlayerTex", L"texture\\Fighter.bmp");
// t0 바인딩
((CTexture*)pTexture.Get())->UpdateData(0);
}
CRes 클래스
// 헤더파일
#pragma once
#include "CEntity.h"
class CRes :
public CEntity
{
private:
const RES_TYPE m_Type;
int m_iRefCount;
wstring m_strKey;
wstring m_strRelativePath;
private:
void SetKey(const wstring& _strKey) { m_strKey = _strKey; }
void SetRelativePath(const wstring& _strPath) { m_strRelativePath = _strPath; }
void AddRef() { ++m_iRefCount; }
void Release();
// 리소스 바인딩
virtual void UpdateData() = 0;
private:
// 파일로부터 로딩
virtual int Load(const wstring& _strFilePath) = 0;
public:
// 파일로 저장
virtual int Save(const wstring&) = 0;
// 리소스는 Clone 을 구현하지 않는다.
virtual CRes* Clone() { return nullptr; assert(nullptr); }
public:
const wstring& GetKey() { return m_strKey; }
const wstring& GetRelativePath() { return m_strRelativePath; }
public:
CRes(RES_TYPE _type);
virtual ~CRes();
friend class CResMgr;
template<typename T>
friend class Ptr;
};
// cpp파일
#include "pch.h"
#include "CRes.h"
CRes::CRes(RES_TYPE _type)
: m_Type(_type)
, m_iRefCount(0)
{
}
CRes::~CRes()
{
}
void CRes::Release()
{
--m_iRefCount;
if (0 == m_iRefCount)
{
delete this;
}
}
CShader 클래스
// 헤더파일
#pragma once
#include "CRes.h"
class CShader :
public CRes
{
protected:
ComPtr<ID3DBlob> m_ErrBlob;
public:
virtual int Save(const wstring& _strRelativePath) { return S_OK; }
private:
virtual int Load(const wstring& _strFilePath) { return S_OK; }
public:
CShader(RES_TYPE _eType);
~CShader();
};
// cpp파일
#include "pch.h"
#include "CShader.h"
CShader::CShader(RES_TYPE _eType)
: CRes(_eType)
{
}
CShader::~CShader()
{
}
CMesh 클래스
// 헤더파일
#pragma once
#include "CRes.h"
class CMesh
: public CRes
{
private:
ComPtr<ID3D11Buffer> m_VB;
D3D11_BUFFER_DESC m_tVBDesc;
UINT m_VtxCount;
ComPtr<ID3D11Buffer> m_IB;
D3D11_BUFFER_DESC m_tIBDesc;
UINT m_IdxCount;
void* m_pVtxSys;
void* m_pIdxSys;
public:
void Create(void* _VtxSysMem, UINT _iVtxCount, void* _IdxSysMem, UINT _IdxCount);
private:
virtual int Load(const wstring& _strFilePath) { return S_OK; }
public:
virtual int Save(const wstring& _strRelativePath) { return S_OK; }
void render();
private:
virtual void UpdateData() override;
public:
CMesh();
~CMesh();
};
// cpp파일
#include "pch.h"
#include "CMesh.h"
#include "CDevice.h"
CMesh::CMesh()
: CRes(RES_TYPE::MESH)
, m_tVBDesc{}
, m_VtxCount(0)
, m_tIBDesc{}
, m_IdxCount(0)
, m_pVtxSys(nullptr)
, m_pIdxSys(nullptr)
{
}
CMesh::~CMesh()
{
if (nullptr != m_pVtxSys)
delete m_pVtxSys;
if (nullptr != m_pIdxSys)
delete m_pIdxSys;
}
void CMesh::Create(void* _VtxSysMem, UINT _iVtxCount, void* _IdxSysMem, UINT _IdxCount)
{
m_VtxCount = _iVtxCount;
m_IdxCount = _IdxCount;
// SystemMem 데이터 복사
m_pVtxSys = new Vtx[m_VtxCount];
memcpy(m_pVtxSys, _VtxSysMem, sizeof(Vtx) * m_VtxCount);
m_pIdxSys = new UINT[m_IdxCount];
memcpy(m_pIdxSys, _IdxSysMem, sizeof(UINT) * m_IdxCount);
// Vertex 버퍼 생성
m_tVBDesc.BindFlags = D3D11_BIND_FLAG::D3D11_BIND_VERTEX_BUFFER;
m_tVBDesc.CPUAccessFlags = 0;
m_tVBDesc.Usage = D3D11_USAGE_DEFAULT;
m_tVBDesc.ByteWidth = sizeof(Vtx) * m_VtxCount;
D3D11_SUBRESOURCE_DATA tSub = {};
tSub.pSysMem = _VtxSysMem;
if (FAILED(DEVICE->CreateBuffer(&m_tVBDesc, &tSub, m_VB.GetAddressOf())))
{
assert(nullptr);
}
// Index 버퍼 생성
m_tIBDesc.BindFlags = D3D11_BIND_FLAG::D3D11_BIND_INDEX_BUFFER;
m_tIBDesc.CPUAccessFlags = 0;
m_tIBDesc.Usage = D3D11_USAGE_DEFAULT;
m_tIBDesc.ByteWidth = sizeof(UINT) * m_IdxCount;
tSub.pSysMem = _IdxSysMem;
if (FAILED(DEVICE->CreateBuffer(&m_tIBDesc, &tSub, m_IB.GetAddressOf())))
{
assert(nullptr);
}
}
void CMesh::UpdateData()
{
UINT iStride = sizeof(Vtx);
UINT iOffset = 0;
CONTEXT->IASetVertexBuffers(0, 1, m_VB.GetAddressOf(), &iStride, &iOffset);
CONTEXT->IASetIndexBuffer(m_IB.Get(), DXGI_FORMAT_R32_UINT, 0);
}
void CMesh::render()
{
UpdateData();
CONTEXT->DrawIndexed(m_IdxCount, 0, 0);
}
Mesh는 나중에는 파일로부터 로딩을 해야하기 때문에 미리 구현을 해놓는 것이다.
- 파일로부터 로딩하는 기능 : Load(순수가상함수, private 처리)
- 파일로 저장하는 기능 : Save(순수가상함수, public 처리)
CDevice 클래스
// 헤더파일
#pragma once
class CConstBuffer;
class CDevice
: public CSingleton<CDevice>
{
private:
HWND m_hWnd;
ComPtr<ID3D11Device> m_Device; // GPU 메모리 할당
ComPtr<ID3D11DeviceContext> m_Context; // GPU 제어, 렌더링, 동작 수행
ComPtr<IDXGISwapChain> m_SwapChain;
ComPtr<ID3D11Texture2D> m_RTTex;
ComPtr<ID3D11RenderTargetView> m_RTV;
ComPtr<ID3D11Texture2D> m_DSTex;
ComPtr<ID3D11DepthStencilView> m_DSV;
D3D11_VIEWPORT m_ViewPort;
// 렌더타겟 해상도
Vec2 m_vRenderResolution;
CConstBuffer* m_arrConstBuffer[(UINT)CB_TYPE::END];
// ID3D11RenderTargetView
// ID3D11DepthStencilView
// ID3D11ShaderResourceView
// ID3D11UnorderedAccessView
public:
int init(HWND _hWnd, UINT _iWidth, UINT _iHeight);
void ClearTarget(float(&_color)[4]);
void Present() { m_SwapChain->Present(0, 0); }
private:
int CreateSwapChain();
int CreateView();
void CreateConstBuffer();
public:
ID3D11Device* GetDevice() { return m_Device.Get(); }
ID3D11DeviceContext* GetDeviceContext() { return m_Context.Get(); }
CConstBuffer* GetConstBuffer(CB_TYPE _Type) { return m_arrConstBuffer[(UINT)_Type]; }
public:
CDevice();
~CDevice();
};
// cpp파일
#include "pch.h"
#include "CDevice.h"
#include "CEngine.h"
#include "CConstBuffer.h"
CDevice::CDevice()
: m_hWnd(nullptr)
, m_ViewPort{}
, m_arrConstBuffer{}
{
}
CDevice::~CDevice()
{
Safe_Del_Array(m_arrConstBuffer);
}
int CDevice::init(HWND _hWnd, UINT _iWidth, UINT _iHeight)
{
m_hWnd = _hWnd;
m_vRenderResolution = Vec2((float)_iWidth, (float)_iHeight);
int iFlag = 0;
#ifdef _DEBUG
iFlag = D3D11_CREATE_DEVICE_DEBUG;
#endif
D3D_FEATURE_LEVEL eLevel = D3D_FEATURE_LEVEL::D3D_FEATURE_LEVEL_11_0;
if (FAILED(D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE
, nullptr, iFlag
, nullptr, 0
, D3D11_SDK_VERSION
, m_Device.GetAddressOf(), &eLevel
, m_Context.GetAddressOf())))
{
MessageBox(nullptr, L"Device, Context 생성 실패", L"Device 초기화 에러", MB_OK);
return E_FAIL;
}
if (FAILED(CreateSwapChain()))
{
MessageBox(nullptr, L"스왚체인 생성 실패", L"Device 초기화 에러", MB_OK);
return E_FAIL;
}
if (FAILED(CreateView()))
{
MessageBox(nullptr, L"View 생성 실패", L"Device 초기화 에러", MB_OK);
return E_FAIL;
}
// 출력 타겟 설정
m_Context->OMSetRenderTargets(1, m_RTV.GetAddressOf(), m_DSV.Get());
// ViewPort 설정
m_ViewPort.TopLeftX = 0.f;
m_ViewPort.TopLeftY = 0.f;
Vec2 vWindowResol = CEngine::GetInst()->GetWindowResolution();
m_ViewPort.Width = vWindowResol.x;
m_ViewPort.Height = vWindowResol.y;
m_ViewPort.MinDepth = 0.f;
m_ViewPort.MaxDepth = 1.f;
m_Context->RSSetViewports(1, &m_ViewPort);
// 상수버퍼 생성
CreateConstBuffer();
return S_OK; // E_FAIL;
}
int CDevice::CreateSwapChain()
{
// 스왚체인 설정
DXGI_SWAP_CHAIN_DESC tDesc = {};
tDesc.OutputWindow = m_hWnd; // 출력 윈도우
tDesc.Windowed = true; // 창모드, 전체화면 모드
tDesc.BufferCount = 1;
tDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
tDesc.BufferDesc.Width = (UINT)m_vRenderResolution.x;
tDesc.BufferDesc.Height = (UINT)m_vRenderResolution.y;
tDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
tDesc.BufferDesc.RefreshRate.Denominator = 1;
tDesc.BufferDesc.RefreshRate.Numerator = 60;
tDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
tDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER::DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
tDesc.SwapEffect = DXGI_SWAP_EFFECT::DXGI_SWAP_EFFECT_DISCARD;
tDesc.SampleDesc.Count = 1;
tDesc.SampleDesc.Quality = 0;
tDesc.Flags = 0;
// 스왚체인 생성
ComPtr<IDXGIDevice> pDXGIDevice;
ComPtr<IDXGIAdapter> pAdapter;
ComPtr<IDXGIFactory> pFactory;
HRESULT hr = S_OK;
hr = m_Device->QueryInterface(__uuidof(IDXGIDevice), (void**)pDXGIDevice.GetAddressOf());
hr = pDXGIDevice->GetParent(__uuidof(IDXGIAdapter), (void**)pAdapter.GetAddressOf());
hr = pAdapter->GetParent(__uuidof(IDXGIFactory), (void**)pFactory.GetAddressOf());
hr = pFactory->CreateSwapChain(m_Device.Get(), &tDesc, m_SwapChain.GetAddressOf());
return hr;
}
int CDevice::CreateView()
{
m_SwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)m_RTTex.GetAddressOf());
if (FAILED(m_Device->CreateRenderTargetView(m_RTTex.Get(), nullptr, m_RTV.GetAddressOf())))
{
return E_FAIL;
}
// DepthStencil 용도 텍스쳐 생성
D3D11_TEXTURE2D_DESC tDesc = {};
tDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
// 반드시 렌더타겟과 같은 해상도로 설정해야 함
tDesc.Width = (UINT)m_vRenderResolution.x;
tDesc.Height = (UINT)m_vRenderResolution.y;
tDesc.ArraySize = 1;
tDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
tDesc.Usage = D3D11_USAGE_DEFAULT;
tDesc.CPUAccessFlags = 0;
tDesc.MipLevels = 1; // 원본만 생성
tDesc.SampleDesc.Count = 1;
tDesc.SampleDesc.Quality = 0;
if (FAILED(m_Device->CreateTexture2D(&tDesc, nullptr, m_DSTex.GetAddressOf())))
{
return E_FAIL;
}
// DepthStencilView 생성
if (FAILED(m_Device->CreateDepthStencilView(m_DSTex.Get(), nullptr, m_DSV.GetAddressOf())))
{
return E_FAIL;
}
return S_OK;
}
void CDevice::ClearTarget(float(&_color)[4])
{
m_Context->ClearRenderTargetView(m_RTV.Get(), _color);
m_Context->ClearDepthStencilView(m_DSV.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.f, 0);
}
void CDevice::CreateConstBuffer()
{
m_arrConstBuffer[(UINT)CB_TYPE::TRANSFORM] = new CConstBuffer((UINT)CB_TYPE::TRANSFORM);
m_arrConstBuffer[(UINT)CB_TYPE::TRANSFORM]->Create(sizeof(Vec4), 1);
}
쉐이더(test.fx)파일의 변화
VS_IN 에서 float2 vUV 는 정점의 좌표를 받아오는 것.
#ifndef _TEST
#define _TEST
cbuffer TRANSFORM : register(b0)
{
float4 vPlayerPos;
};
Texture2D g_tex_0 : register(t0);
SamplerState g_sam_0 : register(s0);
// VS 입력 구조체
struct VS_IN
{
float3 vPos : POSITION; // semantic
float4 vColor : COLOR;
float2 vUV : TEXCOORD;
};
struct VS_OUT
{
float4 vPosition : SV_Position;
float4 vOutColor : COLOR;
float2 vOutUV : TEXCOORD;
};
// vertex shader
// LocalSpace 물체를 NDC 좌표계로 이동
VS_OUT VS_Test(VS_IN _in)
{
VS_OUT output = (VS_OUT) 0.f;
// 입력으로 들어온 정점좌표에 상수버퍼 값을 더해서 출력
float3 vPos = _in.vPos;
vPos.xy += vPlayerPos.xy;
output.vPosition = float4(vPos, 1.f);
output.vOutColor = _in.vColor;
output.vOutUV = _in.vUV;
return output;
}
// pixel shader
float4 PS_Test(VS_OUT _in) : SV_Target
{
float4 vColor = (float4) 0.f;
// vColor = _in.vOutColor;
vColor = g_tex_0.Sample(g_sam_0, _in.vOutUV);
return vColor;
}
#endif
//struct PS_OUT
//{
// float4 vColor : SV_Target;
//};
//PS_OUT PS_Test(VS_OUT _in)
//{
// PS_OUT output = (PS_OUT) 0.f;
// output.vColor = float4(1.f, 0.f, 0.f, 1.f);
// return output;
//}
따라서 struct.h 파일의 구조체에서도 변화가 생기게 된다.
#pragma once
struct tVertex
{
Vec3 vPos;
Vec4 vColor;
Vec2 vUV;
};
typedef tVertex Vtx;
또한, CGraphicsShader.cpp파일에서도 추가 데이터를 받아와야 하므로 해당 코드를 추가해준다.
#include "pch.h"
#include "CGraphicsShader.h"
#include "CPathMgr.h"
#include "CDevice.h"
CGraphicsShader::CGraphicsShader()
: CShader(RES_TYPE::GRAPHICS_SHADER)
, m_eTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST)
{
}
CGraphicsShader::~CGraphicsShader()
{
}
void CGraphicsShader::CreateVertexShader(const wstring& _strFileName, const string& _strFuncName)
{
// Shader 파일 경로
wstring strShaderFile = CPathMgr::GetInst()->GetContentPath();
strShaderFile += _strFileName;
// VertexShader Compile
if (FAILED(D3DCompileFromFile(strShaderFile.c_str(), nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE
, _strFuncName.c_str(), "vs_5_0", 0, 0, m_VSBlob.GetAddressOf(), m_ErrBlob.GetAddressOf())))
{
MessageBoxA(nullptr, (const char*)m_ErrBlob->GetBufferPointer()
, "Vertex Shader Compile Failed!!", MB_OK);
}
// 컴파일된 객체로 VertexShader, PixelShader 를 만든다.
DEVICE->CreateVertexShader(m_VSBlob->GetBufferPointer(), m_VSBlob->GetBufferSize()
, nullptr, m_VS.GetAddressOf());
// InputLayout 생성
D3D11_INPUT_ELEMENT_DESC LayoutDesc[3] = {};
LayoutDesc[0].SemanticName = "POSITION";
LayoutDesc[0].SemanticIndex = 0;
LayoutDesc[0].AlignedByteOffset = 0;
LayoutDesc[0].Format = DXGI_FORMAT_R32G32B32_FLOAT;
LayoutDesc[0].InputSlot = 0;
LayoutDesc[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
LayoutDesc[0].InstanceDataStepRate = 0;
LayoutDesc[1].SemanticName = "COLOR";
LayoutDesc[1].SemanticIndex = 0;
LayoutDesc[1].AlignedByteOffset = 12;
LayoutDesc[1].Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
LayoutDesc[1].InputSlot = 0;
LayoutDesc[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
LayoutDesc[1].InstanceDataStepRate = 0;
LayoutDesc[2].SemanticName = "TEXCOORD";
LayoutDesc[2].SemanticIndex = 0;
LayoutDesc[2].AlignedByteOffset = 28;
LayoutDesc[2].Format = DXGI_FORMAT_R32G32_FLOAT;
LayoutDesc[2].InputSlot = 0;
LayoutDesc[2].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
LayoutDesc[2].InstanceDataStepRate = 0;
if (FAILED(DEVICE->CreateInputLayout(LayoutDesc, 3
, m_VSBlob->GetBufferPointer(), m_VSBlob->GetBufferSize()
, m_Layout.GetAddressOf())))
{
assert(nullptr);
}
}
void CGraphicsShader::CreatePixelShader(const wstring& _strFileName, const string& _strFuncName)
{
// Shader 파일 경로
wstring strShaderFile = CPathMgr::GetInst()->GetContentPath();
strShaderFile += _strFileName;
// PixelShader Compile
if (FAILED(D3DCompileFromFile(strShaderFile.c_str(), nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE
, _strFuncName.c_str(), "ps_5_0", 0, 0, m_PSBlob.GetAddressOf(), m_ErrBlob.GetAddressOf())))
{
MessageBoxA(nullptr, (const char*)m_ErrBlob->GetBufferPointer()
, "Pixel Shader Compile Failed!!", MB_OK);
}
// 컴파일된 객체로 PixelShader 를 만든다.
DEVICE->CreatePixelShader(m_PSBlob->GetBufferPointer(), m_PSBlob->GetBufferSize()
, nullptr, m_PS.GetAddressOf());
}
void CGraphicsShader::UpdateData()
{
CONTEXT->IASetInputLayout(m_Layout.Get());
CONTEXT->IASetPrimitiveTopology(m_eTopology);
CONTEXT->VSSetShader(m_VS.Get(), nullptr, 0);
//CONTEXT->HSSetShader(m_HS.Get(), nullptr, 0);
//CONTEXT->DSSetShader(m_DS.Get(), nullptr, 0);
//CONTEXT->GSSetShader(m_GS.Get(), nullptr, 0);
CONTEXT->PSSetShader(m_PS.Get(), nullptr, 0);
}
UV의 역할은?
텍스쳐 좌표계에서 쓰는 좌표정보이다.
UV좌표계의 특징
- 좌상단을 (0,0)으로 한다.
- 우하단을 (1,1)으로 한다. (NDC 좌표계랑 헷갈리지 말 것!)
(NDC 좌표계는 가운데가 (0,0)이다.)