응애맘마조

230626 강의 본문

공부/3D강의

230626 강의

TH.Wert 2023. 6. 26. 21:53

저번 금요일에 했던 로드에 이어서 노멀 매핑에 대해서 강의했습니다. 구글 검색만 해도 obj 다운로드할 수 있는 파일은 굉장히 많이 나오지만 막상 로드할 수 있는 파일은 그렇게 많지 않고 코드 상에서도 많이 터지는 경우도 있습니다. 뿐만 아니라 만드는 사람마다 방식이 전부 다르기 때문에 정점이 안 맞는 경우도 있고 따로 되어있는 경우도 있습니다. 그래서 확실하게 검증된 파일을 사용하는 것이 좋은데 유니티 엔진에서 원활하게 움직이는 파일이라면 DirectX에서도 문제없이 사용할 수 있다고 했습니다.

#include "Common.hlsl"

struct VertexInput
{
	float4 Position : POSITION0;
	float2 Uv : UV0;
	float3 Normal : NORMAL0;
	float3 Tangent : TANGENT0;
	float4 Indices : INDICES0;
	float4 Weights : WEIGHTS0;
};
struct PixelInput
{
	float4 Position : SV_POSITION;
	float3 wPosition : POSITION0;
	float2 Uv : UV0;
	float3 Normal : NORMAL;
	float3 Tangent : TANGENT;
	float3 Binormal : BINORMAL;
};

PixelInput VS(VertexInput input)
{
    PixelInput output;
    output.Uv = input.Uv;
    output.Position = mul(input.Position, World);
	output.wPosition = output.Position.xyz;
    output.Position = mul(output.Position, ViewProj);
	output.Normal = mul(input.Normal, (float3x3) World);
	output.Tangent = mul(input.Tangent, (float3x3) World);
	output.Binormal = cross(output.Normal.xyz, output.Tangent.xyz);
    
    return output;
}

float4 PS(PixelInput input) : SV_TARGET
{
	float4 BaseColor = DiffuseMapping(input.Uv);
	float3 ariel = normalize(-float3(1, -1, 1));
	float3 normal = NormalMapping(input.Normal, input.Tangent, input.Binormal, input.Uv);
    
	float diffuse = saturate(dot(ariel, normal));
	float ambient = Ka.rgb;
    
    float3 lambert = (diffuse + ambient) * Kd.rgb * BaseColor.rgb;
	float3 Reflect = reflect(normalize(float3(1, -1, 1)), normal);
	float3 viewDir = normalize(ViewPos.xyz - input.wPosition);
	float3 specular = pow(saturate(dot(Reflect, viewDir)), Shininess) * Ks.rgb * SpecularMapping(input.Uv);
    
	float alpha = (BaseColor.a < Opacity) ? BaseColor.a : Opacity;
	return float4(lambert + specular, alpha);
}

이번 노멀 매핑에 사용한 hlsl파일입니다. 이전에 노멀 매핑 전에 사용했던 파일과 비슷하지만 Tangent, Binormal, Weight가 추가되었습니다.

#pragma once

class Main : public Scene
{
private:
	Camera*				Cam;
	Grid*				grid;

	Actor*				temp;
	string				file;

	Assimp::Importer	importer;
	const aiScene*		scene;
public:
	Main();
	~Main();
	virtual void Init() override;
	virtual void Release() override;
	virtual void Update() override;
	virtual void LateUpdate() override;
	virtual void Render() override;
	virtual void PreRender() override;
	virtual void ResizeScreen() override;

	void MakeHierarchy(aiNode* node, GameObject* node2);
	void MakeMesh(aiNode* node, GameObject* node2);
	void MakeMaterial();
};
#include "stdafx.h"
#include "Main.h"

Main::Main()
{
}

Main::~Main()
{
}

void Main::Init()
{
	Cam = Camera::Create();
	Cam->LoadFile("Cam.xml");
	Camera::main = Cam;
	Cam->width = App.GetWidth();
	Cam->height = App.GetHeight();
	Cam->viewport.width = App.GetWidth();
	Cam->viewport.height = App.GetHeight();

	grid = Grid::Create();

	temp = Actor::Create();
}

void Main::Release()
{
}

void Main::Update()
{
	ImGui::Begin("Hierarchy");
	Cam->RenderHierarchy();
	temp->RenderHierarchy();
	grid->RenderHierarchy();
	ImGui::End();

	if (GUI->FileImGui("ModelImporter", "ModelImporter",
		".fbx,.obj,.x", "../Assets"))
	{
		file = ImGuiFileDialog::Instance()->GetCurrentFileName();
		string path = "../Assets/" + file;

		importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, false);
		scene = importer.ReadFile
		(
			path,
			aiProcess_ConvertToLeftHanded
			| aiProcess_Triangulate
			| aiProcess_GenUVCoords
			| aiProcess_GenNormals
			| aiProcess_CalcTangentSpace
		);
		assert(scene != NULL and "Import Error");

		temp->ReleaseMember();

		MakeMaterial();

		GameObject* empty = GameObject::Create("empty");
		temp->AddChild(empty);
		MakeHierarchy(scene->mRootNode, empty);

		importer.FreeScene();
	}

	Camera::ControlMainCam();
	Cam->Update();
	grid->Update();
	temp->Update();
}

void Main::LateUpdate()
{
}

void Main::PreRender()
{
}

void Main::Render()
{
	Cam->Set();
	grid->Render();
	temp->Render();
}

void Main::ResizeScreen()
{
}

void Main::MakeHierarchy(aiNode* node, GameObject* node2)
{
	MakeMesh(node, node2);
	
	for (int i = 0; i < node->mNumChildren; i++)
	{
		GameObject* child = GameObject::Create(node->mChildren[i]->mName.C_Str());
		node2->AddChild(child);
		MakeHierarchy(node->mChildren[i], child);
	}
}

void Main::MakeMesh(aiNode* node, GameObject* node2)
{
	//루트 노드에 담겨있는 메쉬 갯수만큼 반복
	for (int i = 0; i < node->mNumMeshes; i++)
	{
		int index = node->mMeshes[i];
		aiMesh* mesh = scene->mMeshes[index];
		aiMaterial* mtl = scene->mMaterials[mesh->mMaterialIndex];
		string mtlFile = mtl->GetName().C_Str();
		int tok = file.find_last_of(".");
		string filePath = file.substr(0, tok) + "/";
		mtlFile = filePath + mtlFile + ".mtl";
        
		GameObject* Current = node2;
		//메쉬가 두개 이상일때
		if (i != 0)
		{
			Current = GameObject::Create(node2->name + "meshObject" + to_string(i));
			node2->AddChild(Current);
		}
		Current->shader = RESOURCE->shaders.Load("4.Cube.hlsl");
		Current->material =new Material();
		Current->material->LoadFile(mtlFile);
		Current->mesh = make_shared<Mesh>();
		Current->mesh->byteWidth = sizeof(VertexModel);
		Current->mesh->primitiveTopology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
		Current->mesh->vertexType = VertexType::MODEL;
		Current->mesh->vertexCount = mesh->mNumVertices;
		Current->mesh->vertices = new VertexModel[mesh->mNumVertices];
		//Current->mesh->indexCount = 
		vector<UINT> indexList;

		for (int j = 0; j < mesh->mNumVertices; j++)
		{
			VertexModel* vertex = (VertexModel*)Current->mesh->vertices;
			//aiVector3D
			//텍스쳐 좌표가 있다면
			if (mesh->HasTextureCoords(0))
			{
				vertex[j].uv.x =mesh->mTextureCoords[0][j].x;
				vertex[j].uv.y =mesh->mTextureCoords[0][j].y;
			}
			if (mesh->HasNormals())
			{
				vertex[j].normal.x = mesh->mNormals[j].x;
				vertex[j].normal.y = mesh->mNormals[j].y;
				vertex[j].normal.z = mesh->mNormals[j].z;
			}
			if (mesh->HasPositions())
			{
				vertex[j].position.x = mesh->mVertices[j].x;
				vertex[j].position.y = mesh->mVertices[j].y;
				vertex[j].position.z = mesh->mVertices[j].z;
			}
			if (mesh->HasTangentsAndBitangents())
			{
				vertex[j].tangent.x = mesh->mTangents[j].x;
				vertex[j].tangent.y = mesh->mTangents[j].y;
				vertex[j].tangent.z = mesh->mTangents[j].z;
			}
		}

		for (int j = 0; j < mesh->mNumFaces; j++)
		{
			for (int k = 0; k < mesh->mFaces[j].mNumIndices; k++)
			{
				indexList.push_back(mesh->mFaces[j].mIndices[k]);
			}
		}

		Current->mesh->indexCount = indexList.size();
		Current->mesh->indices = new UINT[indexList.size()];
		copy(indexList.begin(), indexList.end(), 
			stdext::checked_array_iterator<UINT*>
			(Current->mesh->indices, indexList.size()));

		Current->mesh->Reset();

		{
			int tok = file.find_last_of(".");
			string checkPath = "../Contents/Mesh/" + file.substr(0, tok);
			if (!PathFileExistsA(checkPath.c_str()))
			{
				CreateDirectoryA(checkPath.c_str(), NULL);
			}

			string filePath = file.substr(0, tok) + "/";
			string meshFile = mesh->mName.C_Str();
			Current->mesh->file = filePath + meshFile + ".mesh";
			Current->mesh->SaveFile(Current->mesh->file);
		}
	}
}

void Main::MakeMaterial()
{
	for (int i = 0; i < scene->mNumMaterials; i++)
	{
		aiMaterial* srcMtl = scene->mMaterials[i];
		Material* destMtl = new Material();
		aiColor3D tempColor;

		destMtl->file = srcMtl->GetName().C_Str();

		//ambient
		srcMtl->Get(AI_MATKEY_COLOR_AMBIENT, tempColor);
		destMtl->ambient.x = tempColor.r;
		destMtl->ambient.y = tempColor.g;
		destMtl->ambient.z = tempColor.b;

		//diffuse
		srcMtl->Get(AI_MATKEY_COLOR_DIFFUSE, tempColor);
		destMtl->diffuse.x = tempColor.r;
		destMtl->diffuse.y = tempColor.g;
		destMtl->diffuse.z = tempColor.b;

		//specular
		srcMtl->Get(AI_MATKEY_COLOR_SPECULAR, tempColor);
		destMtl->specular.x = tempColor.r;
		destMtl->specular.y = tempColor.g;
		destMtl->specular.z = tempColor.b;

		//emissive
		srcMtl->Get(AI_MATKEY_COLOR_EMISSIVE, tempColor);
		destMtl->emissive.x = tempColor.r;
		destMtl->emissive.y = tempColor.g;
		destMtl->emissive.z = tempColor.b;

		//Shininess
		srcMtl->Get(AI_MATKEY_SHININESS, destMtl->shininess);
		//opacity
		srcMtl->Get(AI_MATKEY_OPACITY, destMtl->opacity);

		//Normal
		{
			aiString aifile;
			string TextureFile;
			aiReturn texFound;
			texFound = srcMtl->GetTexture(aiTextureType_NORMALS, 0, &aifile);
			TextureFile = aifile.C_Str();
			size_t index = TextureFile.find_last_of('/');
			TextureFile = TextureFile.substr(index + 1, TextureFile.length());

			//텍스쳐가 있다.
			if (texFound == AI_SUCCESS && file != "")
			{
				destMtl->ambient.w = 1.0f;
				destMtl->normalMap = make_shared<Texture>();

				size_t tok = file.find_last_of(".");
				string checkPath = "../Contents/Texture/" + file.substr(0, tok);
				if (!PathFileExistsA(checkPath.c_str()))
				{
					CreateDirectoryA(checkPath.c_str(), NULL);
				}
				string orgin = "../Assets/" + TextureFile;
				string copy = "../Contents/Texture/" + file.substr(0, tok) + "/" + TextureFile;
				bool isCheck = true;
				CopyFileA(orgin.c_str(), copy.c_str(), isCheck);

				destMtl->normalMap->LoadFile(file.substr(0, tok) + "/" + TextureFile);
			}
		}

		//Diffuse
		{
			aiString aifile;
			string TextureFile;
			aiReturn texFound;
			texFound = srcMtl->GetTexture(aiTextureType_DIFFUSE, 0, &aifile);
			TextureFile = aifile.C_Str();
			size_t index = TextureFile.find_last_of('/');
			TextureFile = TextureFile.substr(index + 1, TextureFile.length());

			//텍스쳐가 있다.
			if (texFound == AI_SUCCESS && file != "")
			{
				destMtl->diffuse.w = 1.0f;
				destMtl->diffuseMap = make_shared<Texture>();

				size_t tok = file.find_last_of(".");
				string checkPath = "../Contents/Texture/" + file.substr(0, tok);
				if (!PathFileExistsA(checkPath.c_str()))
				{
					CreateDirectoryA(checkPath.c_str(), NULL);
				}
				string orgin = "../Assets/" + TextureFile;
				string copy = "../Contents/Texture/" + file.substr(0, tok) + "/" + TextureFile;
				bool isCheck = true;
				CopyFileA(orgin.c_str(), copy.c_str(), isCheck);

				destMtl->diffuseMap->LoadFile(file.substr(0, tok) + "/" + TextureFile);
			}
		}

		//specular
		{
			aiString aifile;
			string TextureFile;
			aiReturn texFound;
			texFound = srcMtl->GetTexture(aiTextureType_SPECULAR, 0, &aifile);
			TextureFile = aifile.C_Str();
			size_t index = TextureFile.find_last_of('/');
			TextureFile = TextureFile.substr(index + 1, TextureFile.length());

			//텍스쳐가 있다.
			if (texFound == AI_SUCCESS && file != "")
			{
				destMtl->specular.w = 1.0f;
				destMtl->specularMap = make_shared<Texture>();

				size_t tok = file.find_last_of(".");
				string checkPath = "../Contents/Texture/" + file.substr(0, tok);
				if (!PathFileExistsA(checkPath.c_str()))
				{
					CreateDirectoryA(checkPath.c_str(), NULL);
				}
				string orgin = "../Assets/" + TextureFile;
				string copy = "../Contents/Texture/" + file.substr(0, tok) + "/" + TextureFile;
				bool isCheck = true;
				CopyFileA(orgin.c_str(), copy.c_str(), isCheck);

				destMtl->specularMap->LoadFile(file.substr(0, tok) + "/" + TextureFile);
			}
		}

		//emissive
		{
			aiString aifile;
			string TextureFile;
			aiReturn texFound;
			texFound = srcMtl->GetTexture(aiTextureType_EMISSIVE, 0, &aifile);
			TextureFile = aifile.C_Str();
			size_t index = TextureFile.find_last_of('/');
			TextureFile = TextureFile.substr(index + 1, TextureFile.length());

			if (texFound == AI_SUCCESS && file != "")
			{
				destMtl->emissive.w = 1.0f;
				destMtl->emissiveMap = make_shared<Texture>();

				size_t tok = file.find_last_of(".");
				string checkPath = "../Contents/Texture/" + file.substr(0, tok);
				if (!PathFileExistsA(checkPath.c_str()))
				{
					CreateDirectoryA(checkPath.c_str(), NULL);
				}
				string orgin = "../Assets/" + TextureFile;
				string copy = "../Contents/Texture/" + file.substr(0, tok) + "/" + TextureFile;
				bool isCheck = true;
				CopyFileA(orgin.c_str(), copy.c_str(), isCheck);

				destMtl->emissiveMap->LoadFile(file.substr(0, tok) + "/" + TextureFile);
			}
		}

		size_t tok = file.find_last_of(".");
		string checkPath = "../Contents/Material/" + file.substr(0, tok);
		if (!PathFileExistsA(checkPath.c_str()))
		{
			CreateDirectoryA(checkPath.c_str(), NULL);
		}

		string filePath = file.substr(0, tok) + "/";
		destMtl->file = filePath + destMtl->file + ".mtl";
		destMtl->SaveFile(destMtl->file);
	}
}

int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prevInstance, LPWSTR param, int command)
{
	App.SetAppName(L"ObjLoader");
	App.SetInstance(instance);
    WIN->Create();
    D3D->Create();
	Main * main = new Main();
    main->Init();

	int wParam = (int)WIN->Run(main);

    main->Release();
	SafeDelete(main);
    D3D->DeleteSingleton();
	WIN->DeleteSingleton();

	return wParam;
}

Main의 헤더와 cpp 파일입니다. 노멀매핑에 맞춰서 코드가 변경되었습니다. 그럼 노멀 매핑 전과 후의 모습을 비교해 보겠습니다.

좌 : 노멀 매핑 전, 우 : 노멀 매핑 후

카메라의 위치에는 차이가 있지만 확연한 차이를 느낄 수 있는 모습입니다.

 

읽어주셔서 감사합니다.

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

230628 강의  (0) 2023.06.28
230627 강의  (0) 2023.06.27
230623 강의  (0) 2023.06.23
230622 강의  (0) 2023.06.22
230621 강의  (0) 2023.06.21