DirectX11 Tutorial 7 - 3D 모델 렌더링

강좌번역/DirectX 11 2013. 2. 2. 17:28 by 빠재

원문: http://www.rastertek.com/dx11tut07.html



이번 튜토리얼에서는 HLSL을 이용하여 DirectX 11에서 3D 모델을 그리는 방법을 다룰 것입니다. 튜토리얼의 코드는 이전의 조명 튜토리얼에 기초해 있습니다.


이미 우리는 이전 튜토리얼에서 3D 모델을 그리긴 했었지만 단순히 하나의 삼각형이었기에 재미가 별로 없었을 겁니다. 지금은 기초를 단단히 다졌기 때문에 앞으로 좀 더 복잡한 물체를 그려볼 것입니다. 여기서는 육면체(cube)를 그릴 것입니다. 더 복잡한 모델들을 그리기에 앞서 우선 모델의 포맷(구성)에 대해 이야기해야 합니다.


사용자들이 3D 모델을 만들 수 있게 도와주는 툴들이 정말 많습니다. 그중 가장 유명한 것 들 중 두 개만 뽑자면 마야(Maya)와 3D Studio Max가 있습니다. 그 외에도 기능은 적지만 우리가 필요로 하는 기본은 되어 있는 것들이 많이 있습니다.


여러분이 어떤 툴을 사용하기로 결정하는 것에 상관없이 그 툴들은 각자 모델들을 수많은 형태의 포맷으로 출력할 수 있습니다. 차라리 저는 여러분만의 모델 포맷을 만들고 툴에서 만들어낸 포맷을 여러분의 포맷으로 바꿔주는 파서를 만드는 것을 추천합니다. 언젠가는 여러분이 사용하는 툴의 모델 포맷이 바뀌어서 읽어들이는 것을 다시 만들어야 하는 경우가 발생할 수 있기 때문입니다. 그리고 사용하는 툴의 종류가 둘 이상이라면, 처리해야 할 포맷이 훨씬 많아집니다. 따라서 자신만의 포맷이 있고 툴들의 포맷을 여러분들의 포맷으로 바꾼다면 여러분의 코드는 더 이상 수정될 필요가 없습니다. 단지 파서 역할을 하는 프로그램만 고치면 되는 것이죠. 또한 대부분의 3D 모델링 패키지들은 그 프로그램에게는 필요하겠지만 여러분의 프로그램에는 전혀 필요가 없는 수많은 쓰레기값들을 담고 있습니다.


여러분만의 포맷을 만드는 것이 좋은 가장 큰 이유는 바로 여러분이 필요한 모든 값을 갖고 있고 (여러분에게) 사용하기 쉽다는 것입니다. 또한 애니메이션이나, 정적인 것이나 그외에 것들을 각기 저장하는 여러 포맷을 만드는 것을 생각해 볼 수 있을 것입니다.


제가 앞으로 보여드릴 포맷은 정말로 기본적인 것만 담고 있습니다. 우선 그 모델의 정점들을 연결하는 선을 담고 있습니다. 각 선분들은 위치벡터(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, nx, ny, nz로 이루어진 36줄을 볼 수 있습니다. 3줄마다 삼각형이 하나 만들어지므로 12개의 삼각형으로 이루어진 육면체를 볼 수 있게 됩니다. 이 포맷은 굉장히 직관적이고 아무런 수정 없이 정점 버퍼에 넣어 그려낼 수 있습니다.


한 가지 더 살펴보아야 할 것은 어떤 툴이 어떤 좌표계를 쓰는지, 오른손 좌표계인지 왼손 좌표계인지를 알아야 합니다. DirectX 11에서는 기본값으로 왼손 좌표계를 쓰므로 모델 데이터도 왼손 좌표계에 맞게 되어 있어야 합니다. 그 차이를 계속 주시하고 파서 프로그램이 그런 좌표계를 올바르게 다루는지 확인해야 할 것입니다.




Modelclass.h


이 튜토리얼에서는 텍스트 파일로부터 3D 모델을 읽어 그려내기 때문에 ModelClass이 조금 수정되어야 합니다.


////////////////////////////////////////////////////////////////////////////////
// Filename: modelclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _MODELCLASS_H_
#define _MODELCLASS_H_


//////////////
// INCLUDES //
//////////////
#include <d3d11.h>
#include <d3dx10math.h>









텍스트 파일을 읽기 위해 fstream 라이브러리를 인클루드 합니다.


#include <fstream>
using namespace std;


///////////////////////
// MY CLASS INCLUDES //
///////////////////////
#include "textureclass.h"


////////////////////////////////////////////////////////////////////////////////
// Class name: ModelClass
////////////////////////////////////////////////////////////////////////////////
class ModelClass
{
private:
	struct VertexType
	{
		D3DXVECTOR3 position;
		D3DXVECTOR2 texture;
		D3DXVECTOR3 normal;
	};







그 다음으로 바뀐 것은 모델 포맷을 표현하기 위한 새로운 구조체의 추가입니다. 이를 ModelType라고 부르겠습니다. 이 ModelType은 텍스트 포맷에 나온 것처럼 위치, 텍스쳐, 그리고 법선 벡트 정보를 가집니다.


	struct ModelType
	{
		float x, y, z;
		float tu, tv;
		float nx, ny, nz;
	};

public:
	ModelClass();
	ModelClass(const ModelClass&);
	~ModelClass();







Initialize함수는 불러올 모델의 파일명을 입력으로 받습니다.


	bool Initialize(ID3D11Device*, char*, WCHAR*);
	void Shutdown();
	void Render(ID3D11DeviceContext*);

	int GetIndexCount();
	ID3D11ShaderResourceView* GetTexture();


private:
	bool InitializeBuffers(ID3D11Device*);
	void ShutdownBuffers();
	void RenderBuffers(ID3D11DeviceContext*);

	bool LoadTexture(ID3D11Device*, WCHAR*);
	void ReleaseTexture();






또한 모델 데이터를 읽어들이고 해제하는 역할을 하는 새 두 함수가 추가되었습니다.


	bool LoadModel(char*);
	void ReleaseModel();

private:
	ID3D11Buffer *m_vertexBuffer, *m_indexBuffer;
	int m_vertexCount, m_indexCount;
	TextureClass* m_Texture;





마지막으로 m_model이라 하는 ModelType형의 private 멤버 변수가 추가되었습니다. 이 변수는 모델 데이터를 읽은 뒤 정점 버퍼에 놓여지기 전까지 이 데이터를 저장해두는 데 사용될 것입니다.


	ModelType* m_model;
};

#endif










Modelclass.cpp


////////////////////////////////////////////////////////////////////////////////
// Filename: modelclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "modelclass.h"


ModelClass::ModelClass()
{
	m_vertexBuffer = 0;
	m_indexBuffer = 0;
	m_Texture = 0;





새로운 변수 역시 생성자에서 null로 초기화됩니다.


	m_model = 0;
}


ModelClass::ModelClass(const ModelClass& other)
{
}


ModelClass::~ModelClass()
{
}





Initialize 함수는 불러올 모델의 파일명을 입력으로 받습니다.


bool ModelClass::Initialize(ID3D11Device* device, char* modelFilename, WCHAR* textureFilename)
{
	bool result;






Initialize 함수 내부에서는 우선 새로 추가한 LoadModel 함수를 호출합니다. 이 함수에서 파일명을 이용하여 m_model 배열에 그 내용을 불러들일 것입니다. 일단 배열이 채워지면 이를 이용하여 정점 버퍼와 인덱스 버퍼를 생성할 수 있습니다.  이제 InitializeBuffers 함수는 불러오는 모델 데이터에 의존하게 되었기 때문에 각 함수들이 올바른 순서로 호출되는지 확인해야 합니다.


	// Load in the model data,
	result = LoadModel(modelFilename);
	if(!result)
	{
		return false;
	}

	// Initialize the vertex and index buffers.
	result = InitializeBuffers(device);
	if(!result)
	{
		return false;
	}

	// Load the texture for this model.
	result = LoadTexture(device, textureFilename);
	if(!result)
	{
		return false;
	}

	return true;
}


void ModelClass::Shutdown()
{
	// Release the model texture.
	ReleaseTexture();

	// Shutdown the vertex and index buffers.
	ShutdownBuffers();






Shutdown 함수에서는 채워졌던 m_model 배열의 메모리를 해제하는 ReleaseModel 함수를 호출하는 부분이 추가되었습니다.


	// Release the model data.
	ReleaseModel();

	return;
}


void ModelClass::Render(ID3D11DeviceContext* deviceContext)
{
	// Put the vertex and index buffers on the graphics pipeline to prepare them for drawing.
	RenderBuffers(deviceContext);

	return;
}


int ModelClass::GetIndexCount()
{
	return m_indexCount;
}


ID3D11ShaderResourceView* ModelClass::GetTexture()
{
	return m_Texture->GetTexture();
}


bool ModelClass::InitializeBuffers(ID3D11Device* device)
{
	VertexType* vertices;
	unsigned long* indices;
	D3D11_BUFFER_DESC vertexBufferDesc, indexBufferDesc;
	D3D11_SUBRESOURCE_DATA vertexData, indexData;
	HRESULT result;
	int i;






주의해야 할 것은, 앞으로는 더 이상 정점과 인덱스 수를 직접 설정하지 않는다는 것입니다. 그 대신 우선 ModelClass::LoadModel을 호출하고 난 뒤에 정점과 인덱스 수를 가져온다는 것을 볼 수 있습니다.


	// Create the vertex array.
	vertices = new VertexType[m_vertexCount];
	if(!vertices)
	{
		return false;
	}

	// Create the index array.
	indices = new unsigned long[m_indexCount];
	if(!indices)
	{
		return false;
	}







정점과 인덱스 배열을 불러오는 것 역시 조금 바뀌었습니다. 직접 그 값을 넣어주는 대신, for 루프를 돌면서 m_model 배열에 있는 정보들을 정점 배열로 복사합니다. 인덱스 배열은 불러올 때 해당 배열에서의 위치가 곧 인덱스 번호이기 때문에 간단하게 지정할 수 있습니다.


	// Load the vertex array and index array with data.
	for(i=0; i<m_vertexCount; i++)
	{
		vertices[i].position = D3DXVECTOR3(m_model[i].x, m_model[i].y, m_model[i].z);
		vertices[i].texture = D3DXVECTOR2(m_model[i].tu, m_model[i].tv);
		vertices[i].normal = D3DXVECTOR3(m_model[i].nx, m_model[i].ny, m_model[i].nz);

		indices[i] = i;
	}

	// Set up the description of the static vertex buffer.
	vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
	vertexBufferDesc.ByteWidth = sizeof(VertexType) * m_vertexCount;
	vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	vertexBufferDesc.CPUAccessFlags = 0;
	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 create the vertex buffer.
	result = device->CreateBuffer(&vertexBufferDesc, &vertexData, &m_vertexBuffer);
	if(FAILED(result))
	{
		return false;
	}

	// Set up the description of the static 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;
}


void ModelClass::ShutdownBuffers()
{
	// Release the index buffer.
	if(m_indexBuffer)
	{
		m_indexBuffer->Release();
		m_indexBuffer = 0;
	}

	// Release the vertex buffer.
	if(m_vertexBuffer)
	{
		m_vertexBuffer->Release();
		m_vertexBuffer = 0;
	}

	return;
}


void ModelClass::RenderBuffers(ID3D11DeviceContext* deviceContext)
{
	unsigned int stride;
	unsigned int offset;


	// Set vertex buffer stride and offset.
	stride = sizeof(VertexType); 
	offset = 0;
    
	// Set the vertex buffer to active in the input assembler so it can be rendered.
	deviceContext->IASetVertexBuffers(0, 1, &m_vertexBuffer, &stride, &offset);

	// Set the index buffer to active in the input assembler so it can be rendered.
	deviceContext->IASetIndexBuffer(m_indexBuffer, DXGI_FORMAT_R32_UINT, 0);

	// Set the type of primitive that should be rendered from this vertex buffer, in this case triangles.
	deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

	return;
}


bool ModelClass::LoadTexture(ID3D11Device* device, WCHAR* filename)
{
	bool result;


	// Create the texture object.
	m_Texture = new TextureClass;
	if(!m_Texture)
	{
		return false;
	}

	// Initialize the texture object.
	result = m_Texture->Initialize(device, filename);
	if(!result)
	{
		return false;
	}

	return true;
}


void ModelClass::ReleaseTexture()
{
	// Release the texture object.
	if(m_Texture)
	{
		m_Texture->Shutdown();
		delete m_Texture;
		m_Texture = 0;
	}

	return;
}






새 LoadModel 함수는 텍스트 파일의 모델 데이터를 m_model 배열로 로드하는 일을 합니다. 우선 파일을 열고 정점 수를 읽습니다. 그 뒤 ModelType의 배열을 생성하고 각 라인을 읽어 그 값을 배열에 넣습니다. 정점 수와 인덱스 수가 이 함수에서 설정됩니다.


bool ModelClass::LoadModel(char* filename)
{
	ifstream fin;
	char input;
	int i;


	// Open the model file.
	fin.open(filename);
	
	// If it could not open the file then exit.
	if(fin.fail())
	{
		return false;
	}

	// Read up to the value of vertex count.
	fin.get(input);
	while(input != ':')
	{
		fin.get(input);
	}

	// Read in the vertex count.
	fin >> m_vertexCount;

	// Set the number of indices to be the same as the vertex count.
	m_indexCount = m_vertexCount;

	// Create the model using the vertex count that was read in.
	m_model = new ModelType[m_vertexCount];
	if(!m_model)
	{
		return false;
	}

	// Read up to the beginning of the data.
	fin.get(input);
	while(input != ':')
	{
		fin.get(input);
	}
	fin.get(input);
	fin.get(input);

	// Read in the vertex data.
	for(i=0; i<m_vertexCount; i++)
	{
		fin >> m_model[i].x >> m_model[i].y >> m_model[i].z;
		fin >> m_model[i].tu >> m_model[i].tv;
		fin >> m_model[i].nx >> m_model[i].ny >> m_model[i].nz;
	}

	// Close the model file.
	fin.close();

	return true;
}






ReleaseModel 함수는 모델 데이터 배열을 해제하는 일을 합니다.


void ModelClass::ReleaseModel()
{
	if(m_model)
	{
		delete [] m_model;
		m_model = 0;
	}

	return;
}









Graphicsclass.h


GraphicsClass의 헤더 파일은 이전 튜토리얼과 달라진 것이 없습니다.


////////////////////////////////////////////////////////////////////////////////
// Filename: graphicsclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _GRAPHICSCLASS_H_
#define _GRAPHICSCLASS_H_


///////////////////////
// MY CLASS INCLUDES //
///////////////////////
#include "d3dclass.h"
#include "cameraclass.h"
#include "modelclass.h"
#include "lightshaderclass.h"
#include "lightclass.h"


/////////////
// GLOBALS //
/////////////
const bool FULL_SCREEN = true;
const bool VSYNC_ENABLED = true;
const float SCREEN_DEPTH = 1000.0f;
const float SCREEN_NEAR = 0.1f;


////////////////////////////////////////////////////////////////////////////////
// Class name: GraphicsClass
////////////////////////////////////////////////////////////////////////////////
class GraphicsClass
{
public:
	GraphicsClass();
	GraphicsClass(const GraphicsClass&);
	~GraphicsClass();

	bool Initialize(int, int, HWND);
	void Shutdown();
	bool Frame();

private:
	bool Render(float);

private:
	D3DClass* m_D3D;
	CameraClass* m_Camera;
	ModelClass* m_Model;
	LightShaderClass* m_LightShader;
	LightClass* m_Light;
};

#endif




Graphicsclass.cpp


////////////////////////////////////////////////////////////////////////////////
// Filename: graphicsclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "graphicsclass.h"


GraphicsClass::GraphicsClass()
{
	m_D3D = 0;
	m_Camera = 0;
	m_Model = 0;
	m_LightShader = 0;
	m_Light = 0;
}


GraphicsClass::GraphicsClass(const GraphicsClass& other)
{
}


GraphicsClass::~GraphicsClass()
{
}


bool GraphicsClass::Initialize(int screenWidth, int screenHeight, HWND hwnd)
{
	bool result;


	// Create the Direct3D object.
	m_D3D = new D3DClass;
	if(!m_D3D)
	{
		return false;
	}

	// Initialize the Direct3D object.
	result = m_D3D->Initialize(screenWidth, screenHeight, VSYNC_ENABLED, hwnd, FULL_SCREEN, SCREEN_DEPTH, SCREEN_NEAR);
	if(!result)
	{
		MessageBox(hwnd, L"Could not initialize Direct3D.", L"Error", MB_OK);
		return false;
	}

	// Create the camera object.
	m_Camera = new CameraClass;
	if(!m_Camera)
	{
		return false;
	}

	// Set the initial position of the camera.
	m_Camera->SetPosition(0.0f, 0.0f, -10.0f);
	
	// Create the model object.
	m_Model = new ModelClass;
	if(!m_Model)
	{
		return false;
	}






모델의 초기화 함수에서 모델의 파일명을 받기 때문에 육면체를 그리기 위해 cube.txt를 입력으로 넣습니다.


	// Initialize the model object.
	result = m_Model->Initialize(m_D3D->GetDevice(), "../Engine/data/cube.txt", L"../Engine/data/seafloor.dds");
	if(!result)
	{
		MessageBox(hwnd, L"Could not initialize the model object.", L"Error", MB_OK);
		return false;
	}

	// Create the light shader object.
	m_LightShader = new LightShaderClass;
	if(!m_LightShader)
	{
		return false;
	}

	// Initialize the light shader object.
	result = m_LightShader->Initialize(m_D3D->GetDevice(), hwnd);
	if(!result)
	{
		MessageBox(hwnd, L"Could not initialize the light shader object.", L"Error", MB_OK);
		return false;
	}

	// Create the light object.
	m_Light = new LightClass;
	if(!m_Light)
	{
		return false;
	}







그리고 이 튜토리얼의 조명 색상을 흰색으로 바꿉니다.


	// Initialize the light object.
	m_Light->SetDiffuseColor(1.0f, 1.0f, 1.0f, 1.0f);
	m_Light->SetDirection(0.0f, 0.0f, 1.0f);

	return true;
}


void GraphicsClass::Shutdown()
{
	// Release the light object.
	if(m_Light)
	{
		delete m_Light;
		m_Light = 0;
	}

	// Release the light shader object.
	if(m_LightShader)
	{
		m_LightShader->Shutdown();
		delete m_LightShader;
		m_LightShader = 0;
	}

	// Release the model object.
	if(m_Model)
	{
		m_Model->Shutdown();
		delete m_Model;
		m_Model = 0;
	}

	// Release the camera object.
	if(m_Camera)
	{
		delete m_Camera;
		m_Camera = 0;
	}

	// Release the D3D object.
	if(m_D3D)
	{
		m_D3D->Shutdown();
		delete m_D3D;
		m_D3D = 0;
	}

	return;
}


bool GraphicsClass::Frame()
{
	bool result;
	static float rotation = 0.0f;


	// Update the rotation variable each frame.
	rotation += (float)D3DX_PI * 0.01f;
	if(rotation > 360.0f)
	{
		rotation -= 360.0f;
	}
	
	// Render the graphics scene.
	result = Render(rotation);
	if(!result)
	{
		return false;
	}

	return true;
}


bool GraphicsClass::Render(float rotation)
{
	D3DXMATRIX worldMatrix, viewMatrix, projectionMatrix;
	bool result;


	// Clear the buffers to begin the scene.
	m_D3D->BeginScene(0.0f, 0.0f, 0.0f, 1.0f);

	// Generate the view matrix based on the camera's position.
	m_Camera->Render();

	// Get the world, view, and projection matrices from the camera and d3d objects.
	m_Camera->GetViewMatrix(viewMatrix);
	m_D3D->GetWorldMatrix(worldMatrix);
	m_D3D->GetProjectionMatrix(projectionMatrix);

	// Rotate the world matrix by the rotation value so that the triangle will spin.
	D3DXMatrixRotationY(&worldMatrix, rotation);

	// Put the model vertex and index buffers on the graphics pipeline to prepare them for drawing.
	m_Model->Render(m_D3D->GetDeviceContext());

	// Render the model using the light shader.
	result = m_LightShader->Render(m_D3D->GetDeviceContext(), m_Model->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, 
				       m_Model->GetTexture(), m_Light->GetDirection(), m_Light->GetDiffuseColor());
	if(!result)
	{
		return false;
	}

	// Present the rendered scene to the screen.
	m_D3D->EndScene();

	return true;
}





마치면서


ModelClass를 조금 바꾸어서 이제 3D 모델들을 불러와서 그릴 수 있게 되었습니다. 여기서 이용된 포맷은 단지 기본적인 객체에 조명을 더한 것이지만, 모델 포맷이 어떻게 동작하는 지 이해하는 데는 좋은 시작일 것입니다.








연습 문제


1. 소스를 다시 컴파일하고 프로그램을 실행해 보십시오. seafloore.dds 텍스쳐가 먹여진 육면체가 돌고 있는 것이 보일 것입니다. 확인했다면 esc 버튼을 눌러 나갈 수 있습니다.


2. 괜찮은 3D 모델링 패키지를 찾고(웬만하면 무료인 것을 이용하세요) 그것으로 간단한 모델을 만들고 내보내십시오(export). 그 포맷을 한번 보시기 바랍니다.


3. 해당 패키지에서 만든 모델 포맷을 여기서 사용한 모델 포맷으로 바꿔주는 파서(parser) 프로그램을 작성해 보십시오. cube.txt 대신 직접 만든 모델 파일을 가지고 프로그램을 실행해 보십시오.







소스 코드


Visual Studio 2010 프로젝트: dx11tut07.zip


소스 코드: dx11src07.zip


실행 파일: dx11exe07.zip

Nav