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으로 되있기 때문에 쉽게 사용하라면 원근투영처럼 원점을 정중앙으로 옮겨줘야 된다.
XMMatrixOrthographicOffCenterLH(-width / 2, width / 2, height / 2, -height / 2, nearZ, farZ);
width와 height의을 절반으로 나눠 -,+값으로하면 원하는 카메라 화면을 구할수 있다.
'컴퓨터 그래픽스' 카테고리의 다른 글
[DirectX11] 절두체 컬링(Frustum Culing) (0) | 2023.06.21 |
---|---|
[DirectX11] 계층구조(Hierarchy) - D3D 태양계 만들기 (0) | 2023.06.13 |
그래픽스 파이프라인 간단 내용 정리 (0) | 2023.06.08 |
기본용어 정리 (0) | 2023.06.03 |