튀김족발
공부 끄적이는 공간
튀김족발
전체 방문자
오늘
어제
  • 분류 전체보기 (33)
    • C++ (10)
    • DirectX (12)
    • Unreal (11)
    • Unity (0)
    • EASTL (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
튀김족발

공부 끄적이는 공간

6. 3D 모델 렌더링
DirectX

6. 3D 모델 렌더링

2022. 11. 26. 15:20

이번에 사용할 포맷은 정말 기본적인 것만 담고 있습니다. 우선 그 모겔의 점ㅇ점을 연결하는 선을 담고 있습니다. 각 선분들은 위치벡터(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
    'DirectX' 카테고리의 다른 글
    • 8. 주변광
    • 7. Maya 2011 모델 불러오기
    • 5. 조명
    • 4. 텍스쳐
    튀김족발
    튀김족발

    티스토리툴바