🎨DirectX

[DX] 3D Model Render -2(Shader)

공대 컴린이 2023. 5. 2. 11:42
728x90

🪄 3D Model Render Shader

Model이 그려지는 위치인 Transform이 SRT로 존재하고,

Model의 Mesh마다 BoneIndex가 존재할 때, BoneIndex를 참조해서 Bone에 있는 Transform을 그리게 된다.

 

Model이 움직이거나 크기가 변화할 때 바뀌는 값이 바로 Model의 Transform SRT 인 것이다.

 

따라서 3D Model을 출력한 후, SRT 변화에도 정상적인 Mesh를 출력하기 위해 아래와 같은 연산을 거쳐야 한다.

먼저, Bone이 기준이므로 먼저 움직이므로 자신이 가지고있는 BoneTransforms의 BoneIndex 요소를 가져와서,

mul (BoneTransforms[BoneIndex])

World에 곱하고

mul (BoneTransforms[BoneIndex], World)

해당 공간이 최종 공간이므로 World에 저장한다.

World = mul (BoneTransforms[BoneIndex], World)

 

이후, 쉐이더로 들어오는 input.Position을 World에 곱해서 Model Rendering을 구현할 수 있다.

mul (input.Position, World)  --> (WorldPosition 으로 Typedef 해놓음)


📄 Main 출력 파일 - ModelDemo.cpp

더보기
#include "stdafx.h"
#include "ModelDemo.h"
#include "Converter.h"

void ModelDemo::Initialize()
{
	Context::Get()->GetCamera()->RotationDegree(20, 0, 0);
	Context::Get()->GetCamera()->Position(1, 36, -85);

	shader = new Shader(L"38_Model.fx");

	Airplane();
	Tower();
	Tank();

	sky = new CubeSky(L"Environment/GrassCube1024.dds");

	gridShader = new Shader(L"25_Mesh.fx");
	grid = new MeshGrid(gridShader, 6, 6);
	grid->GetTransform()->Scale(12, 1, 12);
	grid->DiffuseMap(L"Floor.png");
}

void ModelDemo::Update()
{
	sky->Update();
	grid->Update();

	if (airplane != NULL) 
		airplane->Update();
	if (tower != NULL) 
		tower->Update();
	if (tank != NULL) 
		tank->Update();
}

void ModelDemo::Render()
{
	ImGui::SliderFloat3("Direction", direction, -1, +1);
	shader->AsVector("Direction")->SetFloatVector(direction);
	gridShader->AsVector("Direction")->SetFloatVector(direction);

	static int pass = 0;
	ImGui::InputInt("Pass", &pass);
	pass %= 2;

	sky->Render();
	grid->Render();

	if (airplane != NULL)
	{
		airplane->Pass(pass);
		airplane->Render();
	}

	if (tower != NULL)
	{
		tower->Pass(pass);
		tower->Render();
	}

	if (tank != NULL)
	{
		tank->Pass(pass);
		tank->Render();
	}
}

void ModelDemo::Airplane()
{
	airplane = new ModelRender(shader);
	airplane->ReadMesh(L"B787/Airplane");
	airplane->GetTransform()->Scale(0.005f, 0.005f, 0.005f);
}

void ModelDemo::Tower()
{
	tower = new ModelRender(shader);
	tower->ReadMesh(L"Tower/Tower");
	tower->GetTransform()->Position(-20, 0, 0);
	tower->GetTransform()->Scale(0.01f, 0.01f, 0.01f);
}

void ModelDemo::Tank()
{
	tank = new ModelRender(shader);
	tank->ReadMesh(L"Tank/Tank");
	tank->GetTransform()->Position(20, 0, 0);
}

📄 ModelRender.h

더보기
#pragma once

class ModelRender
{
public:
	ModelRender(Shader* shader);
	~ModelRender();

	void Update();
	void Render();

public:
	void ReadMesh(wstring file);

	Transform* GetTransform() { return transform; }
	Model* GetModel() { return model; }

	void Pass(UINT pass);

	void UpdateTransform(ModelBone* bone = NULL, Matrix& matrix = Matrix());

private:
	void UpdateBones(ModelBone* bone, Matrix& matrix);

private:
	bool bRead = false;

	Shader* shader;
	Model* model;
	Transform* transform;

	Matrix transforms[MAX_MODEL_TRANSFORMS];
};

📄 ModelRender.cpp

더보기
#include "Framework.h"
#include "ModelRender.h"

ModelRender::ModelRender(Shader * shader)
	: shader(shader)
{
	model = new Model();
	transform = new Transform(shader);
}

ModelRender::~ModelRender()
{
	SafeDelete(model);
	SafeDelete(transform);
}

void ModelRender::Update()
{
	if(bRead == true)
	{
		bRead = false;

		// 전체 Mesh 돌아가면서 Shader Setting
		for (ModelMesh* mesh : model->Meshes())
			mesh->SetShader(shader);

		UpdateTransform();
	}

	for (ModelMesh* mesh : model->Meshes())
		mesh->Update();
}

void ModelRender::Render()
{
	for (ModelMesh* mesh : model->Meshes())
	{
		mesh->SetTransform(transform);
		mesh->Render();
	}
}

void ModelRender::ReadMesh(wstring file)
{
	model->ReadMesh(file);

	bRead = true;
}


void ModelRender::Pass(UINT pass)
{
	for (ModelMesh* mesh : model->Meshes())
		mesh->Pass(pass);
}

void ModelRender::UpdateTransform(ModelBone* bone, Matrix& matrix)
{
	if (bone != NULL)
		UpdateBones(bone, matrix);

	// Bone 에 있는 Transform을 transforms 배열에 복사
	for(UINT i = 0; i < model->BoneCount(); i++)
	{
		ModelBone* bone = model->BoneByIndex(i);
		transforms[i] = bone->Transform();
	}

	for (ModelMesh* mesh : model->Meshes())
		mesh->Transforms(transforms);
}

void ModelRender::UpdateBones(ModelBone* bone, Matrix& matrix)
{
}

📄 Shader (38_Model.fx)

더보기
#include "00_Global.fx"

float3 Direction = float3(-1, -1, +1);

struct VertexModel
{
    float4 Position : Position;
    float2 Uv : Uv;
    float3 Normal : Normal;
    float3 Tangent : Tangent;
    float4 BlendIndices : BlendIndices;
    float4 BlendWeights : BlendWeights;
};

#define MAX_MODEL_TRANSFORMS 250

cbuffer CB_Bone
{
    matrix BoneTransforms[MAX_MODEL_TRANSFORMS];
    
    uint BoneIndex;
};

struct VertexOutput
{
    float4 Position : SV_Position;
    float3 Normal : Normal;
    float2 Uv : Uv;
};

VertexOutput VS(VertexModel input)
{
    VertexOutput output;

    World = mul(BoneTransforms[BoneIndex], World);

    output.Position = WorldPosition(input.Position);
    output.Position = ViewProjection(output.Position);

    output.Normal = WorldNormal(input.Normal);
    output.Uv = input.Uv;
    
    return output;
}

float4 PS(VertexOutput input) : SV_Target
{
    float NdotL = dot(normalize(input.Normal), -Direction); // 음영 넣기

    return float4(1, 1, 1, 1) * NdotL;
}

technique11 T0
{
    P_VP(P0, VS, PS)
    P_RS_VP(P1, FillMode_WireFrame, VS, PS)
}

[ 실행 결과 ]

3D Model 3가지 출력 결과
WireFrame 모드 출력

728x90