이번엔, 물체 표면의 색상정보를 이미지 파일에서 읽어와서 입히는 텍스쳐링을 추가하였다.
먼저, 이미지를 로드하는 기능이 필요한데 이 기능은 오픈소스로 제공되고 있는 stb 라이브러리를 활용하였다.
std::string Path = "../Texture/";
Path += _TextureName;
int Width = 0;
int Height = 0;
int Channels = 0;
unsigned char* LoadedImage = stbi_load(Path.c_str(), &Width, &Height, &Channels, 0);
if (LoadedImage == nullptr)
{
std::cout << "Image Load Failed" << std::endl;
return FALSE;
}
stbi_load는 이미지의 정보를 읽어오는 함수이다.
해당 함수의 첫번째 인자는 파일의 이름인데, 상대경로를 통해 파일을 인식하기 때문에 경로 설정을 잘 해야한다.
본인은 프로젝트 폴더에 텍스쳐 파일을 따로 모아둘 Texture 폴더를 생성하였고, 해당 폴더에서 텍스쳐를 읽어오도록 경로를 ../Texture/(파일이름.확장자) 의 형식으로 설정하였다.
std::vector<uint8_t> Image;
Image.resize(Width * Height * Channels);
memcpy(Image.data(), LoadedImage, Image.size() * sizeof(uint8_t));
이후, 로드한 이미지의 색상정보를 벡터에 복사해주었다.
(굳이 복사하지 않아도 기능작동은 잘 된다. 다만 본인은 벡터가 편해서 옮겨주었다. 최적화적인 측면에선 안하는게 좋을 듯 하다.)
Microsoft::WRL::ComPtr<ID3D11Texture2D> Texture;
Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> SRV;
D3D11_TEXTURE2D_DESC TexDesc = {};
TexDesc.Width = Width;
TexDesc.Height = Height;
TexDesc.MipLevels = TexDesc.ArraySize = 1;
TexDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
TexDesc.SampleDesc.Count = 1;
TexDesc.Usage = D3D11_USAGE_IMMUTABLE;
TexDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
D3D11_SUBRESOURCE_DATA InitData;
InitData.pSysMem = Image.data();
InitData.SysMemPitch = TexDesc.Width * sizeof(uint8_t) * Channels;
HRESULT Result = EngineBase::GetInstance().GetDevice()->CreateTexture2D(&TexDesc, &InitData, Texture.GetAddressOf());
if (Result != S_OK)
{
std::cout << "CreateTexture2D failed " << std::endl;
return FALSE;
}
Result = EngineBase::GetInstance().GetDevice()->CreateShaderResourceView(Texture.Get(), nullptr, SRV.GetAddressOf());
if (Result != S_OK)
{
std::cout << "CreateTexture2D failed " << std::endl;
return FALSE;
}
이후, Texture2D와 ShaderResourcesView를 만들어주었다.
TextureData NewTextureData;
NewTextureData.Texture = Texture;
NewTextureData.ShaderResourceView = SRV;
Textures.insert({ _TextureName, NewTextureData });
stbi_image_free(LoadedImage);
return TRUE;
이후, EngineBase에 텍스쳐 정보를 저장하기 위해 선언해둔 자료구조에 데이터를 저장한 뒤, 로드한 이미지의 메모리를 해제해주었다.
자료구조는 아래와 같다.
struct TextureData
{
Microsoft::WRL::ComPtr<ID3D11Texture2D> Texture;
Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> ShaderResourceView;
};
std::unordered_map<std::string, TextureData> Textures;
텍스쳐를 만들었으니, 샘플러도 만들어야 한다.
BOOL EngineBase::CreateSampler()
{
{
Microsoft::WRL::ComPtr<ID3D11SamplerState> NewSampler;
// Texture sampler 만들기
D3D11_SAMPLER_DESC SamplerDesc;
ZeroMemory(&SamplerDesc, sizeof(SamplerDesc));
SamplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
SamplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
SamplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
SamplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
SamplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
SamplerDesc.MinLOD = 0;
SamplerDesc.MaxLOD = D3D11_FLOAT32_MAX;
// Create the Sample State
HRESULT Result = EngineBase::GetInstance().GetDevice()->CreateSamplerState(&SamplerDesc, NewSampler.GetAddressOf());
if (Result != S_OK)
{
std::cout << "CreateSamplerState failed : LINEARWRAP" << std::endl;
return FALSE;
}
Samplers.insert({ "LINEARWRAP", NewSampler });
}
{
Microsoft::WRL::ComPtr<ID3D11SamplerState> NewSampler;
// Texture sampler 만들기
D3D11_SAMPLER_DESC SamplerDesc;
ZeroMemory(&SamplerDesc, sizeof(SamplerDesc));
SamplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
SamplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
SamplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
SamplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
SamplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
SamplerDesc.MinLOD = 0;
SamplerDesc.MaxLOD = D3D11_FLOAT32_MAX;
// Create the Sample State
HRESULT Result = EngineBase::GetInstance().GetDevice()->CreateSamplerState(&SamplerDesc, NewSampler.GetAddressOf());
if (Result != S_OK)
{
std::cout << "CreateSamplerState failed : LINEARCLAMP" << std::endl;
return FALSE;
}
Samplers.insert({ "LINEARCLAMP", NewSampler });
}
return TRUE;
}
샘플러는 일단 2개의 종류를 만들어두었다.
Filter는 LINEAR인 상태에서 WRAP인 샘플러와 CLAMP인 샘플러 2개를 만들었다.
이 역시 EngineBase의 자료구조에 저장해두었다.
void SetTexture(const std::string& _TextureName)
{
TextureName = _TextureName;
}
void SetSampler(const std::string& _SamplerName)
{
SamplerName = _SamplerName;
}
렌더러에선 위처럼 샘플러와 텍스쳐의 이름만 저장하도록 하였다.
Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> SRV = EngineBase::GetInstance().GetTextureData(TextureName).ShaderResourceView;
Microsoft::WRL::ComPtr<ID3D11SamplerState> Sampler = EngineBase::GetInstance().GetSampler(SamplerName);
EngineBase::GetInstance().GetContext()->PSSetShaderResources(0, 1, SRV.GetAddressOf());
EngineBase::GetInstance().GetContext()->PSSetSamplers(0, 1, Sampler.GetAddressOf());
이후, Render함수에 위처럼 샘플러와 텍스쳐를 세팅하는 코드를 추가해주었다.
여기서, 하나의 쉐이더에 텍스쳐와 샘플러를 여러개 세팅하는 상황이 발생할 수도 있는데 일단은 1개만 세팅한다고 가정하고 구현하였다.
처음부터 염두에 두고 코드를 구성하는 것이 좋은 방법이지만, 본인은 수정하는 과정에서도 배울 것이 있다고 생각하여서 추후 다시 수정하는 과정을 거치려고 한다.
cbuffer UV : register(b1)
{
float4 UV;
};
struct PixelShaderInput
{
float4 pos : SV_POSITION;
float3 color : COLOR;
float2 TexCoord : TEXCOORD;
};
Texture2D DiffuseTexture : register(t0);
SamplerState Sampler : register(s0);
float4 main(PixelShaderInput _Input) : SV_TARGET
{
float4 Color = DiffuseTexture.Sample(Sampler, _Input.TexCoord);
return Color;
}
픽셀 쉐이더에 텍스쳐와 샘플러를 추가한 뒤, 색상을 샘플링해주었다.
결과는 아래와 같이 정상적으로 잘 작동되는 것을 확인할 수 있다.
'프로젝트 > Direct X 그래픽스' 카테고리의 다른 글
프로젝트 : DirectX를 활용한 그래픽스 (12 - 조명 추가 (1) ) (0) | 2024.05.08 |
---|---|
프로젝트 : DirectX를 활용한 그래픽스 (11 - IMGUI, 델타타임 추가) (0) | 2024.05.07 |
프로젝트 : DirectX를 활용한 그래픽스 (9 - 상수버퍼 구조 변경) (0) | 2024.05.06 |
프로젝트 : DirectX를 활용한 그래픽스 (8 - 텍스쳐 좌표 추가) (0) | 2024.05.04 |
프로젝트 : DirectX를 활용한 그래픽스 (7 - 렌더링) (1) | 2024.05.02 |