이번에 사용할 포맷은 정말 기본적인 것만 담고 있습니다. 우선 그 모겔의 점ㅇ점을 연결하는 선을 담고 있습니다. 각 선분들은 위치벡터(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 | 
