[D3D] 직교투영

vector·2025년 12월 9일

직교투영

원근감(거리감) 없이, 물체의 실제 크기를 그대로 화면에 투영하는 방식
즉, 카메라에서 얼마나 멀리 있든 화면 크기가 절대 줄어들지 않는다.

이를 통해서 UI를 만들어 보자

Camera

  • ProjectionType에 따라 Projection을 따로 그려주고
  • GameObject의 레이어에 따라 카메라에서 비트플래그를 통해 그려줄지를 선택해서 렌더링
  • Define.h에 Layer관련 enum 추가하기

Camera.h

#pragma once
#include "Component.h"

enum class ProjectionType
{
	Perspective,		// 원근 투형 (3d, 원근법에 따라 크기 차이)
	Orthographic,		// 직교 투영 (2d, 화면에 들어온 모든 오브젝트는 같은 크기)
};

class Camera : public Component
{
	using Super = Component;

public:
	Camera();
	virtual ~Camera();

	virtual void Update() override;

	void SetProjectionType(ProjectionType type) { _type = type; }
	ProjectionType GetRrojectionType() { return _type; }
	void UpdateMatrix();


	void SetNear(float nearPlane) { _near = nearPlane; }
	void SetFar(float farPlane) { _far = farPlane; }
	void SetFov(float fov) { _fov = fov; }
	void SetWidth(float width) { _width = width; }
	void SetHeight(float height) { _height = height; }


	Matrix& GetViewMatrix() { return _matView; }
	Matrix& GetProjectionMatrix() { return _matProjection; }
	
	float GetWidth() { return _width; }
	float GetHeight() { return _height; }

private:
	ProjectionType _type = ProjectionType::Perspective;
	Matrix _matView = Matrix::Identity;
	Matrix _matProjection = Matrix::Identity;

	float _near = 1.f;
	float _far = 1000.f;
	float _fov = XM_PI / 4.f;
	float _width = 0.f;
	float _height = 0.f;

public:
	static Matrix S_MatView;
	static Matrix S_MatProjection;


public:
	// 관련된 문체들을 가져오기
	void SortGameObject();
	void Render_Forward();

	void SetCullingMaskLayerOnOff(uint8 layer, bool on)
	{
		if (on)
			_cullingMask |= (1 << layer);
		else
			_cullingMask &= ~(1 << layer);
	}

	// 아무것도 그리지 않겠다.
	void SetCullingMaskAll() { SetCullingMask(UINT32_MAX); }
	void SetCullingMask(uint32 mask) { _cullingMask = mask; }
	// 비트연산을 통해서 그려줄지 안그려줄지 판단
	bool IsCulled(uint8 layer) { return (_cullingMask & (1 << layer)) != 0; }

private:
	// 비트마스크 - 그릴지 안그릴지에 대한 규칙
	uint32 _cullingMask = 0;
	vector<shared_ptr<GameObject>> _vecForward;
};

Camera.cpp

void Camera::UpdateMatrix()
{
	Vec3 eyePosition = GetTransform()->GetPosition();
	Vec3 focusPosition = eyePosition + GetTransform()->GetLook();
	Vec3 upDirection = GetTransform()->GetUp();
	//S_MatView = GetTransform()->GetWorldMatrix().Invert();
	_matView = ::XMMatrixLookAtLH(eyePosition, focusPosition, upDirection);

	if (_type == ProjectionType::Perspective)
	{
		_matProjection = ::XMMatrixPerspectiveFovLH(_fov, _width / _height, _near, _far);
	}
	else
	{
		_matProjection = ::XMMatrixOrthographicLH(_width, _height, _near, _far);
	}
}

void Camera::SortGameObject()
{
	shared_ptr<Scene> scene = CUR_SCENE;
	unordered_set<shared_ptr<GameObject>>& gameObjects = scene->GetObjects();

	_vecForward.clear();

	for (auto& gameObject : gameObjects)
	{
		if (IsCulled(gameObject->GetLayerIndex()))
			continue;


		if (gameObject->GetMeshRenderer() == nullptr &&
			gameObject->GetModelRenderer() == nullptr &&
			gameObject->GetModelAnimator() == nullptr)
			continue;

		_vecForward.push_back(gameObject);
	}
}

void Camera::Render_Forward()
{
	S_MatView = _matView;
	S_MatProjection = _matProjection;

	// 카메라에서 물체 그려주기
	GET_SINGLE(InstancingManager)->Render(_vecForward);
}

Define.h

enum LayerMask
{
	Layer_Default	= 0,
	Layer_UI		= 1,
};

Scene

  • 따로 Render하는 부분 생성
  • 기존의 GetCamera()를 GetMainCamera()로 수정하고, GetUICamera() 생성
  • SceneManager에서 현재 씬의 Render를 호출

Scene.h

#pragma once
class Scene
{
public:
	virtual void Start();
	virtual void Update();
	virtual void LateUpdate();

	virtual void Render();

	virtual void Add(shared_ptr<GameObject> object);
	virtual void Remove(shared_ptr<GameObject> object);

	unordered_set<shared_ptr<GameObject>>& GetObjects() { return _objects; }

	shared_ptr<GameObject> GetMainCamera();
	shared_ptr<GameObject> GetUICamera();

	shared_ptr<GameObject> GetLight() { return _lights.empty() ? nullptr : *_lights.begin(); }

	shared_ptr<class GameObject> Pick(int32 screenX, int32 screenY);
	void CheckCollision();

private:
	unordered_set<shared_ptr<GameObject>> _objects;
	// Cache Camera
	unordered_set<shared_ptr<GameObject>> _cameras;
	// Cache Light
	unordered_set<shared_ptr<GameObject>> _lights;
};

Scene.cpp

void Scene::Render()
{
	for (auto& camera : _cameras)
	{
		camera->GetCamera()->SortGameObject();
		camera->GetCamera()->Render_Forward();
	}
}

shared_ptr<GameObject> Scene::GetMainCamera()
{
	for (auto& camera : _cameras)
	{
		if (camera->GetCamera()->GetRrojectionType() == ProjectionType::Perspective)
			return camera;
	}

	return nullptr;
}

shared_ptr<GameObject> Scene::GetUICamera()
{
	for (auto& camera : _cameras)
	{
		if (camera->GetCamera()->GetRrojectionType() == ProjectionType::Orthographic)
			return camera;
	}

	return nullptr;
}

SceneManager

void SceneManager::Update()
{
	if (_currentScene == nullptr)
		return;

	_currentScene->Update();		
	_currentScene->LateUpdate();	// 카메라가 들어감 
	_currentScene->Render();
}

메인함수(Demo)

  • 카메라를 추가로 배치
  • 버튼형식의 mesh 추가
void OrthographicDemo::Init()
{
	//RESOURCES->Init();

	_shader = make_shared<Shader>(L"23.RenderDemo.fx");

	// Camera
	{
		auto camera = make_shared<GameObject>();
		camera->GetOrAddTransform()->SetPosition(Vec3{ 0.f, 0.f, -5.f });
		camera->AddComponent(make_shared<Camera>());
		camera->AddComponent(make_shared<CameraScript>());
		camera->GetCamera()->SetCullingMaskLayerOnOff(Layer_UI, true); // ui 끄기
		CUR_SCENE->Add(camera);
	}

	// UI_Camera
	{
		auto camera = make_shared<GameObject>();
		camera->GetOrAddTransform()->SetPosition(Vec3{ 0.f, 0.f, -5.f });
		camera->AddComponent(make_shared<Camera>());
		camera->GetCamera()->SetProjectionType(ProjectionType::Orthographic);
		camera->GetCamera()->SetNear(1.f);
		camera->GetCamera()->SetFar(100.f);

		camera->GetCamera()->SetCullingMaskAll();
		camera->GetCamera()->SetCullingMaskLayerOnOff(Layer_UI, false); // ui 켜기
		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);
	}


	// Material
	{
		shared_ptr<Material> material = make_shared<Material>();
		material->SetShader(_shader);
		auto texture = RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.jpg");
		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"Veigar", material);
	}

	// Mesh 버튼 형식
	{
		auto obj = make_shared<GameObject>();
		obj->GetOrAddTransform()->SetLocalPosition(Vec3(0.f, 200.f, 0.f));
		obj->GetOrAddTransform()->SetScale(Vec3(200.f));
		obj->AddComponent(make_shared<MeshRenderer>());

		obj->SetLayerIndex(Layer_UI);
		{
			obj->GetMeshRenderer()->SetMaterial(RESOURCES->Get<Material>(L"Veigar"));
		}
		{
			auto mesh = RESOURCES->Get<Mesh>(L"Quad");
			obj->GetMeshRenderer()->SetMesh(mesh);
			obj->GetMeshRenderer()->SetPass(0);
		}

		CUR_SCENE->Add(obj);
	}

	// Mesh
	{
		auto obj = make_shared<GameObject>();
		obj->GetOrAddTransform()->SetLocalPosition(Vec3(0.f));
		obj->GetOrAddTransform()->SetScale(Vec3(2.f));
		obj->AddComponent(make_shared<MeshRenderer>());
		{
			obj->GetMeshRenderer()->SetMaterial(RESOURCES->Get<Material>(L"Veigar"));
		}
		{
			auto mesh = RESOURCES->Get<Mesh>(L"Sphere");
			obj->GetMeshRenderer()->SetMesh(mesh);
			obj->GetMeshRenderer()->SetPass(0);
		}

		CUR_SCENE->Add(obj);
	}
}

결과

참고 강의

https://www.inflearn.com/courses/lecture?courseId=329791&tab=curriculum&type=LECTURE&unitId=161178&subtitleLanguage=ko

profile
게임 클라이언트 프로그래머 준비중 (공부 및 기록용)

0개의 댓글