-
[DirectX11] Viewport 이해2024년 03월 12일
- 유니얼
-
작성자
-
2024.03.12.:49
728x90DirectX 11로 게임 엔진 아키텍처 만들기
게임 개발 과정에서 게임 엔진의 주요 구성 요소 중 하나는 렌더링 파이프라인입니다. 렌더링 파이프라인 내에서 매우 중요한 역할을 하는 개념이 바로 Viewport입니다. 이 블로그 포스트에서는 DirectX11 환경에서 Viewport의 개념을 설명하고, 이를 활용하는 방법과 예제 코드를 소개합니다.
참고강의 링크:
Viewport 개념
Viewport는 3D 렌더링된 콘텐츠가 실제로 화면에 표시되는 영역을 정의합니다. 간단히 말해서, Viewport는 3D 월드에서 카메라를 통해 캡처된 장면이 윈도우나 화면 상의 어느 부분에 보여질 것인지를 결정하는 설정입니다. DirectX11에서는 이 Viewport를 통해 렌더링의 최종 결과물이 어떻게 화면에 나타날지 세밀하게 제어할 수 있습니다.
Viewport의 주요 속성
- 위치와 크기: Viewport의 위치와 크기를 통해 화면 상에서 장면이 표시될 정확한 영역을 지정할 수 있습니다.
- 깊이 범위: Viewport는 깊이 값을 통해 장면 내 객체의 렌더링 순서를 결정합니다. 이는 객체 간의 시각적 겹침을 제어하는 데 중요합니다.
Viewport 설정 예제
DirectX11에서 Viewport를 설정하는 기본적인 방법을 살펴봅시다.
위 코드는 800x600 크기의 Viewport를 생성하고, 이를 화면의 왼쪽 상단 모서리부터 시작하도록 설정합니다. 또한, 깊이 범위는 가장 얕은 곳(0.0f)부터 가장 깊은 곳(1.0f)까지로 설정됩니다.
Viewport의 활용
Viewport를 활용하면 하나의 렌더링 타겟에 여러 장면을 다른 위치나 크기로 표시하는 등 다양한 시각적 효과를 구현할 수 있습니다. 예를 들어, 분할 화면 멀티플레이어 게임에서는 각 플레이어의 시점을 다른 Viewport를 통해 화면의 다른 부분에 표시할 수 있습니다.
Viewport.h
#pragma once // 뷰포트를 정의하는 클래스 class Viewport { public: // 기본 생성자: 비어있는 뷰포트를 생성 Viewport(); // 파라미터를 받는 생성자: 뷰포트의 크기와 위치, 깊이 범위를 설정 Viewport(float width, float height, float x = 0, float y = 0, float minDepth = 0, float maxDepth = 0); // 소멸자 ~Viewport(); // 뷰포트를 Direct3D에 설정 void RSSetViewport(); // 뷰포트의 속성을 설정 void Set(float width, float height, float x = 0, float y = 0, float minDepth = 0, float maxDepth = 0); // 뷰포트의 너비를 가져옴 float GetWidth() { return _vp.Width; } // 뷰포트의 높이를 가져옴 float GetHeight() { return _vp.Height; } // 3D 좌표를 2D 화면 좌표로 프로젝션 Vec3 Project(const Vec3& pos, const Matrix& W, const Matrix& V, const Matrix& P); // 2D 화면 좌표를 3D 좌표로 역프로젝션 Vec3 Unproject(const Vec3& pos, const Matrix& W, const Matrix& V, const Matrix& P); private: // Direct3D 뷰포트 구조체 D3D11_VIEWPORT _vp; };
Viewport.cpp
#include "pch.h" #include "Viewport.h" // 기본 생성자: 기본 크기로 뷰포트 설정 Viewport::Viewport() { Set(800, 600); // 기본 크기 800x600으로 설정 } // 매개변수를 받는 생성자: 사용자 지정 크기와 위치, 깊이 범위로 뷰포트 설정 Viewport::Viewport(float width, float height, float x, float y, float minDepth, float maxDepth) { Set(width, height, x, y, minDepth, maxDepth); } // 소멸자: 특별한 정리 작업 없음 Viewport::~Viewport() { } // Direct3D에 뷰포트 설정을 적용하는 함수 void Viewport::RSSetViewport() { DC->RSSetViewports(1, &_vp); // 설정된 뷰포트를 렌더링 파이프라인에 적용 } // 뷰포트 속성 설정 함수 void Viewport::Set(float width, float height, float x, float y, float minDepth, float maxDepth) { _vp.TopLeftX = x; _vp.TopLeftY = y; _vp.Width = width; _vp.Height = height; _vp.MinDepth = minDepth; _vp.MaxDepth = maxDepth; } // 3D 좌표를 2D 스크린 좌표로 변환하는 함수 Vec3 Viewport::Project(const Vec3& pos, const Matrix& W, const Matrix& V, const Matrix& P) { Matrix wvp = W * V * P; // 월드, 뷰, 프로젝션 행렬을 결합 Vec3 p = Vec3::Transform(pos, wvp); // 좌표 변환 // 스크린 좌표로 변환 p.x = (p.x + 1.0f) * (_vp.Width / 2) + _vp.TopLeftX; p.y = (-p.y + 1.0f) * (_vp.Height / 2) + _vp.TopLeftY; p.z = p.z * (_vp.MaxDepth - _vp.MinDepth) + _vp.MinDepth; return p; } // 2D 스크린 좌표를 3D 좌표로 역변환하는 함수 Vec3 Viewport::Unproject(const Vec3& pos, const Matrix& W, const Matrix& V, const Matrix& P) { Vec3 p = pos; // 스크린 좌표를 정규화된 좌표로 변환 p.x = 2.f * (p.x - _vp.TopLeftX) / _vp.Width - 1.f; p.y = -2.f * (p.y - _vp.TopLeftY) / _vp.Height + 1.f; p.z = ((p.z - _vp.MinDepth) / (_vp.MaxDepth - _vp.MinDepth)); Matrix wvp = W * V * P; Matrix wvpInv = wvp.Invert(); // 변환 행렬의 역행렬 계산 p = Vec3::Transform(p, wvpInv); // 역변환 return p; }
프로젝트 호출
ViewportDemo.h
#pragma once #include "IExecute.h" class ViewportDemo :public IExecute { public: void Init() override; void Update() override; void Render() override; private: shared_ptr<Shader> _shader; };
ViewportDemo.cpp
#include "pch.h" #include "ViewportDemo.h" #include "GeometryHelper.h" #include "Camera.h" #include "GameObject.h" #include "CameraScript.h" #include "MeshRenderer.h" #include "Mesh.h" #include "Material.h" #include "Model.h" #include "ModelRenderer.h" #include "ModelAnimator.h" #include "Mesh.h" #include "Transform.h" #include "VertexBuffer.h" #include "IndexBuffer.h" #include "Light.h" #include "TextureBuffer.h" #include "Viewport.h" void ViewportDemo::Init() { _shader = make_shared<Shader>(L"23. RenderDemo.fx"); // Camera { auto camera = make_shared<GameObject>(); camera->GetOrAddTransform()->SetWorldPosition(Vec3{ 0.f, 0.f, -5.f }); camera->AddComponent(make_shared<Camera>()); camera->AddComponent(make_shared<CameraScript>()); CUR_SCENE->Add(camera); } // Light { auto light = make_shared<GameObject>(); light->AddComponent(make_shared<Light>()); LightDesc lightDesc; lightDesc.ambient = Vec4(0.4f); lightDesc.diffuse = Vec4(1.f); lightDesc.specular = Vec4(0.1f); lightDesc.direction = Vec3(1.f, 0.f, 1.f); light->GetLight()->SetLightDesc(lightDesc); CUR_SCENE->Add(light); } // Mesh // Material { shared_ptr<Material> material = make_shared<Material>(); material->SetShader(_shader); auto texture = RESOURCES->Load<Texture>(L"UnityLogo", L"..\\Resources\\Texture\\UnityLogo.png"); material->SetDiffuseMap(texture); MaterialDesc& desc = material->GetMaterialDesc(); desc.ambient = Vec4(1.f); desc.diffuse = Vec4(1.f); desc.specular = Vec4(1.f); RESOURCES->Add(L"UnityLogo", material); } for (int32 i = 0; i < 100; i++) { auto obj = make_shared<GameObject>(); obj->GetOrAddTransform()->SetLocalPosition(Vec3(rand() % 100, 0, rand() % 100)); obj->AddComponent(make_shared<MeshRenderer>()); { obj->GetMeshRenderer()->SetMaterial(RESOURCES->Get<Material>(L"UnityLogo")); } { auto mesh = RESOURCES->Get<Mesh>(L"Sphere"); obj->GetMeshRenderer()->SetMesh(mesh); obj->GetMeshRenderer()->SetPass(0); } CUR_SCENE->Add(obj); } } void ViewportDemo::Update() { static float width = 800.f; static float height = 600.f; static float x = 0.f; static float y = 0.f; ImGui::InputFloat("Width", &width, 10.0f); ImGui::InputFloat("Height", &height, 10.0f); ImGui::InputFloat("X", &x, 10.0f); ImGui::InputFloat("Y", &y, 10.0f); GRAPHICS->SetViewport(width, height, x, y); } void ViewportDemo::Render() { }
결론
Viewport는 DirectX11 렌더링 파이프라인에서 중요한 역할을 합니다. 올바른 Viewport 설정은 게임 또는 3D 어플리케이션에서 필수적인 다양한 시각적 효과와 기능을 구현하는 데 기여합니다.
반응형다음글이전글이전 글이 없습니다.댓글