[DirectX11] 111-112 Projector
투영은 3D 공간에서의 객체를 2D 화면에 어떻게 표현할 것인지를 결정하는 과정이다. 이 과정에서 VS 단계에서 WVP(World, View, Projection) 변환된 결과가 SV_Position을 기준으로 2D 좌표로 레스터라이징되어 화면에 표현된다.
하지만, 이렇게 레스터라이징된 2D 좌표는 쉐이더로부터 바로 리턴받을 수 없기 때문에, CPU를 통해 VS, RS에서 사용된 같은 수식을 통해 2D 변환된 위치를 계산하고, 그 위치에 UI 요소를 배치하기 위해 사용된다.
목차
Projector
Shaders | |
Light.fx Projector.fx 생성 |
|
Framework | |
Objects | |
Projector.h .cpp 생성 | |
Viewer | |
Fixity.h .cpp 생성 | |
UnitTest | |
Objects | |
ProjectorDemo.h .cpp 생성 |

PS float4(x, y, z, w)에서 w는 동차
w를 나누어서 x, y, z 모두 -1 ~ 1 사이의 값으로 바꿔서 NDC 좌표계에 맞게 바꿔준다.
-1 ~ 1의 값을 넘어서는 좌표들은 NDC 좌표계에서 벗어나 있다.(=절두체에서 벗어나 있다)
NDC 공간에 들어오지 않는 값들은 그리지 않는다.
기본 원리
Viewport
뷰포트는 렌더링 시스템(RS)에서 할당되며, 3D 공간에서 2D로 변환하는 과정에서 실제 렌더링될 화면의 크기를 결정한한다.
- 화면의 좌상단 위치와 함께 넓이와 깊이를 설정하여 변환 과정을 거친다.
- Clipping 과정을 통해 뷰포트로 변환하면서 화면에 렌더링되지 않는 픽셀 영역을 자른다. 이는 불필요한 계산을 줄이고 성능을 향상시키는 데 도움이 된다.
Projection
투영은 3D 공간에서의 객체를 2D 화면에 어떻게 표현할 것인지를 결정하는 과정이다.
- 렌더링 파이프라인은 IA(Input Assembler) -> VS(Vertex Shader) -> RS(Rasterizer) -> PS(Pixel Shader)를 거친다.
- ex. 캐릭터의 HP바와 같이 3D 공간에서의 특정 위치를 2D 화면에 표현
- 이 과정에서 VS 단계에서 WVP(World, View, Projection) 변환된 결과가 SV_Position을 기준으로 2D 좌표로 레스터라이징되어 화면에 표현된다.
- 하지만, 이렇게 레스터라이징된 2D 좌표는 쉐이더로부터 바로 리턴받을 수 없기 때문에, CPU를 통해 VS, RS에서 사용된 같은 수식을 통해 2D 변환된 위치를 계산하고, 그 위치에 UI 요소를 배치하기 위해 사용된다.
3. Unprojection
역투영은 말 그대로 투영의 반대 과정이다. 2D 화면에서의 위치를 다시 3D 공간의 위치로 변환하는 과정이다.
- WVP 행렬의 역행렬을 계산하여 수행되며, NDC(Normalized Device Coordinates) 공간에 화면 비율을 적용하여 나누어주는 과정을 거친다.
- 이를 통해 사용자의 입력이나 다른 2D 요소들을 3D 공간에서의 위치로 매핑할 수 있다.
Light.fx
Light.fx
Texture2D ProjectorMap; struct ProjectorDesc { matrix View; matrix Projection; float4 Color; }; cbuffer CB_Projector { ProjectorDesc Projector; }; void VS_Projector(inout float4 wvp, float4 oPosition) { wvp = WorldPosition(oPosition); wvp = mul(wvp, Projector.View); wvp = mul(wvp, Projector.Projection); } void PS_Projector(inout float4 color, float4 wvp) { float3 uvw = 0; uvw.x = wvp.x / wvp.w * 0.5f + 0.5f; uvw.y = -wvp.y / wvp.w * 0.5f + 0.5f; uvw.z = wvp.z / wvp.w; [flatten] //0~1 사이의 공간에 들어가 있지 않다면 그리지 않겠다. if (saturate(uvw.x) == uvw.x && saturate(uvw.y) == uvw.y && saturate(uvw.z) == uvw.z) { float4 map = ProjectorMap.Sample(LinearSampler, uvw.xy); //화면 공간에 그려진다. map.rgb *= Projector.Color.rgb; color = lerp(color, map, map.a); //lerp 적용. } } cbuffer CB_Shadow { matrix ShadowView; matrix ShadowProjection; float2 ShadowMapSize; float ShadowBias; uint ShadowQuality; };
Projector.fx
Projector.fx
#include "00_Global.fx" #include "00_Light.fx" #include "00_Render.fx" MeshOutput VS_Mesh_Projector(VertexMesh input) { MeshOutput output = VS_Mesh(input); VS_Projector(output.wvpPosition_Sub, input.Position);//Light.fx에서 만든 VS_Projector 사용. return output; } MeshOutput VS_Model_Projector(VertexModel input) { MeshOutput output = VS_Model(input); VS_Projector(output.wvpPosition_Sub, input.Position); return output; } MeshOutput VS_Animation_Projector(VertexModel input) { MeshOutput output = VS_Animation(input); VS_Projector(output.wvpPosition_Sub, input.Position); return output; } float4 PS(MeshOutput input) : SV_Target { float4 color = PS_AllLight(input); PS_Projector(color, input.wvpPosition_Sub); return color; } technique11 T0 { //Render P_VP(P1, VS_Mesh_Projector, PS) P_VP(P2, VS_Model_Projector, PS) P_VP(P3, VS_Animation_Projector, PS) }
Fixity
Fixity.h
#pragma once #include "Camera.h" class Fixity : public Camera { public: Fixity(); ~Fixity(); void Update() override; void SetView(Matrix& view) { matView = view; } };
Fixity.cpp
#include "Framework.h" #include "Fixity.h" Fixity::Fixity() { Rotation(); Move(); } Fixity::~Fixity() { } void Fixity::Update() { }
Projector
Projector.h
#pragma once class Projector { public: Projector(Shader* shader, wstring mapFile, UINT width, UINT height); ~Projector(); void Update(); void Render(); Camera* GetCamera() { return (Camera *)camera; } private: struct Desc { Matrix View; Matrix Projection; Color Color = D3DXCOLOR(1, 1, 1, 1); } desc; private: Shader* shader; UINT width, height; Fixity* camera; Projection* projection; Texture* map; ID3DX11EffectShaderResourceVariable* sMap; ConstantBuffer* buffer; ID3DX11EffectConstantBuffer* sBuffer; };
Projector.cpp
#include "Framework.h" #include "Projector.h" Projector::Projector(Shader * shader, wstring mapFile, UINT width, UINT height) : shader(shader), width(width), height(height) { camera = new Fixity(); //camera->Position(0, 0, -20.0f); //camera->RotationDegree(90, 0, 0); //projection = new Perspective((float)width, (float)height, 1, 100, Math::PI * 0.25f); camera->RotationDegree(90, 0, 0); camera->Position(0, 30, 0); projection = new Orthographic((float)width, (float)height); map = new Texture(mapFile); buffer = new ConstantBuffer(&desc, sizeof(Desc)); sMap = shader->AsSRV("ProjectorMap"); sMap->SetResource(map->SRV()); sBuffer = shader->AsConstantBuffer("CB_Projector"); } Projector::~Projector() { SafeDelete(camera); SafeDelete(projection); SafeDelete(map); SafeDelete(buffer); } void Projector::Update() { Vector3 position; camera->Position(&position); ImGui::SliderFloat3("Position", position, -100, 100); camera->Position(position); ImGui::ColorEdit3("Color", desc.Color); //Perspective { static float width = this->width, height = this->height; static float n = 1.0f, f = 100.0f; static float fov = 0.25f; ImGui::SliderFloat("Width", &width, 0, 100); ImGui::SliderFloat("Height", &height, 0, 100); ImGui::SliderFloat("Near", &n, 0, 200); ImGui::SliderFloat("Far", &f, 0, 200); ImGui::SliderFloat("Fov", &fov, 0, Math::PI * 2.0f); ((Perspective *)projection)->Set(width, height, n, f, Math::PI * fov); } //Orthographic //{ // static float width = this->width, height = this->height; // static float n = 1.0f, f = 100.0f; // ImGui::SliderFloat("Width", &width, 0, 100); // ImGui::SliderFloat("Height", &height, 0, 100); // ImGui::SliderFloat("Near", &n, 0, 200); // ImGui::SliderFloat("Far", &f, 0, 200); // ((Orthographic *)projection)->Set(width, height, n, f); //} camera->GetMatrix(&desc.View); projection->GetMatrix(&desc.Projection); } void Projector::Render() { buffer->Render(); sBuffer->SetConstantBuffer(buffer->Buffer()); }
Projector Demo
ProjectorDemo.h
#pragma once #include "Systems/IExecute.h" class ProjectorDemo : 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 KachujinWeapon(); void PointLighting(); void SpotLighting(); void Pass(UINT mesh, UINT model, UINT anim); private: Shader* shader; Projector* projector; CubeSky* sky; Material* floor; Material* stone; Material* brick; Material* wall; MeshRender* cube; MeshRender* cylinder; MeshRender* sphere; MeshRender* grid; ModelRender* airplane = NULL; ModelAnimator* kachujin = NULL; Transform* colliderInitTransforms; ColliderObject** colliders; ModelRender* weapon = NULL; Transform* weaponInitTransform; vector<MeshRender *> meshes; vector<ModelRender *> models; vector<ModelAnimator *> animators; };
ProjectorDemo.cpp
#include "stdafx.h" #include "ProjectorDemo.h" void ProjectorDemo::Initialize() { Context::Get()->GetCamera()->RotationDegree(20, 0, 0); Context::Get()->GetCamera()->Position(1, 36, -85); ((Freedom *)Context::Get()->GetCamera())->Speed(50, 2); shader = new Shader(L"111_Projector.fxo"); //projector = new Projector(shader, L"Box.png", 1, 1); projector = new Projector(shader, L"Environment/MagicCircle.png", 1, 1); sky = new CubeSky(L"Environment/GrassCube1024.dds"); Mesh(); Airplane(); Kachujin(); KachujinCollider(); KachujinWeapon(); PointLighting(); SpotLighting(); } void ProjectorDemo::Update() { projector->Update(); sky->Update(); cube->Update(); grid->Update(); cylinder->Update(); sphere->Update(); airplane->Update(); kachujin->Update(); Matrix worlds[MAX_MODEL_TRANSFORMS]; for (UINT i = 0; i < kachujin->GetTransformCount(); i++) { kachujin->GetAttachTransform(i, worlds); weapon->GetTransform(i)->World(weaponInitTransform->World() * worlds[40]); } weapon->UpdateTransforms(); weapon->Update(); } void ProjectorDemo::PreRender() { } void ProjectorDemo::Render() { projector->Render(); sky->Pass(0); 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(); weapon->Render(); } void ProjectorDemo::PostRender() { } void ProjectorDemo::Mesh() { //Create Material { floor = new Material(shader); floor->DiffuseMap("Floor.png"); floor->Specular(1, 1, 1, 20); floor->SpecularMap("Floor_Specular.png"); floor->NormalMap("Floor_Normal.png"); stone = new Material(shader); stone->DiffuseMap("Stones.png"); stone->Specular(1, 1, 1, 20); stone->SpecularMap("Stones_Specular.png"); stone->Emissive(0.15f, 0.15f, 0.15f, 0.3f); stone->NormalMap("Stones_Normal.png"); brick = new Material(shader); brick->DiffuseMap("Bricks.png"); brick->Specular(1, 0.3f, 0.3f, 20); brick->SpecularMap("Bricks_Specular.png"); brick->Emissive(0.15f, 0.15f, 0.15f, 0.3f); brick->NormalMap("Bricks_Normal.png"); wall = new Material(shader); wall->DiffuseMap("Wall.png"); wall->Specular(1, 1, 1, 20); wall->SpecularMap("Wall_Specular.png"); wall->Emissive(0.15f, 0.15f, 0.15f, 0.3f); wall->NormalMap("Wall_Normal.png"); } //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 ProjectorDemo::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 ProjectorDemo::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(); animators.push_back(kachujin); } void ProjectorDemo::KachujinCollider() { UINT count = kachujin->GetTransformCount(); colliders = new ColliderObject*[count]; colliderInitTransforms = new Transform(); colliderInitTransforms->Position(-2.9f, 1.45f, -50.0f); colliderInitTransforms->Scale(5, 5, 75); 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); colliders[i]->Collider = new Collider(colliders[i]->Transform, colliderInitTransforms); } } void ProjectorDemo::KachujinWeapon() { weapon = new ModelRender(shader); weapon->ReadMesh(L"Weapon/Sword"); weapon->ReadMaterial(L"Weapon/Sword"); UINT count = kachujin->GetTransformCount(); for (UINT i = 0; i < count; i++) weapon->AddTransform(); weapon->UpdateTransforms(); models.push_back(weapon); weaponInitTransform = new Transform(); weaponInitTransform->Position(-2.9f, 1.45f, -6.45f); weaponInitTransform->Scale(0.5f, 0.5f, 0.75f); weaponInitTransform->Rotation(0, 0, 1); } void ProjectorDemo::PointLighting() { PointLight light; light = { Color(0.0f, 0.0f, 0.0f, 1.0f), //Ambient Color(0.0f, 0.0f, 1.0f, 1.0f), //Diffuse Color(0.0f, 0.0f, 0.7f, 1.0f), //Specular Color(0.0f, 0.0f, 0.7f, 1.0f), //Emissive Vector3(-30, 10, -30), 5.0f, 0.9f }; Lighting::Get()->AddPointLight(light); light = { Color(0.0f, 0.0f, 0.0f, 1.0f), Color(1.0f, 0.0f, 0.0f, 1.0f), Color(0.6f, 0.2f, 0.0f, 1.0f), Color(0.6f, 0.3f, 0.0f, 1.0f), Vector3(15, 10, -30), 10.0f, 0.3f }; Lighting::Get()->AddPointLight(light); light = { Color(0.0f, 0.0f, 0.0f, 1.0f), //Ambient Color(0.0f, 1.0f, 0.0f, 1.0f), //Diffuse Color(0.0f, 0.7f, 0.0f, 1.0f), //Specular Color(0.0f, 0.7f, 0.0f, 1.0f), //Emissive Vector3(-5, 1, -17.5f), 5.0f, 0.9f }; Lighting::Get()->AddPointLight(light); light = { Color(0.0f, 0.0f, 0.0f, 1.0f), Color(0.0f, 0.0f, 1.0f, 1.0f), Color(0.0f, 0.0f, 0.7f, 1.0f), Color(0.0f, 0.0f, 0.7f, 1.0f), Vector3(-10, 1, -17.5f), 5.0f, 0.9f }; Lighting::Get()->AddPointLight(light); } void ProjectorDemo::SpotLighting() { SpotLight light; light = { Color(0.3f, 1.0f, 0.0f, 1.0f), Color(0.7f, 1.0f, 0.0f, 1.0f), Color(0.3f, 1.0f, 0.0f, 1.0f), Color(0.3f, 1.0f, 0.0f, 1.0f), Vector3(-15, 20, -30), 25.0f, Vector3(0, -1, 0), 30.0f, 0.4f }; Lighting::Get()->AddSpotLight(light); light = { Color(1.0f, 0.2f, 0.9f, 1.0f), Color(1.0f, 0.2f, 0.9f, 1.0f), Color(1.0f, 0.2f, 0.9f, 1.0f), Color(1.0f, 0.2f, 0.9f, 1.0f), Vector3(0, 20, -30), 30.0f, Vector3(0, -1, 0), 40.0f, 0.55f }; Lighting::Get()->AddSpotLight(light); } void ProjectorDemo::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); }
실행화면


'⭐ DirectX > DirectX11 3D' 카테고리의 다른 글
[DirectX11] 113-117 Shadow 그림자, PCF (Percentage Closer Filtering) (0) | 2023.05.17 |
---|---|
[DirectX11] 107-110 Dynamic Cube Map (0) | 2023.05.05 |
[DirectX11] 106 Bloom (0) | 2023.05.03 |
[DirectX11] 104-105 Blur, Gaussian Blur (0) | 2023.04.28 |
[DirectX11] 103 MRT(Multiple Render Targets) (0) | 2023.04.27 |
댓글
이 글 공유하기
다른 글
-
[DirectX11] 113-117 Shadow 그림자, PCF (Percentage Closer Filtering)
[DirectX11] 113-117 Shadow 그림자, PCF (Percentage Closer Filtering)
2023.05.17그림자 구현방법에는 원형 그림자, 투영 그림자, 그리고 깊이 버퍼 그림자 방식이 있다. 이 중 가장 정교한 묘사가 가능한 깊이 버퍼 그림자(Depth Buffer Shadow)를 구현하여 그림자를 표현하였다. Two Pass Rendering 원리를 이용하여 깊이 버퍼 그림자를 계산하고 이를 활용한다. 목차 그림자 구현하기 그림자 구현방법 원형 그림자: 고전 방식 투영 그림자: 그 다음 등장한 방식. 장면 그대로 렌더링 깊이 버퍼 그림자 (Depth Buffer Shadow): 최근 대부분의 게임에서 사용하는 방식. 깊이 버퍼를 이용하여 그림자를 구현한다. 깊이 버퍼 그림자 (Depth Buffer Shadow) Two Pass Rendering 1 Pass: 깊이(DSV) - 조명의 방향을 가지고 Pr… -
[DirectX11] 107-110 Dynamic Cube Map
[DirectX11] 107-110 Dynamic Cube Map
2023.05.05 -
[DirectX11] 106 Bloom
[DirectX11] 106 Bloom
2023.05.03 -
[DirectX11] 104-105 Blur, Gaussian Blur
[DirectX11] 104-105 Blur, Gaussian Blur
2023.04.28DirectX 11에서 Blur는 이미지를 초점이 맞지 않거나 흐릿하게 보이게 하는 시각적 효과를 말한다. 이 효과는 종종 그래픽과 게임에서 필드 깊이를 시뮬레이션하거나 이미지의 가장자리를 부드럽게 하는 데 사용된다. Blur 효과를 생성하기 위한 다양한 기법이 있으며, 가장 일반적으로 사용되는 기법 중 하나는 Gaussian Blur다. 목차 Blur Gaussian blur는 가우스 함수로 이미지를 합성하여 작동하는 블러의 일종이다. 이 기능은 각 픽셀에 적용되는 흐림의 양을 결정하는 종 모양의 곡선을 가지고 있다. 픽셀이 주변 영역에 더 많은 픽셀을 가질수록 픽셀은 더 흐려진다. 가우스 블러 효과는 부드럽고 자연스럽게 보이는 흐림을 생성하기 때문에 종종 사용되며, 다양한 수준의 흐림을 달성하기 위해…
댓글을 사용할 수 없습니다.