프로그래밍 공부
작성일
2023. 10. 25. 12:16
작성자
WDmil
728x90

지금까지 작성했던 노말맵, 스펙컬맵 디퓨즈맵을 관리하는 ImGui를 작성해보자.

 

우선 머티리얼에서 데이터를 전부 관리함으로. Buffer를. MaterialBuffer를 따로 만들어주어야 할것이다.

 

머티리얼에서 생성하여 관리한다.

전역에서 사용하는 MaterialBuffer을 작성하자.

 

class MaterialBuffer : public ConstBuffer
{
public :
	struct Data
	{
		Float4 diffuse = { 1, 1, 1, 1 };
		Float4 specular = { 1, 1, 1, 1 };
		Float4 ambient = { 1, 1, 1, 1 };

		float shininess = 24.0f;
		float hasNormalMap = 0;
		float padding[2];
	};

public:
	MaterialBuffer() : ConstBuffer(&data, sizeof(Data))
	{
	}

	Data* GetData() { return &data; }

private:
	Data data;
};

머티리얼 Buffer는 위와같다.

원래 사용하는 diffuse와 specular, ambient와 shiniess, hasNormalMap(int타입으로 hlsl에서 NormalMap이 있는지 없는지 판단)

을 MaterialBuffer에서 사용하자.

항상 float4단위로 끊어서 사용해야 함으로. padding을 준다.

 

나머지 Buffer에서 데이터를 가져와 사용함으로. 원래 사용하던 Buffer의 데이터는 그 위치에 padding을 작성하여 

 

오류를 방지해준다.


ImGui로 Material에서 조절하기

머티리얼을 조절하기 위해ImGui에서 데이터를 조정하는 인터페이스를 작성해야한다.

 

그럼으로, Buffer데이터를 Material에서 가지고 있어야 한다.

buffer를 Material에 생성하여 관리한다.

 

또한, 머티리얼 코드를 한번에 관리할것 임으로 편의성을 위해 enum을 작성한다.

 

void Material::SetDiffuseMap(wstring textureFile)
{
	if (textureFile.size() > 0)
		diffuseMap = Texture::Add(textureFile);
	else
		diffuseMap = Texture::Add(L"DM");
}

void Material::SetSpecularMap(wstring textureFile)
{
	if (textureFile.size() > 0)
		specularMap = Texture::Add(textureFile);
	else
		specularMap = Texture::Add(L"SM");
}

void Material::SetNormalMap(wstring textureFile)
{
	if (textureFile.size() > 0) {
		normalMap = Texture::Add(textureFile);
		buffer->GetData()->hasNormalMap = true;
	}
	else
	{
		normalMap = Texture::Add(L"Textures/Colors/Blue.png");
		buffer->GetData()->hasNormalMap = false;
	}
}

Material에 위와같은 코드를 생성해준다.

 

원하는 Map값을 지정하여 바꾸어줄 수 있도록, 지정해준다.

 

이때, 같은 데이터를 사용하면 srv가 중복되어 사용되지 않을 수 있음으로, Key값으로 데이터를 기입할 수 있도록 해야할 것 이다.

 

그러니까 원래 이름으로도 데이터를 기입하고. Key값으로도 데이터를 기입할 수 있도록 하여. 원하는 머티리얼을 두개 같이 사용할 수 있도록도 코딩이 필요하다.

 

전에 사용하던 머티리얼을 삭제해야할 일이 발생하거나, 중복사용이 필요할 수 있음으로(거의 없기는 하다).

void Material::SelectMap(string name, MapType mapType)
{
	ImGui::TextColored(ImVec4(1, 0.5f, 0.8f, 1), name.c_str());
	ImGui::SameLine();

	ImTextureID textureID = nullptr;

	switch (mapType)
	{
	case Material::DIFFUSE:
		textureID = diffuseMap->GetSRV();
		break;
	case Material::SPECULAR:
		textureID = specularMap->GetSRV();

		break;
	case Material::NORMAL:
		textureID = normalMap->GetSRV();
		break;
	}

	if (ImGui::ImageButton(textureID, ImVec2(50, 50)))
		DIALOG->OpenDialog(this->name + name, name, ".png,.jpg,.tga", ".");

	if (DIALOG->Display(this->name + name))
	{
		if (DIALOG->IsOk())
		{
			string file = DIALOG->GetFilePathName();

			switch (mapType)
			{
			case Material::DIFFUSE:
				SetDiffuseMap(ToWString(file));
				break;
			case Material::SPECULAR:
				SetSpecularMap(ToWString(file));
				break;
			case Material::NORMAL:
				SetNormalMap(ToWString(file));
				break;
			}
		}

		DIALOG->Close();
	}
}

그후, 해당 코드를 사용하는 SelectMap코드를 작성하여 코드를 간결하게 유지해준다.

 

name과 mapType값을 받아와 사용하며,

 

name은 내가 사용할 material, mapType은 내가 사용할 mapType을 기입한다.

 

name으로 TextureId값을 받아와 srv를 넣어준다.

그후, maptype에 따라 위에 정의해놓았던 코드를 사용한다.

 

void Material::UnselectMap(MapType mapType)
{
	string str;

	switch (mapType)
	{
	case Material::DIFFUSE:
		str = "DM Clear";
		break;
	case Material::SPECULAR:
		str = "SM Clear";
		break;
	case Material::NORMAL:
		str = "NM Clear";
		break;
	}

	if (ImGui::Button(str.c_str()))
	{
		switch (mapType)
		{
		case Material::DIFFUSE:
			SetDiffuseMap(L"");
			break;
		case Material::SPECULAR:
			SetSpecularMap(L"");
			break;
		case Material::NORMAL:
			SetNormalMap(L"");
			break;
		default:
			break;
		}
	}

}

머티리얼을 삭제하는 코드 또한 정의해놓는다.

필요할 경우가 있을 수 있기 때문.


머티리얼을 사용할 준비가 끝났고, Buffer도 세팅해놓았으면 PS에서 받아와 사용할 수있도록 정의해준다.

항상 Buffer을 작성할 때 순서에 유의한다.

 

작성하였다면, hlsl에서도 처리할 준비가 끝났다.

이제 hlsl에서 사용해보자.

float4 PS(PixelInput input) : SV_TARGET
{
	float4 baseColor = diffuseMap.Sample(samp, input.uv);
	
	float3 T = normalize(input.tangent);
	float3 B = normalize(input.binormal);
	float3 N = normalize(input.normal);
	
	// 빛 정규화 작업.
	float3 normal = N;
	float3 light = normalize(lightDirection);
	float3 viewDir = normalize(input.worldPos - invView._41_42_43);
	
	if (hasNormalMap)
	{
		float3 normalMapColor = normalMap.Sample(samp, input.uv).rgb;
		normal = normalMapColor * 2.0f - 1.0f; // 0~1 -> -1~1
		float3x3 TBN = float3x3(T, B, N);
		normal = normalize(mul(normal, TBN));
	}

	// 빛을 반대로 뒤집고. dot으로 바꿔줌.
	// saturate 는 데이터의 제한범위 설정하는것.
	
	float diffuseIntensity = saturate(dot(normal, -light));
	float4 specular = 0;
	
	if (diffuseIntensity > 0)
	{
		//Blinn Phong Shading
		float3 halfWay = normalize(viewDir + light);
		specular = saturate(dot(normal, -halfWay));
		
		float4 specularIntensity = specularMap.Sample(samp, input.uv);
		
		specular = pow(specular, shininess) * specularIntensity * mSpecular;
	}
	
	float4 diffuse = baseColor * diffuseIntensity * mDiffuse;
	float4 ambient = baseColor * ambientLight * mAmbient;
	
	return diffuse + specular + ambient;
}

hasNormalMap을 통해 NormalMap이 존재한다면. normal을 정의하고 아닐경우 normal을 정의하지 않아 001로 대체된다.

(normal은 데이터 기입시 001이 들어왔기 때문, 항상 위로 뻗어있어야함.)

 

그후에 normalmap의 여하에 관계없이 shininess값을 받아와 처리하고, Specular을 곱해주는건 같다.

 

mDiffuse에서 데이터를 받아와서 처리할 수 있게한다.

머티리얼에서 ImGui로 바꾸는 Diffuse와 specular, Ambient를 연산하여 마지막에 다 더해주는 것으로

 

Pixel의 color값을 정의한다.

위와같이 노을빛을 받는것마냥 만들어줄 수 있다.

728x90