D3D 공부하던 코드를 긁어온거입니다. 코드 보실때 참고만해주세요 


3D 공간에서 기존 카메라의 영향을 받지 않는 2D Obj를 만든다.

즉, D3D를 이용하여 2D 게임을 만들거나, 게임의 UI를 구성할때 많이 사용된다.

 

Sprite2D의 구성요소는 

유저에게 보여줘야할 2D Obj -> sprite

2D를 비춰줄 전용 카메라 Obj -> camera2D

2D를 이미지를 렌더링해줄 컴포넌트 ->SpriteRenederer

2D에 사용할 전용 VS셰이더와 PS셰이더 -> sprite_vertexShader, sprite_pixelShader

셰이더를 사용하면 필요한 HLSL -> sprite2D_ps.hlsl,sprite2D_vs.hlsl (따로작성)

 

<HLSL 작성>

더보기
//sprite2D.ps.hlsl

struct PixelInput
{
	float4 position : SV_POSITION;
	float4 color : COLOR;
	float2 texcoord : TEXCOORD0;
}; //빛의 영향을 받지 않기 때문에 법선벡터 data를 가질필요가 없다.

Texture2D tex : TEXCOORD: register(t0);
SamplerState sample : SAMPLER: register(s0);
float4 main(PixelInput input) : SV_TARGET
{
	return input.color * tex.Sample(sample, input.texcoord);
}

//======================================================

// sprite2D.vs.hlsl

#pragma pack_matrix(row_major)
cbuffer constants
{
	matrix worldMatrix;
	matrix viewProjectionMatrix;
};
struct VertexInput
{
	float3 position : POSITION;
	float4 color : COLOR;
	float2 texcoord : TEXCOORD0;
	float3 normal : NORMAL;
};
struct VertexOutput
{
	float4 position : SV_POSITION;
	float4 color : COLOR;
	float2 texcoord : TEXCOORD0;
}; // ps.hlsl의 input과 vs.hlsl의 output은 똑같아야한다.(중요)

VertexOutput main(VertexInput input)
{
	VertexOutput output;
	output.position = mul(float4(input.position, 1.0f), worldMatrix);
	output.position = mul(output.position, viewProjectionMatrix);
	output.color = input.color;
	output.texcoord = input.texcoord;
	return output;
}

<SpriteRenderer 생성(컴포넌트)>

더보기
//SpriteRenderer class 생성 - 컴포넌트로 사용

// DXSpriteRenderer.h
#pragma once
#include "../DXGameObject.h"
#include "../Model.h"
class DXSpriteRenderer : public RenderComponent
{
    private:
        std::unique_ptr<Mesh> mesh; //그림을 그릴 mesh
        
        ID3D11DeviceContext* deviceContext;
        ConstantBuffer<Matrices>* vsConstantBuffer; //VertexShader에서 사용할 행렬data
    public:
        DXSpriteRenderer(DXGameObject* owner); // Render컴포넌트를 자기자신으로 선언 하는 생성자
        
        //sprite이미지를 로드해서 Mesh에 초기화
        void LoadSpriteImage(ID3D11Device* device, ID3D11DeviceContext* deviceContext, ConstantBuffer<Matrices>& vsConstantBuffer, const std::string& filename);
        
    protected:
    	virtual void Opertate() override; 
};

//=========================================

// DXSpriteRenderer.cpp

#include "DXSpriteRenderer.h"
DXSpriteRenderer::DXSpriteRenderer(DXGameObject* owner) :
    RenderComponent(owner),
    deviceContext(nullptr),
    vsConstantBuffer(nullptr)
{
}


void DXSpriteRenderer::LoadSpriteImage(ID3D11Device* device, ID3D11DeviceContext* deviceContext, ConstantBuffer<Matrices>& vsConstantBuffer, const std::string& filename)
{
	//2DspriteRenderer의 deviceContext,vsConstantBuffer 초기화
	this->deviceContext, = deviceContext;
	this->vsConstantBuffer = &vsConstantBuffer;
	
    //vertex 벡터 초기화
	std::vector<ModelVertex> vertices =
	{
		ModelVertex(-0.5f, -0.5f, 0.0f,0.0f, 0.0f), // top left
		ModelVertex(0.5f, -0.5f, 0.0f,1.0f, 0.0f), // top right
		ModelVertex(-0.5f, 0.5f, 0.0f,0.0f, 1.0f), // bottom left
		ModelVertex(0.5f, 0.5f, 0.0f,1.0f, 1.0f), // bottom right
	};
    //index 벡터 초기화 
	std::vector<DWORD> indices =
	{
		0, 1, 2,
		2, 1, 3
	};
    
    //인자값으로 가져온 텍스쳐 파일이름을 Texture생성자에서 2D이미지 로드
	std::vector<Texture> textures = { Texture(device, filename) };
	//mesh에 data를 초기화해서 모델링하는 작업 
    mesh = std::make_unique<Mesh>(device, deviceContext, vertices, indices, textures, XMMatrixIdentity());
}

//constantBuffer = 정점 및 픽셀 셰이더에서 사용될 상수를 모아 놓은 버퍼이다.
//ConstantBuffer에 월드행렬과 뷰투영 행렬을 초기화 하고 메모리락(ApplyChanges) 한 뒤 mesh를 Draw한다. 
void DXSpriteRenderer::Opertate()
{	
	deviceContext->VSSetConstantBuffers(0, 1, vsConstantBuffer->GetAddressOf());
	vsConstantBuffer->data.world = mesh->GetTransformMatrix() * owner->GetTransform()->GetWorldMatrix();
	vsConstantBuffer->data.viewProjection = viewProjectionMatrix;
	vsConstantBuffer->ApplyChanges();
	mesh->Draw();
}

<DirectX 11 Graphics 의 2D sprite 추가>

: DirectX 11 Graphics : DirectX 11 기반으로 씬(Scnen)을 렌더링하기 위한 그래픽스 객체

더보기
// DirectX11Graphics.h

#include "Components/DXSpriteRenderer.h" //SpriteReneder 헤더 추가
class DirectX11Graphics final : public Graphics
{
Private:
	...
    VertexShader sprite_vertexShader; //2Dsprite VS
    PixelShader sprite_pixelShader; //2Dsprite PS
    DXGameObject camera2D; //2D 전용 카메라 Obj
    DXGameObject sprite; //2D 이미지 mesh
	...
};

//=================================

// DirectX11Graphics.cpp

void DirectX11Graphics::RenderFrame()
{
	...
    //deviceContext에 sprite관련 data를 Set(IA,VS,PS)
    deviceContext->IASetInputLayout(sprite_vertexShader.GetInputLayout());
    deviceContext->VSSetShader(sprite_vertexShader.GetShader(), NULL, 0);
    deviceContext->PSSetShader(sprite_pixelShader.GetShader(), NULL, 0);
    //Camera2D에 뷰투영행렬을 인자값으로 가져와서 draw를 실행
    sprite.Draw(camera2D.GetComponent<DXCamera>()->GetViewProjectionMatrix());
    swapChain->Present(NULL, NULL);
}

//Shader Init
bool DirectX11Graphics::InitializeShader()
{
   ...
    //vs,ps HLSL Initialize
    if (!sprite_vertexShader.Initialize(device.Get(), L"sprite2D.vs.hlsl", inputLayout,
    numElements)) return false;
    if (!sprite_pixelShader.Initialize(device.Get(), L"sprite2D.ps.hlsl")) return false;
    return true;
}

//Scene Init
bool DirectX11Graphics::InitializeScene()
{

...
		//Cam(Base컴포넌트)에 DXCamera(컴포넌트)추가
		DXCamera* cam = camera2D.AddComponent<DXCamera>();
        //Cam의 투영방법 Set : 직교 투영
		cam->SetProjectionType(ProjectionType::Orthographic);
		//카메라 좌표계 원점을 left,top에서 중앙으로 옮기는 과정 
		cam->SetProjection(screenWidth, screenHeight, -10.0f, 10.0f);

		//2D 이미지(sprite)dp Renderer 컴포넌트 추가후 이미지 로드
		sprite.AddComponent<DXSpriteRenderer>()->LoadSpriteImage(
			device.Get(), deviceContext.Get(), constantMatricesBuffer,
			"Textures\\hello_world_sprite.jpg");

		//2D이미지는 Scale 및 Translation 조정시 3D처럼 원본의 배율이 아닌 직접 값을 입력해야된다. 
		//sprite.GetTransform()->SetLocalScale({ 100.0f, 100.0f, 1 }); // Scale Set
		//sprite.GetTransform()->SetLocalRocation({ 0, 0, 30.0f }); // Rotation Set(Z축을 회전시켜야 2D이미지가 회전됨)
        //sprite.GetTransform()->SetTranslation({ 100.0f, 100.0f, 0 }); // Translation Set
        /**/


...
}

 

2D 카메라 세팅중 직교투영과 좌표계 수정이 필요하다.

bool DirectX11Graphics::InitializeScene()
{        ...
        //Cam의 투영방법 Set : 직교 투영
		cam->SetProjectionType(ProjectionType::Orthographic);
		//카메라 좌표계 원점을 left,top에서 중앙으로 옮기는 과정 
		cam->SetProjection(screenWidth, screenHeight, -10.0f, 10.0f);
        ...
}

 [2D카메라 투영 세팅]

//DXCamera 투영 행렬을 Set 하는 함수
void DXCamera::SetProjection(float width, float height, float nearZ, float farZ, float fovDegrees)
{
	this->width = width;
	this->height = height;
	this->nearZ = nearZ;
	this->farZ = farZ;
	this->fovDegrees = fovDegrees;

	switch (type)
	{ // 원근/직교(Perspective/Orthographic) 투영 행렬 생성.
	case ProjectionType::Perspective: projectionMatrix = XMMatrixPerspectiveFovLH(fovDegrees * Deg2Rad, (width / height), nearZ, farZ); break;
	case ProjectionType::Orthographic: projectionMatrix = XMMatrixOrthographicOffCenterLH(-width / 2, width / 2, height / 2, -height / 2, nearZ, farZ); break;
	}
}

DXCamera 객채 생성자에서 원근투영(Perspective)으로 set하게 해놨기 떄문에 직교투영(Orthographic)으로 set해줘야한다.

 

-원근투영

뷰투영행렬 연산을 거쳐 카메라 화면(유저가 보는 화면 = viwe port)이 모니터에 출력되는데

이때 데카르트 좌표계(정규좌표)를 사용하고 있다.

우리가 흔히 쓰는 데카르트 좌표계가 기준

 그렇기 때문에 원점(0,0)이 Width,Height 값만 있으면 원점은 정중앙으로 인식된다.

XMMatrixPerspectiveFovLH(fovDegrees * Deg2Rad, (width / height), nearZ, farZ); 

view Space의 각도(=카메라의 각도) , (카메라 화면 폭 / 카메라 화면 높이) , 가까운 Z축 plane , 멀리있는 Z축 plane

 

-직교투영

원근투영과는 다르게 직교투영은 화면좌표계(컴퓨터좌표계)를 사용한다.

원점(0,0)이 정중앙이 아닌 left,top으로 되있기 때문에 쉽게 사용하라면 원근투영처럼 원점을 정중앙으로 옮겨줘야 된다.

Y축의 증감 방향이 반대로 되어있다.

XMMatrixOrthographicOffCenterLH(-width / 2, width / 2, height / 2, -height / 2, nearZ, farZ);  

width와 height의을 절반으로 나눠 -,+값으로하면 원하는 카메라 화면을 구할수 있다.

 

 

+ Recent posts