일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |
- 제네릭프로그래밍
- 암시적인터페이스
- uniqueptr
- 스택풀림
- effectivec++.
- sharedptr
- contextswitching
- 이른진단
- operator=
- 복사함수
- Directx9
- most vexing parse
- 게임프로그래밍
- c++
- fvf
- rcsp
- 도우미함수
- EffectiveC++
- 교차dll문제
- 상수객체참조
- 정점버퍼
- 멤버함수템플릿
- 일반화복사생성자
- private상속
- 해골책
- 가상기본클래스
- 암시적변환
- 부분복사
- RAII
- 템플릿
- Today
- Total
성공할 게임개발자
[DirectX 11] 7. 2D 랜더링 & 글꼴 엔진 본문
이 두가지를 혼합하여 2D 랜더링으로 텍스처를 업로드하고 글꼴엔진도 같이 업로드 할것임. 이글에서는 그렇고
이 두가지와 충돌처리, 상수버퍼를 활용해서
대략적으로 만들 게임에 대한 아이디어가 나왔는데 바로 가위,바위,보 게임임 쇼츠보다가 생각해낸건데 플레이어 1,2,3은 가위,바위,보 중 하나를 가지고 시작함.
플레이가 시작되면 맵에 랜덤으로 돌아다니는 가위 바위 보 와 맞 닿았을 때, 내가 지면 삭제되거나 다른 동일한 개체로 이동하고 이기면 나와 대결한 개체를 내 문양으로 바꿈.
결과적으로 바운더리 내의 모든 문양을 내 문양으로 만들면 승리. 간단한 게임임
3인 게임이라 밸런스도 맞을 거 같고...
여튼 결과는 이렇다.
눈여겨 봐야할점은
글자를 랜더링하기위해
fontShaderClass, FontClass, TextClass를 사용했다는 것이고
2D랜더링을 위해
TextureShaderClass, BitmapClass를 사용했다는 점이다. 기계에 기능을 추가하듯 해당 클래스들을 생성하고 applicationClass와 D3DClass 등에서 요소들을 잘 이어주면 작동한다는 소리이다.
2D 렌더링
BitmapClass
BitmapClass는 화면에 그리는데 필요한 각 이미지를 표현하는데 사용됨. 따라서 모든 2D 이미지에 대해 각각의 BitmapClass를 만들어주어야함 이 클래스는 3D객체 대신 2D 이미지를 다루는 ModelClass의 변형임
////////////////////////////////////////////////////////////////////////////////
// Filename: bitmapclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _BITMAPCLASS_H_
#define _BITMAPCLASS_H_
//////////////
// INCLUDES //
//////////////
#include <directxmath.h>
using namespace DirectX;
///////////////////////
// MY CLASS INCLUDES //
///////////////////////
#include "textureclass.h"
////////////////////////////////////////////////////////////////////////////////
// Class name: BitmapClass
////////////////////////////////////////////////////////////////////////////////
class BitmapClass
{
private:
struct VertexType
{
XMFLOAT3 position;
XMFLOAT2 texture;
};
public:
BitmapClass();
BitmapClass(const BitmapClass&);
~BitmapClass();
bool Initialize(ID3D11Device*, ID3D11DeviceContext*, int, int, char*, int, int);
void Shutdown();
bool Render(ID3D11DeviceContext*,int ,int);
int GetIndexCount();
ID3D11ShaderResourceView* GetTexture();
void SetRenderLocation(int, int);
private:
bool InitializeBuffers(ID3D11Device*);
void ShutdownBuffers();
bool UpdateBuffers(ID3D11DeviceContext*, int, int);
void RenderBuffers(ID3D11DeviceContext*);
bool LoadTexture(ID3D11Device*, ID3D11DeviceContext*, char*);
void ReleaseTexture();
private:
ID3D11Buffer* m_vertexBuffer, * m_indexBuffer;
int m_vertexCount, m_indexCount, m_screenWidth, m_screenHeight, m_bitmapWidth, m_bitmapHeight, m_renderX, m_renderY, m_prevPosX, m_prevPosY;
TextureClass* m_Texture;
};
#endif
2D 이미지에는 단순히 위치 벡터와 텍스처 좌표만이 필요함
struct VertexType
{
XMFLOAT3 position;
XMFLOAT2 texture;
};
또한 3D모델과는 달리 BitmapClass에서는 화면 크기, 이미지크기, 이전에 그려졌던 위치를 기억해야함.
BitmapClass.cpp
bool BitmapClass::Initialize(ID3D11Device* device, ID3D11DeviceContext* deviceContext, int screenWidth, int screenHeight, char* textureFilename, int renderX, int renderY)
{
bool result;
// Store the screen size.
m_screenWidth = screenWidth;
m_screenHeight = screenHeight;
// Store where the bitmap should be rendered to.
m_renderX = renderX;
m_renderY = renderY;
// Initialize the vertex and index buffer that hold the geometry for the bitmap quad.
result = InitializeBuffers(device);
if (!result)
{
return false;
}
// Load the texture for this bitmap.
result = LoadTexture(device, deviceContext, textureFilename);
if (!result)
{
return false;
}
return true;
}
Initialize함수에서 화면 크기와 이미지 크기를 저장하고 InitializeBuffers로 DirectX 11 그래픽 파이프라인에 버퍼(vertex buffer, index buffer)를 생성하고 설정함. 보다 정확히 말하면 GPU가 사용할 수 있는 정점 및 인덱스 버퍼를 생성하고 이를 GPU의 메모리에 할당하는 단계
bool BitmapClass::InitializeBuffers(ID3D11Device* device)
{
VertexType* vertices;
unsigned long* indices;
D3D11_BUFFER_DESC vertexBufferDesc, indexBufferDesc;
D3D11_SUBRESOURCE_DATA vertexData, indexData;
HRESULT result;
int i;
// Initialize the previous rendering position to negative one.
m_prevPosX = -1;
m_prevPosY = -1;
// Set the number of vertices in the vertex array.
m_vertexCount = 6;
// Set the number of indices in the index array.
m_indexCount = m_vertexCount;
// Create the vertex array.
vertices = new VertexType[m_vertexCount];
// Create the index array.
indices = new unsigned long[m_indexCount];
// Initialize vertex array to zeros at first.
memset(vertices, 0, (sizeof(VertexType) * m_vertexCount));
// Load the index array with data.
for (i = 0; i < m_indexCount; i++)
{
indices[i] = i;
}
// Set up the description of the dynamic vertex buffer.
vertexBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
vertexBufferDesc.ByteWidth = sizeof(VertexType) * m_vertexCount;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
vertexBufferDesc.MiscFlags = 0;
vertexBufferDesc.StructureByteStride = 0;
// Give the subresource structure a pointer to the vertex data.
vertexData.pSysMem = vertices;
vertexData.SysMemPitch = 0;
vertexData.SysMemSlicePitch = 0;
// Now finally create the vertex buffer.
result = device->CreateBuffer(&vertexBufferDesc, &vertexData, &m_vertexBuffer);
if (FAILED(result))
{
return false;
}
// Set up the description of the index buffer.
indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
indexBufferDesc.ByteWidth = sizeof(unsigned long) * m_indexCount;
indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
indexBufferDesc.CPUAccessFlags = 0;
indexBufferDesc.MiscFlags = 0;
indexBufferDesc.StructureByteStride = 0;
// Give the subresource structure a pointer to the index data.
indexData.pSysMem = indices;
indexData.SysMemPitch = 0;
indexData.SysMemSlicePitch = 0;
// Create the index buffer.
result = device->CreateBuffer(&indexBufferDesc, &indexData, &m_indexBuffer);
if (FAILED(result))
{
return false;
}
// Release the arrays now that the vertex and index buffers have been created and loaded.
delete[] vertices;
vertices = 0;
delete[] indices;
indices = 0;
return true;
}
버퍼를 설정, 생성하고 GPU에 넘겨줌
bool BitmapClass::LoadTexture(ID3D11Device* device, ID3D11DeviceContext* deviceContext, char* filename)
{
bool result;
// Create and initialize the texture object.
m_Texture = new TextureClass;
result = m_Texture->Initialize(device, deviceContext, filename);
if (!result)
{
return false;
}
// Store the size in pixels that this bitmap should be rendered at.
m_bitmapWidth = m_Texture->GetWidth()/2;
m_bitmapHeight = m_Texture->GetHeight()/2;
return true;
}
텍스처를 1/2로 가져와서 (임의로 그렇게 그냥 정함) 텍스처 리소스를 GPU메모리에 로드하는 과정. 이 과정 이후 텍스처를 펙셀 셰이더에 바인딩해야함.
BitmapClass::Render
bool BitmapClass::Render(ID3D11DeviceContext* deviceContext, int positionX, int positionY)
{
bool result;
// Update the buffers if the position of the bitmap has changed from its original position.
result = UpdateBuffers(deviceContext, positionX, positionY);
if (!result)
{
return false;
}
// Put the vertex and index buffers on the graphics pipeline to prepare them for drawing.
RenderBuffers(deviceContext);
return true;
}
applicationclass.cpp의 render함수에서 bitmapclass의 render함수를 실행해서 버퍼를 업데이트하고 그래픽 파이프라인에 바인딩하는 역할을 함. 즉 렌더링을 위한 준비단계
UpdateBuffers
bool BitmapClass::UpdateBuffers(ID3D11DeviceContext* deviceContent, int positionX, int positionY)
{
float left, right, top, bottom;
VertexType* vertices;
D3D11_MAPPED_SUBRESOURCE mappedResource;
VertexType* dataPtr;
HRESULT result;
// If the position we are rendering this bitmap to hasn't changed then don't update the vertex buffer.
if ((m_prevPosX == positionX) && (m_prevPosY == positionY))
{
return true;
}
// If the rendering location has changed then store the new position and update the vertex buffer.
m_prevPosX = positionX;
m_prevPosY = positionY;
// Create the vertex array.
vertices = new VertexType[m_vertexCount];
// Calculate the screen coordinates of the left side of the bitmap.
left = (float)((m_screenWidth / 2) * -1) + (float)positionX;
// Calculate the screen coordinates of the right side of the bitmap.
right = left + (float)m_bitmapWidth;
// Calculate the screen coordinates of the top of the bitmap.
top = (float)(m_screenHeight / 2) - (float)positionY;
// Calculate the screen coordinates of the bottom of the bitmap.
bottom = top - (float)m_bitmapHeight;
// Load the vertex array with data.
// First triangle.
vertices[0].position = XMFLOAT3(left, top, 0.0f); // Top left.
vertices[0].texture = XMFLOAT2(0.0f, 0.0f);
vertices[1].position = XMFLOAT3(right, bottom, 0.0f); // Bottom right.
vertices[1].texture = XMFLOAT2(1.0f, 1.0f);
vertices[2].position = XMFLOAT3(left, bottom, 0.0f); // Bottom left.
vertices[2].texture = XMFLOAT2(0.0f, 1.0f);
// Second triangle.
vertices[3].position = XMFLOAT3(left, top, 0.0f); // Top left.
vertices[3].texture = XMFLOAT2(0.0f, 0.0f);
vertices[4].position = XMFLOAT3(right, top, 0.0f); // Top right.
vertices[4].texture = XMFLOAT2(1.0f, 0.0f);
vertices[5].position = XMFLOAT3(right, bottom, 0.0f); // Bottom right.
vertices[5].texture = XMFLOAT2(1.0f, 1.0f);
// Lock the vertex buffer.
result = deviceContent->Map(m_vertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
if (FAILED(result))
{
return false;
}
// Get a pointer to the data in the constant buffer.
dataPtr = (VertexType*)mappedResource.pData;
// Copy the data into the vertex buffer.
memcpy(dataPtr, (void*)vertices, (sizeof(VertexType) * m_vertexCount));
// Unlock the vertex buffer.
deviceContent->Unmap(m_vertexBuffer, 0);
// Release the pointer reference.
dataPtr = 0;
// Release the vertex array as it is no longer needed.
delete[] vertices;
vertices = 0;
return true;
}
DirectX 11에서 동적으로 정점버퍼를 업데이트하여 비트맵의 새로운 위치를 반영하는 역할을함. 이동이없으면 계산을 생략하여 성능이 올라감.
6개의 정점을 사용하여 삼각형 두개를 만들어 랜더링할 사각형을 만들어주고
// Lock the vertex buffer.
result = deviceContent->Map(m_vertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
if (FAILED(result))
{
return false;
}
Map() 함수를 사용하여 GPU에 있는 기존 정점 버퍼 데이터를 CPU에서 수정할 수 있도록 설정
D3D11_MAP_WRITE_DISCARD 모드를 사용하여 기존 데이터를 폐기하고 새로운 데이터를 작성
mappedResource.pData가 GPU 메모리 내의 정점 데이터 주소를 가리킴
memcpy()를 사용하여 새롭게 생성한 정점 데이터를 GPU 메모리에 복사
이 과정을 통해 입력 어셈블러(IA) 단계에서 사용될 정점 데이터를 업데이트하는 역할을함
D3DClass
ID3D11BlendState* m_alphaEnableBlendingState;
ID3D11BlendState* m_alphaDisableBlendingState;
2D 랜더링 시 z축 버퍼는 필요없으므로 2D용 깊이 스텐실 상태 변수를 설정하고 2D 랜더링 시 이를 해제/활성화 할 수 있는 상태의 함수를 만들어야함
// Clear the second depth stencil state before setting the parameters.
ZeroMemory(&depthDisabledStencilDesc, sizeof(depthDisabledStencilDesc));
// Now create a second depth stencil state which turns off the Z buffer for 2D rendering. The only difference is
// that DepthEnable is set to false, all other parameters are the same as the other depth stencil state.
depthDisabledStencilDesc.DepthEnable = false;
depthDisabledStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
depthDisabledStencilDesc.DepthFunc = D3D11_COMPARISON_LESS;
depthDisabledStencilDesc.StencilEnable = true;
depthDisabledStencilDesc.StencilReadMask = 0xFF;
depthDisabledStencilDesc.StencilWriteMask = 0xFF;
depthDisabledStencilDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
depthDisabledStencilDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;
depthDisabledStencilDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
depthDisabledStencilDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
depthDisabledStencilDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
depthDisabledStencilDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR;
depthDisabledStencilDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
depthDisabledStencilDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
// Create the state using the device.
result = m_device->CreateDepthStencilState(&depthDisabledStencilDesc, &m_depthDisabledStencilState);
if (FAILED(result))
{
return false;
}
D3DClass의 Initialize함수에서 깊이 스텐실을 비활성화하는 구조체의 description을 초기화하는 단계
depthDisabledStencilDesc.DepthEnable = false; 로 설정하여 깊이 버퍼를 비활성화
가장 이상적인 매커니즘은 3D렌더링을 수행하고 Z버퍼를 끄고 2D렌더링을 수행하고 다시 Z버퍼를 키는 것
글꼴엔진
글자를 그리기 위해서는 글자의 텍스처를 인덱스를 이용해 그려냄. 2D렌더링과 마찬가지로 구조가 거의 유사함. 그럴 수 밖에 없는 것이 2D에서도 텍스처, 인덱스를 이용하여 특정 위치에 사각형을 그려내었고 글자도 마찬가지로 이와같은 과정을 거치기 때문
적용 순서는 위의 2D와 같다.
application->initialize
textclass->initialize->initializebuffer
이 외에는 2D하듯이 코드를 만들면 된다.
해본김에 마우스 커서도 렌더링 하였다. 마우스 커서는 systemclass에서 application에게 준 m_input 객체를 커서 비트맵에 넘겨주는 방식으로 구현하였다.
다음 목표
1. 충돌처리
2. 마우스 가두기
3. 게임 시퀀스 짜기
'DirectX 11' 카테고리의 다른 글
[DirectX 11] 6. 정반사광, 주변광 (0) | 2025.02.20 |
---|---|
[DirectX 11] 5. 조명 (0) | 2025.02.15 |
[DirectX 11] 4. 텍스쳐 (0) | 2025.02.14 |
[DirectX 11] 3. 버퍼, 셰이더, HLSL (0) | 2025.02.13 |
[DirectX 11] 2. DirectX 초기화 (1) | 2025.02.12 |