728x90

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


Tutorial 7: 3D Model Rendering


이번 강좌는 HLSL을 이용하여 DirectX 11에서 어떻게 3D 모델을 렌더링하는지를 포함합니다.


이전 강좌에서 이미 3D 모델을 렌더링 해 보았지만 달랑 삼각형 하나라 별로 흥미롭지 못했습니다. 그래서 이번에는 더 복잡한 오브젝트를 렌더링하기 위해 한 발짝 더 움직여 볼 것입니다. 이번 오브젝트는 바로 큐브(정육면체)입니다. 복잡한 모델을 어떻게 렌더링할지 논의하기 전에 먼저 모델 포맷에 대해 알아볼 것입니다.


시중에 3D 모델을 만들수 있는 많은 툴들이 있습니다. 마야나 3D Stuio Max가 대표적인 3D 모델링 프로그램입니다. 또 기능은 저 두개에 비해 적지만 우리가 필요한 만큼의 기능을 갖춘 다른 툴들도 있습니다.


어느 툴을 사용하는지에 상관없이 대부분 만든 3D 모델을 많은 다른 포맷들로 변환하여 저장할 수 있습니다. 제가 제안하는 것은 독자적 모델 포맷을 만들고 다른 대표 포맷에서 독자적 포맷으로 변환시키는 "파서(parser)"를 작성하는 것입니다. 그 이유는 여러분이 사용하는 3D 모델링 패키지는 시간이 지나면 바뀌고 그 모델 포맷도 맞추어 바뀝니다. 그리고 여러분은 아마도 3D 모델링 패키지를 여러개 사용중이실 것이고 따라서 여러 다른 포맷들을 가지실 겁니다. 그래서 만약 여러분이 독자 포맷을 가지며 다른 포맷을 독자 포맷으로 변환하여 쓴다면 여러분의 코드는 바뀌지 않을 것입니다. 파서 프로그램만 수정해주면 됩니다. 뿐만 아니라 대부분의 3D 모델링 패키지들은 해당 프로그램에만 유용하고 여러분의 포맷에 필요치 않은 자질구레한 부분도 포맷에 들어가기 때문이기도 합니다.


독자 포맷을 만드는 것에 대한 중요한 점은 필요한 모든 것을 포함시킬 수 있고 사용하기 편리합니다. 또 애니메이션 데이터를 가지는 혹은 정적인 것을 가지는 등과 같은 다른 특성의 오브젝트들에 대해 다른 포맷을 만드는 것을 생각해 볼수도 있습니다.


제가 만들 모델 포맷은 매우 기초적입니다. 모델의 각 정점을 가지는 라인을 포함할 것입니다. 각 라인은 법선 벡터, 텍셀 좌표, 위치 벡터 등을 가진 코드에서 버텍스 포맷(쉐이더에서 VertexType)과 일치해야 합니다. 이 포맷은 맨 위에 정점 개수를 가지고 있어 첫 라인을 읽고 Data부분을 읽기전에 필요한 메모리를 확보할 수 있습니다. 또 이 포맷은 모든 3줄마다 삼각형(맨 위에서부터 3줄씩)을 이루어야 하며, 모델 포맷의 정점들은 시계방향으로 표현되야 합니다. 여기 우리가 렌더링할 큐브의 모델 파일이 있습니다.




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


보다시피 36줄의 x, y, z, tu, tv, nx, ny, nz(위치 벡터, 텍셀 좌표, 법선 벡터) 데이터가 있습니다. 매 3줄이 삼각형을 구성하여 총 12개의 삼각형이 큐브를 형성합니다. 포맷은 매우 직관적이고 바로 버텍스 버퍼로 읽어들일 수 있으며 어떠한 수정없이 렌더링할 수 있습니다.


그런데 하나 조심할 점은 몇몇 3D 모델링 프로그램은 데이터를 다른방식으로 내보내는데 예컨데 왼손좌표계나 오른손좌표계가 있습니다. 기본적으로 DirectX 11은 왼손좌표계(보통 수학에서의 좌표와 Z축이 반대)이며 모델 데이터도 이와 맞추어야 합니다. 이 차이를 주의하여 여러분의 파싱 프로그램이 데이터를 올바른 포맷/방식으로 변환되는지 확실히 하시기 바랍니다.



Modelclass.h


이번 강좌를 위해 우리가 할 것은 ModelClass가 우리의 텍스트 모델 파일로부터 3D 모델을 렌더링하도록 조금 수정하는 것입니다.


////////////////////////////////////////////////////////////////////////////////
// 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이라 부르는 이 구조체는 모델 파일 포맷과 같이 위치, 텍스쳐, 법선 벡터를 포함합니다.


	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;


마지막으로 ModelType 구조체의 배열이 될 m_model이라 하는 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() 함수만 있으면 만사 OK.


	// 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;
	}


버텍스, 인덱스 배열 로딩 부분은 조금 바뀌었습니다. 직접 값을 설정하는 대신 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;
}


이것이 바로 텍스트 파일에서 m_model 배열 변수로 모델 데이터를 로딩하는 LoadModel() 함수입니다. 먼저 텍스트 파일을 열고 정점 개수를 읽습니다. 정점 개수를 읽은 후 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;
	}


모델 initialization() 함수는 로딩하는 모델 파일의 이름을 받습니다. 이번 강좌에서 우리는 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. 코드를 재컴파일 하고 프로그램을 실행해 보세요. 여러분은 같은 텍스쳐에 회전하는 큐브르르 확인하실 수 있습니다. Esc키로 종료하세요.


2. 괜찮은 3D 모델링 패키지(왠만하면 공짜이길..)를 찾고 간단한 모델을 만든 뒤 파일로 내보내세요. 포맷을 한번 살펴보세요.


3. 다른 포맷의 모델을 이번 강좌에서 사용된 포맷으로 변환하는 간단한 파서 프로그램을 작성해 보세요. 그리고 여러분의 모델로 cube.txt를 대체하고 실행 해 보세요.

728x90

+ Recent posts