DirectX

36. DirectX11 3D 입문_텍스처

devRiripong 2024. 2. 6.
반응형

CreateQuad로 사각형 만드는 거 까지 했는데

텍스처를 붙여서 UV 좌표를 개입시켜서 뭔가를 할 것이다.

 

모양을 여러 개 만들어 볼 것이다.

대표적으로 지원해야 되는 모양이 무엇이 있는지를 생각해 보면

사각형 + 큐브 + 구 + 그리드 + 실린더

이 정도가 일반적이다. 실린더는 공식이 복잡하니 스킵하고 나머지 3 총사를 구현해 본다.

 

VertexData에 필요한 걸 이것저것 만들고 나중에 정리하는 식으로 구현을 해보도록 한다.

vertexData.h에서 모형에 대한 것만 신경을 써 보자. VertexTextureData라고 uv까지 고려한 것을 넣어 보도록 할 것인데

struct VertexTextureData
{
	Vec3 position = { 0, 0, 0 };
	Vec2 uv = { 0, 0 };
};

 

GeometryHelper.h의 CreateQuad를 아래에 복제를 하고,

VertexColorData를 VertexTextureData로 바꿔주고 4개로 만들어 준다.

#pragma once
#include "Geometry.h"
#include "VertexData.h"

class GeometryHelper
{
public: 
	static void CreateQuad(shared_ptr<Geometry<VertexColorData>> geometry, Color color); 

	static void CreateQuad(shared_ptr<Geometry<VertexTextureData>> geometry); 
	static void CreateCube(shared_ptr<Geometry<VertexTextureData>> geometry); 
	static void CreateSphere(shared_ptr<Geometry<VertexTextureData>> geometry); 
	static void CreateGrid(shared_ptr<Geometry<VertexTextureData>> geometry); 
};

4개의 함수를 구현한다.

 

1. color대신 uv를 넣은 VertexTextureData버전 CreateQuad 구현하기

우선 CreateQuad부터 구현한다.

void GeometryHelper::CreateQuad(shared_ptr<Geometry<VertexTextureData>> geometry)
{
	vector<VertexTextureData> vtx;
	vtx.resize(4);

	vtx[0].position = Vec3(-0.5f, -0.5f, 0.f);
	vtx[0].uv = Vec2(0.f, 1.f); 
	vtx[1].position = Vec3(-0.5f, 0.5f, 0.f);
	vtx[1].uv = Vec2(0.f, 0.f);
	vtx[2].position = Vec3(0.5f, -0.5f, 0.f);
	vtx[2].uv = Vec2(1.f, 1.f);
	vtx[3].position = Vec3(0.5f, 0.5f, 0.f);
	vtx[3].uv = Vec2(1.f, 0.f);
	geometry->SetVertices(vtx);

	vector<uint32> idx = { 0, 1, 2, 2, 1, 3 };
	geometry->SetIndices(idx);
}

 

2. Texure를 UV 좌표에 따라 샘플링해서 반환하게 Texture.fx 수정하기

04. World.fx의 이름을 04. Texture.fx로 바꾼다.

그리고 04. Texture.fx 를 개선시킨다.

 

float4 color : COLOR; 이 코드를 float4 uv : TEXCOORD; 로 수정한다.

VS에서도 color 대신 uv를 output에 넣어 return 하게 수정한다.

 

SamplerState Sampler0; 

을 선언한다.

 

그리고 윗부분에

Texture2D Texture0; 

을 선언한다.

여기에 어떤 이미지를 로드해서 꽂아주게 될 것이다.

 

PS에서

float4 PS(VertexOutput input) : SV_TARGET   
{ 
    return Texture0.Sample(Sampler0, input.uv);
}

입력된 UV 좌표를 사용하여 주어진 텍스처에서 색상 값을 샘플링하고, 그 색상 값을 픽셀의 최종 색상으로 반환합니다. 이 과정을 통해 텍스처 매핑이 수행되어, 3D 객체의 표면에 이미지나 패턴이 적용됩니다.

 

 

WireFrame 모드는 일단 내버려두도록 하고 이렇게만 일단 만들어 보도록 한다.

 

지금 두 가지를 동시에 하고 있다. 모형을 만드는 거랑 uv좌표를 이용해서 텍스쳐를 매핑하는 걸 1+1으로 하고 있다.

 

Engine 이 통과가 되는지 빌드를 해본다.

 

일단 Texure를 CreateQuad에서 그려주고 나머지 Cube, Sphere, Grid는 어떻게 할지 고려해 본다.

 

3. TextureDemo 클래스에서 셰이더 세팅하고, 셰이더에 texture 파일 연결해서 이미지 띄우기

1) TextureDemo 클래스 생성

04. CameraDemo클래스를 복제해서 이름을 05. TextureDemo라고 한다.

그리고 솔루션 탐색기의 Client/Game필터에 넣는다.

 

05. TextureDemo의 내용을 TextureDemo에 맞게 수정한다.

TextureDemo는 Quad에다 Texture하나를 그려줘서 띄울 수 있으면 완료가 된다. 사각형을 그리고 UV 매핑을 이용해서 그려주는 걸 했었는데 그 부분을 한 번 더 만들어 보도록 한다.

 

2) Main에서 TextureDemo 세팅

Main.cpp에서도

#include "05. TextureDemo.h"

를 추가하고,

WinMain에서

desc.app = make_shared<TextureDemo>();

이렇게 해준다.

 

3) TextureDemo::Init에서 _shader에  04. Texture.fx 세팅

이제 Texture를 로드해야 한다.

TextureDemo::Init에서

void TextureDemo::Init()
{
	_shader = make_shared<Shader>(L"04. Texture.fx");

    // Object
    _geometry = make_shared<Geometry<VertexColorData>>(); 
    GeometryHelper::CreateQuad(_geometry, Color(0.f, 1.f, 0.f, 1.f)); 
    _vertexBuffer = make_shared<VertexBuffer>(); 
    _vertexBuffer->Create(_geometry->GetVertices()); 
    _indexBuffer = make_shared<IndexBuffer>(); 
    _indexBuffer->Create(_geometry->GetIndices());

이렇게 하고,

 

4)TextureDemo::Init에서 _geometry에 VertexTextureData세팅

Object를 만들고, 거기에 UV매핑을 해줘야 한다.

그러기 해서는 VertexColorData가 아니라 VertexTextureData를 make_shared<Geometry<>>()의 <>에 넣어줘야 한다.

CreateQuad에서 Color를 빼면 VertexTextureData 버전으로 실행되게 된다. 

 

TextureDemo.h에서도 _geometry의 타입에 VertexColorData가 아닌 VertexTextureData를 넣어준다.

	shared_ptr<Geometry<VertexTextureData>> _geometry;

 

이걸 이용해 VertexBuffer, IndexBuffer를 채워줘서 물체가 만들어지게 될 것이다.

void TextureDemo::Init()
{
	_shader = make_shared<Shader>(L"04. Texture.fx");

	// Object
	_geometry = make_shared<Geometry<VertexTextureData>>(); 
	GeometryHelper::CreateQuad(_geometry); 
	_vertexBuffer = make_shared<VertexBuffer>(); 
	_vertexBuffer->Create(_geometry->GetVertices()); 
	_indexBuffer = make_shared<IndexBuffer>(); 
	_indexBuffer->Create(_geometry->GetIndices());

지금은 따로 관리하지만

	// Object
	shared_ptr<Geometry<VertexTextureData>> _geometry;
	shared_ptr<VertexBuffer> _vertexBuffer; 
	shared_ptr<IndexBuffer> _indexBuffer;

이 셋이 합쳐져서 하나의 클래스로 관리될 예정이다.

이걸 뭉친 게 Mesh라는 개념이었다.

 

5) TextureDemo::Init에서 Texture 로드

이제 Texture를 로드해서 이를 꽂아 줘야 한다.

 

같은 Texture를 두 번 쓴다고 하면 두 번 로드할 필요가 없기 때문에 ResourceManager라는 곳에서 공용적으로 관리하게 해 줬다. 

ResourceManager::GetResourceType에 보면

template<typename T>
ResourceType ResourceManager::GetResourceType()
{
	if (std::is_same_v<T, Texture>)
		return ResourceType::Texture;
	if (std::is_same_v<T, Mesh>)
		return ResourceType::Mesh;

	assert(false);
	return ResourceType::None;
}

Texture와 Mesh만 있지만 나중에는 추가해서 텍스쳐끼리 뭉쳐서 맵에다가 관리하고 세분화해서 관리하는 게 우리의 목적이었다.

굳이 텍스쳐를 만들어서 로드하지 않고, ResourceManager를 이용해 로드를 하면 결국 조금 더 우월한 게 키 값을 대입해서 이미 만들어진 애면 걔를 리턴하고, 아니면 걔를 실질적으로 make 해서 로드하는 부분까지 ResourecManager::Load에 들어가 있다.

template<typename T>
shared_ptr<T>
ResourceManager::Load(const wstring& key, const wstring& path)
{
	auto objectType = GetResourceType<T>();
	KeyObjMap& keyObjMap = _resources[static_cast<uint8>(objectType)];

	auto findIt = keyObjMap.find(key);
	if (findIt != keyObjMap.end())
		return static_pointer_cast<T>(findIt->second);

	shared_ptr<T> object = make_shared<T>();
	object->Load(path);
	keyObjMap[key] = object;

	return object;
}

 

일단은 준비가 되어 있다.

 

EnginePch.h에 ResourceManager를 편리하게 쓸 수 있게 해 놓았다.

#define RESOURCES	GET_SINGLE(ResourceManager)

 

해야 할 건 Resources 폴더에다가 사용할 리소스를 넣어서 작업을 해보도록 한다.

올려주신 파일의 Texutres 폴더를 Resoureces에 복붙 한다.

veigar.jpg를 띄어볼 것이다.

 

TextureDemo::Init에서 Texture를 로드할 것인데

경로를 ResourceMahager에 적어 놓는 게 일반적이지만 지금은 그냥 TextureDemo::Init()에서 상대 경로로 적어준다.

RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.jpg");

여기서 Load를 하면 Texture를 사용할 준비가 끝나는 것이다.

 

TextureDemo.h 에

shared_ptr<Texture> _texture;

을 선언하고,

 

TextureDemo::Init에서

_texture = RESOURCES->Load<Texture>(L"Veigar", L"..\\\\Resources\\\\Textures\\\\veigar.jpg");

이렇게 건네준다.

 

6) TextureDemo::Render에서 ShaderRescourceView를 이용해 쉐이더의 Texture0에 _texture 세팅

04. Texture.fx의 Texture2D Texture0이 세팅이 되어야만 나머지 코드들이 실행이 되기 때문에 이 부분을 채워줄 필요가 있다.

Texture0을 TextureDemo에서 만든 _texutre와 연결을 해줘야 하는데

원래는 이미지 파일을 만들고, 그 이미지 파일을 묘사하는게 ShaderResourceView였다. 이걸 이용해서 건네주어야 했다.

Texture::Load를 보면 ShaderResourceView를 만들어 주는 부분도 만들어 놨다.

void Texture::Load(const wstring& path)
{
	DirectX::TexMetadata md;
	HRESULT hr = ::LoadFromWICFile(path.c_str(), WIC_FLAGS_NONE, &md, _img);
	CHECK(hr);

	hr = ::CreateShaderResourceView(DEVICE.Get(), _img.GetImages(), _img.GetImageCount(), md, _shaderResourveView.GetAddressOf());
	CHECK(hr);
	
	_size.x = md.width;
	_size.y = md.height;
}

그리고 Texture의 GetComPtr로 이 ShaderResourceView를 꺼내올 수 있는데

그걸 이용해서 인자처럼 Texture라는 쉐이더한테 넘겨주면 된다.

 

TextureDemo::Render에서

_shader->GetSRV("Texture0")->SetResource(_texture->GetComPtr().Get());

이렇게 연결시켜 줄 수 있다.,

이러면 Texture0에 들어갔으니까,

 

PS (픽셀 셰이더)에서는 입력된 UV 좌표(input.uv)를 사용하여 Texture0 텍스처에서 특정 위치의 색상 값을 샘플링한다. 이 샘플링 과정은 Sampler0에 정의된 샘플링 매개변수(예: 필터링 방식, 경계 처리 방식 등)에 따라 수행된다. 결과적으로, Texture0에서 샘플링된 색상 값이 해당 픽셀의 최종 색상으로 반환된다.

 

빌드 후 실행을 하면

잘 뜬다.

반응형

'DirectX' 카테고리의 다른 글

38. DirectX11 3D 입문_Sampling  (0) 2024.02.07
37. DirectX11 3D 입문_Geometry  (0) 2024.02.06
35. DirectX11 3D 입문_카메라  (0) 2024.02.05
34. DirectX11 3D 입문_Constant Buffer  (0) 2024.02.02
33. DirectX11 3D 입문_사각형 띄우기  (0) 2024.02.01

댓글