투영은 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);
    }

     

     

     

     

     

     


     

     

     

    실행화면