필요한 개념
2D 공간으로 변환하는 Projection을 하기 위해 선행으로 Viewport를 알아야함
앞으로 3D 공간에서 물체를 선택하는 것을 해볼 것이다.(충돌체를 선택하는 것을)
3D 공간 2D 공간으로 변환하는 것과 2D공간을 3D공간으로 변환하는 것을 이해해야 한다.
3D 공간 2D 공간으로 변환되는 과정을 한번 얘기
IA - VS - RS - PS
VS -> WVP
RS -> Vp(viewport)
3D -> W -> V -> P -> Vp(viewport) -> 2D
WVP -> VS(VertexShader) 구간
Vp -> RS(RasterizerStage) 구간
원근 투영(Perspective Projection) : 멀리갈수록 작아짐
직교 투영(Orthographic Projection) : 거리에 상관없이 항상 같은 크기
변환 과정을 보면 world를 직육면체라고 하면 ViewProjection 변환되면서 절두체로 world의 앞면은 줄어들고 뒷면은 늘어남
그후 Viewport로 변환 되면서 축소가 일어남, 앞에 부분은 확대가 일어남
먼면은 축소가 일어나면서 작아지고 가까운면은 확대가 일어나면서 커짐, 거리가 멀면 멀수록 작아지는 이유가 여기에 있다.(원근투영)
->거리에 따라 작아짐
직교 투영은
W가 직육면체 VP도 직육면체(직교 투영은 직육면체 모양) Viewport도 직육면체 모양이어서 크기의 변화가 없다.
NDC란? -1 ~ +1까지로 정규화된 2D 공간
Tip)
3D -> IA -> VS -> RS -> 2D
렌더링 파이프라인을 통해 계산한다고 해도 우리가 사용하기 위해 리턴받기 힘들다.
그래서 같은 수식으로 계산해서 값을 사용할 것임
2D위치로 계산되어야 하는 UI등을 표시하기 위해 사용함
3D위치를 가지고 렌더링파이프라인으로 2D로 변환해서 화면에 보여주게 되는데,(리턴받기 힘들어서)
임의로 코드로 계산할 것이다.
Viewport : W->VP까지 계산되면 2D인데(원래는 3D이다, 깊이가 있어서) 이것을 어느영역에서 얼마만큼(얼마만큼의 깊이)을 보여줄지를 결정하는게 Viewport
어떻게 화면에 보여줄 것인가?(어느 크기로 어느 깊이로 보여줄 것인가)
보통 Viewport의 깊이는 0 ~ +1사이를 둔다.(이유는 어차피 W->VP까지 계산되면 2D라서 깊이를 0을 두고 계산한다)
깊이는 항상 0에서 시작(이유는 시야 뒤에는 렌더링 할 필요가 없어서)
따로 세팅할시에는 RS에 있어서 RSSetViewport()를 사용하면 된다.
Viewport의 width를 화면 영역 이상으로 넓히면 화면 영역이 넘어서 짤림, 반대로 줄이면 백그라운드가 초기화된 컬러가 나옴
x,y를 조정하면 화면의 시작위치가 바뀜(여기서 시작해서 화면 위치만큼 보여주겠단 얘기여서)'
Perspective의 FOV는 시야각을 결정한다.(기본은 PI / 4, 45도를 사용)
minZ는 정규화를 위해 나눌 값이므로 0을 주면 안되며 뒤를 그릴 필요가 없으므로 Z는 -값이 존재하지 않음
Fov가 크면 클수록 시야각이 넓어짐, 작으면 작을 수록 시야각이 좁아짐(FPS 에임 확대할때나, 레이싱게임할때 막 질주할때 시야각이 좁아질때)
(얼마만큼 시야각을 보여줄지)
minZ, maxZ(최대 보여줄 거리)을 값을 늘리면 늘릴수록 잘려나감(얼마만큼 거리를 보여줄지)
maxZ이 커지면 커질 수록 퍼포먼스가 떨어짐(많이 보여줘야 해서)
Perspective는 시야각을 결정하고 시야각과 함께 어느 구간을 보여줄지 결정하는게 Perspective
이렇게 변환이 완료된거를 얼마만큼의 크기로 얼마부터 보여줄지 화면에 보여줄지를 결정하는게 Viewport
ViewportDemo.h
#pragma once
#include "Systems/IExecute.h"
class ViewportDemo : public IExecute
{
public:
virtual void Initialize() override;
virtual void Ready() override {};
virtual void Destroy() override;
virtual void Update() override;
virtual void PreRender() override {};
virtual void Render() override;
virtual void PostRender() override {};
virtual void ResizeScreen() override {};
private:
void Mesh();
void Airplane();
void Kachujin();
void KachujinCollider();
void Pass(UINT mesh, UINT model, UINT anim);
private:
Shader* shader;
CubeSky* sky;
Material* floor;
Material* stone;
Material* brick;
Material* wall;
MeshRender* cube;
MeshRender* cylinder;
MeshRender* sphere;
MeshRender* grid;
ModelRender* airplane = NULL;
ModelAnimator* kachujin = NULL;
// Collider의 인스턴싱은 몇개 인지 모른다.(그래서 *하나 더 붙음)
ColliderObject** colliders;
// 나중에 Path 관리를 편하게 하기 위해
vector<MeshRender*> meshes;
vector<ModelRender*> models;
vector<ModelAnimator*> animators;
};
ViewportDemo.cpp
#include "stdafx.h"
#include "ViewportDemo.h"
void ViewportDemo::Initialize()
{
Context::Get()->GetCamera()->RotationDegree(20, 0, 0);
Context::Get()->GetCamera()->Position(1, 36, -85);
shader = new Shader(L"55_Render.fx");
sky = new CubeSky(L"Environment/GrassCube1024.dds");
Mesh();
Airplane();
Kachujin();
KachujinCollider();
}
void ViewportDemo::Destroy()
{
SafeDelete(shader);
SafeDelete(sky);
SafeDelete(cube);
SafeDelete(cylinder);
SafeDelete(sphere);
SafeDelete(grid);
SafeDelete(floor);
SafeDelete(stone);
SafeDelete(brick);
SafeDelete(wall);
SafeDelete(airplane);
SafeDelete(kachujin);
}
void ViewportDemo::Update()
{
sky->Update();
cube->Update();
grid->Update();
cylinder->Update();
sphere->Update();
airplane->Update();
kachujin->Update();
for (UINT i = 0; i < kachujin->GetTransformCount(); i++)
{
// 카추진 위치 얻어옴(업데이트 시킬)
Matrix attach;
kachujin->GetAttachTransform(i, &attach);
// 얼마만큼 이동할지
colliders[i]->Collider->GetTransform()->World(attach);
colliders[i]->Collider->Update();
}
}
void ViewportDemo::Render()
{
// 메인 뷰포트를 만들때 화면 넓이를 사용
static float width = D3D::Width();
static float height = D3D::Height();
static float x = 0.0f;
static float y = 0.0f;
ImGui::InputFloat("Width", &width, 1.0f);
ImGui::InputFloat("Height", &height, 1.0f);
ImGui::InputFloat("X", &x, 1.0f);
ImGui::InputFloat("Y", &y, 1.0f);
Context::Get()->GetViewport()->Set(width, height, x, y);
// Perspective의 FOV는 시야각을 결정한다.(기본은 PI / 4, 45도를 사용)
// FOV(field of view) -> 시야각을 의미(더 자세한건 나중에 그림자할때 설명)
static float fov = 0.25f;
ImGui::InputFloat("Fov", &fov, 0.001f);
// 0이 되면 안되는 이유가 0이면 내 눈안에 들어가 있는 것이다.
// 피부정도를 약간 늘려준다 생각
// 실제 내부적으로는 나누기 연산을 해서 0을 주면 안된다.
// maxZ은 기본값 1000으로 되어있다.
// WP가 완료되고 Viewport로 넘어갈때
// minZ 이 0이 되고 maxZ이 1로 정규화됨
static float minZ = 0.1, maxZ = 1000.0f;
ImGui::InputFloat("minZ", &minZ, 1.0f);
ImGui::InputFloat("MaxZ", &maxZ, 1.0f);
Perspective* perspective = Context::Get()->GetPerspective();
perspective->Set(D3D::Width(), D3D::Height(), minZ, maxZ, Math::PI * fov);
sky->Render();
Pass(0, 1, 2);
wall->Render();
sphere->Render();
brick->Render();
cylinder->Render();
stone->Render();
cube->Render();
floor->Render();
grid->Render();
airplane->Render();
kachujin->Render();
for (UINT i = 0; i < kachujin->GetTransformCount(); i++)
colliders[i]->Collider->Render();
}
void ViewportDemo::Mesh()
{
//Create Material
{
floor = new Material(shader);
floor->DiffuseMap("Floor.png");
//floor->SpecularMap("Floor_Specular.png");
//floor->NormalMap("Floor_Normal.png");
//floor->Specular(1, 1, 1, 20);
stone = new Material(shader);
stone->DiffuseMap("Stones.png");
//stone->SpecularMap("Stones_Specular.png");
//stone->NormalMap("Stones_Normal.png");
//stone->Specular(1, 1, 1, 20);
brick = new Material(shader);
brick->DiffuseMap("Bricks.png");
//brick->SpecularMap("Bricks_Specular.png");
//brick->NormalMap("Bricks_Normal.png");
//brick->Specular(1, 0.3f, 0.3f, 20);
wall = new Material(shader);
wall->DiffuseMap("Wall.png");
//wall->SpecularMap("Wall_Specular.png");
//wall->NormalMap("Wall_Normal.png");
//wall->Specular(1, 1, 1, 20);
}
//Create Mesh
{
Transform* transform = NULL;
cube = new MeshRender(shader, new MeshCube());
transform = cube->AddTransform();
transform->Position(0, 5, 0);
transform->Scale(20, 10, 20);
grid = new MeshRender(shader, new MeshGrid(5, 5));
transform = grid->AddTransform();
transform->Position(0, 0, 0);
transform->Scale(12, 1, 12);
cylinder = new MeshRender(shader, new MeshCylinder(0.5f, 3.0f, 20, 20));
sphere = new MeshRender(shader, new MeshSphere(0.5f, 20, 20));
for (UINT i = 0; i < 5; i++)
{
transform = cylinder->AddTransform();
transform->Position(-30, 6, -15.0f + (float)i * 15.0f);
transform->Scale(5, 5, 5);
transform = cylinder->AddTransform();
transform->Position(30, 6, -15.0f + (float)i * 15.0f);
transform->Scale(5, 5, 5);
transform = sphere->AddTransform();
transform->Position(-30, 15.5f, -15.0f + (float)i * 15.0f);
transform->Scale(5, 5, 5);
transform = sphere->AddTransform();
transform->Position(30, 15.5f, -15.0f + (float)i * 15.0f);
transform->Scale(5, 5, 5);
}
}
sphere->UpdateTransforms();
cylinder->UpdateTransforms();
cube->UpdateTransforms();
grid->UpdateTransforms();
// 렌더링 할 것들을 추가
meshes.push_back(sphere);
meshes.push_back(cylinder);
meshes.push_back(cube);
meshes.push_back(grid);
}
void ViewportDemo::Airplane()
{
airplane = new ModelRender(shader);
airplane->ReadMesh(L"B787/Airplane");
airplane->ReadMaterial(L"B787/Airplane");
Transform* transform = airplane->AddTransform();
transform->Position(2.0f, 9.91f, 2.0f);
transform->Scale(0.004f, 0.004f, 0.004f);
airplane->UpdateTransforms();
models.push_back(airplane);
}
void ViewportDemo::Kachujin()
{
kachujin = new ModelAnimator(shader);
kachujin->ReadMesh(L"Kachujin/Mesh");
kachujin->ReadMaterial(L"Kachujin/Mesh");
kachujin->ReadClip(L"Kachujin/Sword And Shield Idle");
kachujin->ReadClip(L"Kachujin/Sword And Shield Walk");
kachujin->ReadClip(L"Kachujin/Sword And Shield Run");
kachujin->ReadClip(L"Kachujin/Sword And Shield Slash");
kachujin->ReadClip(L"Kachujin/Salsa Dancing");
Transform* transform = NULL;
transform = kachujin->AddTransform();
transform->Position(0, 0, -30);
transform->Scale(0.075f, 0.075f, 0.075f);
kachujin->PlayTweenMode(0, 0, 1.0f);
transform = kachujin->AddTransform();
transform->Position(-15, 0, -30);
transform->Scale(0.075f, 0.075f, 0.075f);
kachujin->PlayTweenMode(1, 1, 1.0f);
transform = kachujin->AddTransform();
transform->Position(-30, 0, -30);
transform->Scale(0.075f, 0.075f, 0.075f);
kachujin->PlayTweenMode(2, 2, 0.75f);
transform = kachujin->AddTransform();
transform->Position(15, 0, -30);
transform->Scale(0.075f, 0.075f, 0.075f);
kachujin->PlayBlendMode(3, 0, 1, 2);
kachujin->SetBlendAlpha(3, 1.5f);
transform = kachujin->AddTransform();
transform->Position(30, 0, -32.5f);
transform->Scale(0.075f, 0.075f, 0.075f);
kachujin->PlayTweenMode(4, 4, 0.75f);
kachujin->UpdateTransforms();
// 오른손 쪽(40번 본)
kachujin->SetAttachTransform(40);
animators.push_back(kachujin);
}
void ViewportDemo::KachujinCollider()
{
// 인스턴싱 개수
UINT count = kachujin->GetTransformCount();
colliders = new ColliderObject*[count];
for (UINT i = 0; i < count; i++)
{
colliders[i] = new ColliderObject();
// 기준 위치
colliders[i]->Init = new Transform();
colliders[i]->Init->Position(0, 0, 0);
colliders[i]->Init->Scale(10, 30, 10);
colliders[i]->Transform = new Transform();
colliders[i]->Collider = new Collider(colliders[i]->Transform, colliders[i]->Init);
}
}
void ViewportDemo::Pass(UINT mesh, UINT model, UINT anim)
{
for (MeshRender* temp : meshes)
temp->Pass(mesh);
for (ModelRender* temp : models)
temp->Pass(model);
for (ModelAnimator* temp : animators)
temp->Pass(anim);
}
결과
'DirectX11 3D > 기본 문법' 카테고리의 다른 글
<DirectX11 3D> 68 - Unprojection (0) | 2022.03.02 |
---|---|
<DirectX11 3D> 67 - Projection (0) | 2022.03.01 |
<DirectX11 3D> 63 - ComputeShader(CS)(3) - StructuredBuffer를 이용한 GetAnimationBone (0) | 2022.03.01 |
<DirectX11 3D> 62 - ComputeShader(CS)(2) - TextureBuffer (0) | 2022.02.24 |
<DirectX11 3D> 59 - ComputeShader(CS)(1) - RawBuffer(ByteAdress) (0) | 2022.02.24 |