이번에 사용할 포맷은 정말 기본적인 것만 담고 있습니다. 우선 그 모겔의 점ㅇ점을 연결하는 선을 담고 있습니다. 각 선분들은 위치벡터(x,y,z)와 텍스쳐 좌표 (tu, tv), 그리고 법선 벡터 (nx, ny, nz)를 가지는 정점 포맷과 일치합니다. 이 포맷은 또한 가장 위에 정점의 갯수가 있어서 첫번째 라인을 읽고 데이터를 읽기 위한 준비로 구조체들을 위한 메모리들을 미리 할당할 수 있습니다. 또한 세 개의 선분이 삼각형을 만들며, 각 삼각형의 정점들은 시계 방향으로 배열되어 있어야 합니다. 아래에 앞으로 렌더링에 사용할 육면체의 모델이 있습니다.
Cube.txt
Vertex Count: 36
Data:
-1.0 1.0 -1.0 0.0 0.0 0.0 0.0 -1.0
1.0 1.0 -1.0 1.0 0.0 0.0 0.0 -1.0
-1.0 -1.0 -1.0 0.0 1.0 0.0 0.0 -1.0
-1.0 -1.0 -1.0 0.0 1.0 0.0 0.0 -1.0
1.0 1.0 -1.0 1.0 0.0 0.0 0.0 -1.0
1.0 -1.0 -1.0 1.0 1.0 0.0 0.0 -1.0
1.0 1.0 -1.0 0.0 0.0 1.0 0.0 0.0
1.0 1.0 1.0 1.0 0.0 1.0 0.0 0.0
1.0 -1.0 -1.0 0.0 1.0 1.0 0.0 0.0
1.0 -1.0 -1.0 0.0 1.0 1.0 0.0 0.0
1.0 1.0 1.0 1.0 0.0 1.0 0.0 0.0
1.0 -1.0 1.0 1.0 1.0 1.0 0.0 0.0
1.0 1.0 1.0 0.0 0.0 0.0 0.0 1.0
-1.0 1.0 1.0 1.0 0.0 0.0 0.0 1.0
1.0 -1.0 1.0 0.0 1.0 0.0 0.0 1.0
1.0 -1.0 1.0 0.0 1.0 0.0 0.0 1.0
-1.0 1.0 1.0 1.0 0.0 0.0 0.0 1.0
-1.0 -1.0 1.0 1.0 1.0 0.0 0.0 1.0
-1.0 1.0 1.0 0.0 0.0 -1.0 0.0 0.0
-1.0 1.0 -1.0 1.0 0.0 -1.0 0.0 0.0
-1.0 -1.0 1.0 0.0 1.0 -1.0 0.0 0.0
-1.0 -1.0 1.0 0.0 1.0 -1.0 0.0 0.0
-1.0 1.0 -1.0 1.0 0.0 -1.0 0.0 0.0
-1.0 -1.0 -1.0 1.0 1.0 -1.0 0.0 0.0
-1.0 1.0 1.0 0.0 0.0 0.0 1.0 0.0
1.0 1.0 1.0 1.0 0.0 0.0 1.0 0.0
-1.0 1.0 -1.0 0.0 1.0 0.0 1.0 0.0
-1.0 1.0 -1.0 0.0 1.0 0.0 1.0 0.0
1.0 1.0 1.0 1.0 0.0 0.0 1.0 0.0
1.0 1.0 -1.0 1.0 1.0 0.0 1.0 0.0
-1.0 -1.0 -1.0 0.0 0.0 0.0 -1.0 0.0
1.0 -1.0 -1.0 1.0 0.0 0.0 -1.0 0.0
-1.0 -1.0 1.0 0.0 1.0 0.0 -1.0 0.0
-1.0 -1.0 1.0 0.0 1.0 0.0 -1.0 0.0
1.0 -1.0 -1.0 1.0 0.0 0.0 -1.0 0.0
1.0 -1.0 1.0 1.0 1.0 0.0 -1.0 0.0
위 내용에서 볼 수 있듯이 x, y, z, tu, tv, ux, uy, uz로 이루어진 36줄을 볼 수 있습니다. 3줄마다 삼각형이 하나 만들어지므로 12개의 삼각형으로 이루어진 큐브(육면체)를 볼 수 있게 됩니다. 이 포캣은 굉장히 직관적이고 아무런 수정 없이 정점 버퍼에 넣어 그려낼 수 있습니다.
한가지 더 살펴보아야 할 것은 3D 모델링 프로그램이 어떤 좌표계를 쓰는지, 오른손 좌표계인지 왼손 좌표계인지를 알아야 합니다. DirectX 11 에서는 기본값으로 왼손 좌표계를 쓰므로 모델 데이터도 왼손 좌표계에 맞게 되어 있어야 합니다. 그 차이를 계속 주시하고 파서 프로그램이 그런 좌표계를 올바르게 다루는지 확인해야 할 것입니다.
ModelCalss.h
#pragma once
class TextureClass;
class ModelClass : public AlignedAllocationPolicy<16>
{
private:
struct VertexType
{
XMFLOAT3 position;
XMFLOAT2 texture;
XMFLOAT3 normal;
};
struct ModelType
{
float x,y,z;
float tu,tv;
float nx, ny, nz;
};
public:
ModelClass();
ModelClass(const ModelClass& modelClass);
~ModelClass();
bool Initialize(ID3D11Device* device, char* modelFileName, WCHAR* textureFileName);
void ShutDown();
void Render(ID3D11DeviceContext* deviceContext);
int GetIndexCount();
ID3D11ShaderResourceView* GetTexture();
private:
bool InitializeBuffers(ID3D11Device* device);
void ShutdownBuffers();
void RenderBuffers(ID3D11DeviceContext* deviceContext);
bool LoadTexture(ID3D11Device* device, WCHAR* fileName);
void ReleaseTexture();
bool LoadModel(char* fileName);
void ReleaseModel();
private:
ID3D11Buffer* _vertexBuffer = nullptr;
ID3D11Buffer* _indexBuffer = nullptr;
int _vertexCount = 0;
int _indexCount = 0;
TextureClass* _texture = nullptr;
ModelType* _model = nullptr;
};
ModelClass.cpp
#include "stdafx.h"
#include "TextureClass.h"
#include "ModelClass.h"
#include <fstream>
using namespace std;
ModelClass::ModelClass()
{
}
ModelClass::ModelClass(const ModelClass& modelClass)
{
}
ModelClass::~ModelClass()
{
}
bool ModelClass::Initialize(ID3D11Device* device, char* modelFileName, WCHAR* textureFileName)
{
if (LoadModel(modelFileName) == false)
{
return false;
}
// 정점 및 인덱스 버퍼를 초기화 합니다.
if (InitializeBuffers(device) == false)
{
return false;
}
// 이 모델의 텍스처를 로드합니다.
return LoadTexture(device, textureFileName);
}
void ModelClass::ShutDown()
{
// 모델 텍스처를 반환합니다.
ReleaseTexture();
// 버텍스 및 인덱스 버퍼를 종료합니다.
ShutdownBuffers();
// 모델 데이터 반환
ReleaseModel();
}
void ModelClass::Render(ID3D11DeviceContext* deviceContext)
{
// 그리기를 준비하기 위해 그래픽 파이프 라인에 꼭지점과 인덱스 버퍼를 놓습니다.
RenderBuffers(deviceContext);
}
int ModelClass::GetIndexCount()
{
return _indexCount;
}
ID3D11ShaderResourceView* ModelClass::GetTexture()
{
return _texture->GetTexture();
}
bool ModelClass::InitializeBuffers(ID3D11Device* device)
{
// 정점 배열을 만듭니다.
VertexType* vertices = new VertexType[_vertexCount];
if (vertices == nullptr)
{
return false;
}
// 인ㄷㄱ스 배열을 만듭니다.
unsigned long* indices = new unsigned long[_indexCount];
if (indices == nullptr)
{
return false;
}
// 정점 배열과 인덱스 배열을 데이터로 읽어옵니다.
for (int i = 0; i < _vertexCount; i++)
{
vertices[i].position = XMFLOAT3(_model[i].x, _model[i].y, _model[i].z);
vertices[i].texture = XMFLOAT2(_model[i].tu, _model[i].tv);
vertices[i].normal = XMFLOAT3(_model[i].nx, _model[i].ny, _model[i].nz);
indices[i] = i;
}
// 정적 정점 버퍼의 구조체를 설정합니다.
D3D11_BUFFER_DESC vertexBufferDesc;
vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
vertexBufferDesc.ByteWidth = sizeof(VertexType) * _vertexCount;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexBufferDesc.CPUAccessFlags = 0;
vertexBufferDesc.MiscFlags = 0;
vertexBufferDesc.StructureByteStride = 0;
// subresource 구조에 정점 데이터에 대한 포인터를 제공합니다.
D3D11_SUBRESOURCE_DATA vertexData;
vertexData.pSysMem = vertices;
vertexData.SysMemPitch = 0;
vertexData.SysMemSlicePitch = 0;
// 이제 정점 버퍼를 만듭니다.
if (FAILED(device->CreateBuffer(&vertexBufferDesc, &vertexData, &_vertexBuffer)))
{
return false;
}
// 정점 인덱스 버퍼의 구조체를 설정합니다.
D3D11_BUFFER_DESC indexBufferDesc;
indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
indexBufferDesc.ByteWidth = sizeof(unsigned long) * _indexCount;
indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
indexBufferDesc.CPUAccessFlags = 0;
indexBufferDesc.MiscFlags = 0;
indexBufferDesc.StructureByteStride = 0;
// 인덱스 데이터를 가리키는 보조 리소스 구조체를 작성합니다.
D3D11_SUBRESOURCE_DATA indexData;
indexData.pSysMem = indices;
indexData.SysMemPitch = 0;
indexData.SysMemSlicePitch = 0;
// 인덱스 버퍼를 생성합니다.
if (FAILED(device->CreateBuffer(&indexBufferDesc, &indexData, &_indexBuffer)))
{
return false;
}
// 생성되고 값이 할당된 정점 버퍼와 인덱스 버퍼를 해제합니다.
delete[] vertices;
vertices = 0;
delete[] indices;
indices = 0;
return true;
}
void ModelClass::ShutdownBuffers()
{
// 인덱스 버퍼를 해제합니다.
if (_indexBuffer)
{
_indexBuffer->Release();
_indexBuffer = 0;
}
// 정점 버퍼를 해제합니다.
if (_vertexBuffer)
{
_vertexBuffer->Release();
_vertexBuffer = 0;
}
}
void ModelClass::RenderBuffers(ID3D11DeviceContext* deviceContext)
{
// 정점 버퍼의 단위와 오프셋을 설정합니다.
UINT stride = sizeof(VertexType);
UINT offset = 0;
// 렌더링 할 수 있도록 입력 어셈블러에서 정점 버퍼를 활성으로 설정합니다.
deviceContext->IASetVertexBuffers(0, 1, &_vertexBuffer, &stride, &offset);
// 렌더링 할 수 있도록 입력 어셈블러에서 인덱스 버퍼를 활성으로 설정합니다.
deviceContext->IASetIndexBuffer(_indexBuffer, DXGI_FORMAT_R32_UINT, 0);
// 정점 버퍼로 그릴 기본형을 설정합니다. 여기서는 삼각형으로 설정합니다.
deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
}
bool ModelClass::LoadTexture(ID3D11Device* device, WCHAR* fileName)
{
_texture = new TextureClass();
if (_texture == nullptr)
{
return false;
}
// 텍스쳐 오브젝트를 초기화한다.
return _texture->Initialize(device, fileName);
}
void ModelClass::ReleaseTexture()
{
// 텍스쳐 오브젝트를 릴리즈한다.
if (_texture)
{
_texture->Shutdown();
SAFE_DELETE(_texture);
}
}
bool ModelClass::LoadModel(char* fileName)
{
// 모델 파일을 엽니다.
ifstream fin;
fin.open(fileName);
// 파일을 열 수 없으면 종료합니다.
if (fin.fail())
{
return false;
}
// 버텍스 카운트의 값까지 읽는다
char input = 0;
fin.get(input);
while (input != ':')
{
fin.get(input);
}
// 버텍스 카운트를 읽는다.
fin >> _vertexCount;
// 인덱스의 수를 정점의 수와 같게 설정합니다.
_indexCount = _vertexCount;
// 읽어 들인 정점 갯수를 사용하여 모델을 만듭니다.
_model = new ModelType[_vertexCount];
if (_model == nullptr)
{
return false;
}
// 데이터의 시작 부분까지 읽는다.
fin.get(input);
while (input != ':')
{
fin.get(input);
}
fin.get(input);
// 버텍스 데이터를 읽습니다.
for (int i = 0; i < _vertexCount; i++)
{
fin >> _model[i].x >> _model[i].y >> _model[i].z;
fin >> _model[i].tu >> _model[i].tv;
fin >> _model[i].nx >> _model[i].ny >> _model[i].nz;
}
// 모델 파일을 닫는다.
fin.close();
return true;
}
void ModelClass::ReleaseModel()
{
SAFE_DELETE_ARRAY(_model);
}
모델 객체를 생성한 후 초기화시에 Cube.txt 모델 렌더링 파일을 읽어오도록 하였습니다.
GraphicClass.cpp
#include "stdafx.h"
#include "d3dclass.h"
#include "CameraClass.h"
#include "ModelClass.h"
#include "LightShaderClass.h"
#include "LightClass.h"
#include "GraphicsClass.h"
GraphicsClass::GraphicsClass()
{
}
GraphicsClass::GraphicsClass(const GraphicsClass& other)
{
}
GraphicsClass::~GraphicsClass()
{
}
bool GraphicsClass::Initialize(int screenWidth, int screenHeight, HWND hwnd)
{
// Direct3D 객체 생성
//_direct3D = (D3DClass*) SAFE_ALIGNED_NEW(sizeof(D3DClass), 16);
_direct3D = new D3DClass();
if (!_direct3D)
{
return false;
}
// Direct3D 객체 초기화
if (!_direct3D->Initialize(screenWidth, screenHeight, VSYNC_ENABLED, hwnd, FULL_SCREEN, SCREEN_DEPTH, SCREEN_NEAR))
{
MessageBox(hwnd, L"Could not initialze Direct3D", L"Error", MB_OK);
return false;
}
// _camera 객체 생성
_camera = new CameraClass();
if (_camera == nullptr)
{
return false;
}
// 카메라 포지션 변경
_camera->SetPosition(0.0f, 0.0f, -5.0f);
// _model 객체 생성
_model = new ModelClass();
if (_model == nullptr)
{
return false;
}
// _model 객체 초기화
if (!_model->Initialize(_direct3D->GetDevice(), "Data/Cube.txt", L"Data/seafloor.dds"))
{
MessageBox(hwnd, L"Could not initialize the model object.", L"Error", MB_OK);
return false;
}
// _lightShader 생성
_lightShader = new LightShaderClass();
if (_lightShader == nullptr)
{
return false;
}
// _lightShader 객체 초기화
if (!_lightShader->Initialize(_direct3D->GetDevice(), hwnd))
{
MessageBox(hwnd, L"Could not initialize the color shader object.", L"Error", MB_OK);
return false;
}
// _light 객체 생성
_light = new LightClass();
if (_light == nullptr)
{
return false;
}
_light->SetDiffuseColor(1.0f, 1.0f, 1.0f, 1.0f);
_light->SetDirection(0.0f, 0.0f, 1.0f);
return true;
}
void GraphicsClass::Shutdown()
{
if (_lightShader)
{
_lightShader->ShutDown();
SAFE_DELETE(_lightShader);
}
if (_light)
{
SAFE_DELETE(_light);
}
if (_model)
{
_model->ShutDown();
SAFE_DELETE(_model);
}
// _camera 객체 반환
SAFE_DELETE(_camera);
// Direct3D 객체 반환
if (_direct3D)
{
_direct3D->Shutdown();
SAFE_DELETE(_direct3D);
}
}
bool GraphicsClass::Frame()
{
static float rotation = 0.0f;
// 각 프레임의 rotation 변수를 업데이트 합니다.
rotation += (float)XM_PI * 0.01f;
if (rotation > 360.0f)
{
rotation -= 360.0f;
}
// 그래픽 랜더링 처리
return Render(rotation);
}
bool GraphicsClass::Render(float rotation)
{
// 씬을 그리기 위해 버퍼를 지웁니다.
_direct3D->BeginScene(0.0f, 0.0f, 0.0f, 1.0f);
// 카메라의 위치에 따라 뷰 행렬을 생성합니다.
_camera->Render();
// 카메라 및 d3d 객체에서 월드, 뷰 및 투영 행렬을 가져옵니다.
XMMATRIX worldMatrix, viewMatrix, projectionMatrix;
_direct3D->GetWorldMatrix(worldMatrix);
_camera->GetViewMatrix(viewMatrix);
_direct3D->GetProjectionMatrix(projectionMatrix);
// 삼각형이 회전 할 수 있도록 회전 값으로 월드 행렬을 회전합니다.
worldMatrix = XMMatrixRotationY(rotation);
// 모델 버텍스와 인덱스 버퍼를 그래픽 파이프 라인에 배치하여 드로잉을 준비합니다.
_model->Render(_direct3D->GetDeviceContext());
// 텍스처 쉐이더를 사용하여 모델을 렌더링 합니다.
if (!_lightShader->Render(_direct3D->GetDeviceContext(), _model->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix,
_model->GetTexture(), _light->GetDirection(), _light->GetDiffuseColor()))
{
return false;
}
// 버퍼의 내용을 화면에 출력합니다.
_direct3D->EndScene();
return true;
}
출력화면
'DirectX' 카테고리의 다른 글
8. 주변광 (0) | 2022.12.06 |
---|---|
7. Maya 2011 모델 불러오기 (2) | 2022.12.05 |
5. 조명 (0) | 2022.11.21 |
4. 텍스쳐 (0) | 2022.11.11 |
3. 버퍼, 쉐이더 및 HLSL (0) | 2022.10.10 |