Directx11教程四十之加载OBJ模型

本节是有关于如何加载OBJ模型的,程序的结构如下:





一,OBJ模型的介绍。


在具体介绍OBJ模型的内部数据之前,我们来看看我们这次加载的OBJ模型,我们这次使用的OBJ模型为图形学界著名的"CornellBox",经常被一些图形学研究者用于全局光照算法等等。模型如下所示:



来看看我们的OBJ模型文件 “CornellBox-Glossy.obj”在VS2015打开的样子:




这是用VS2015内默认打开OBJ模型的方式。下面我们在VS015中以文本方式打开OBJ模型文件(这样貌似会损坏OBJ模型文件)


这里OBJ模型文件以文本格式显示,我们对文本的开端的内容进行截图,注意画红圈部分代表了此OBJ模型伴随的材质文件为"CornellBox-Glossy.mtl",这里材质文件我们先不介绍,等介绍完OBJ模型文件的本体后再详细说明OBJ文件伴随的材质文件。

"


截取一段文本来说明:

CornellBox-Glossy.obj模型的部分数据

## Object shortBox
v  -0.8677 -0.0001 0.0703
v  -0.4117 0.0001 -0.4921
v  0.1507 0.0001 -0.0362
v  -0.3052 -0.0001 0.5262
v  -0.8678 0.7239 0.0705
v  -0.3054 0.7239 0.5264
v  0.1506 0.7241 -0.0360
v  -0.4118 0.7241 -0.4920
# 8 vertices

vn 0.0002 -1.0000 -0.0002
vn -0.0002 1.0000 0.0002
vn -0.6298 -0.0003 0.7768
vn 0.7768 0.0000 0.6298
vn 0.6298 0.0003 -0.7768
vn -0.7768 0.0000 -0.6298
# 6 vertex normals

vt 1.0000 0.0000 0.0000
vt 1.0000 1.0000 0.0000
vt 0.0000 1.0000 0.0000
vt 0.0000 0.0000 0.0000
# 4 texture coords

g shortBox
usemtl shortBox

s 2
f 547/629/547 548/630/547 549/631/547 
f 549/631/547 550/632/547 547/629/547 
s 4
f 551/632/548 552/629/548 553/630/548 
f 553/630/548 554/631/548 551/632/548 
s 8
f 547/632/549 550/629/549 552/630/549 
f 552/630/549 551/631/549 547/632/549 
s 16
f 550/632/550 549/629/550 553/630/550 
f 553/630/550 552/631/550 550/632/550 
s 32
f 549/632/551 548/629/551 554/630/551 
f 554/630/551 553/631/551 549/632/551 
s 64
f 548/632/552 547/629/552 551/630/552 
f 551/630/552 554/631/552 548/632/552 
# 12 faces


## Object floor
v   1.0000 0.0000 -1.0400
v  -0.9900 0.0000 -1.0400
v  -1.0100 0.0000  0.9900
v   1.0000 0.0000  0.9900
# 4 vertices

vn 0.0000 1.0000 -0.0000
# 1 vertex normals

g floor
usemtl floor

s 1
f 555//553 556//553 557//553 
f 557//553 558//553 555//553 
# 2 faces


## Object ceiling
v   1.0000 1.5900 -1.0400
v   1.0000 1.5900  0.9900
v  -1.0200 1.5900  0.9900
v  -1.0200 1.5900 -1.0400
# 4 vertices

vn 0.0000 -1.0000 -0.0000
# 1 vertex normals

g ceiling
usemtl ceiling

s 1
f 559//554 560//554 561//554 
f 561//554 562//554 559//554 
# 2 faces

(1)这里“## Object shortBox”代表这个物体模型叫做“shortBox”,你可能也注意到下面有个类似的标注“## Object floor”,这也代表一个模型,叫做“floor”,注意整个OBJ模型“CornellBox”是由多个子模型组成的,如“shortBox”和“## Object floor”。

(2)v代表了一个顶点位置坐标,vn代表了一个顶点法向量,vt代表了一个顶点纹理坐标,当然这里vt虽然有三个分量,但是第三个分量没啥用。

(3)f代表了一个面,由三个顶点组成,这里我单独拿出一组f的数据来分析:

f 547/629/547 548/630/547 549/631/547 
这里shortBox的一个面为 547/629/547 548/630/547 549/631/547 ,其中 547/629/547代表了第一个顶点,548/630/547 代表了第二个顶点,549/631/547代表了第三个顶点。

拿547/629/547来说,547代表整个OBJ文件的第547个v代表的顶点位置信息,629代表整个OBJ文件的第629个vt代表的顶点纹理坐标息,547代表整个OBJ文件的第547个vn代表的顶点法向量信息。(注意:上面的信息是我从整个OBJ文件截取部分来的,所以不要误以为整个OBJ文件就只有那么多个顶点而已)这样不难推算出这个面第二顶点和第三个顶点的信息.

(4)细心的你也许会发现子模型“Object ceiling”的面信息组成和“Object shortBox”的面信息有些不一样,来看看“Object ceiling”的面组成

f 559//554 560//554 561//554 

这里一个面也是由三个顶点组成的,但是一个顶点仅仅有两个信息,如555//553,这第一个数据代表的是顶点位置信息,第二个数据代表的是顶点法向量信息。

(5)来看看标注“usemtl shortBox”,代表了Object shortBox使用的材质为"shortBox',什么意思呢?上面我们说过OBJ模型伴随的材质文件为"CornellBox-Glossy.mtl",来看看CornellBox-Glossy.mtl以文本显示的内容:

CornellBox-Glossy.mtl

# The glossy Cornell Box in OBJ format.
# Note that the real box is not a perfect cube, so
# the faces are imperfect in this data set.
# This can be matched with Henrik Jensen's "Global Illumination using Photon Maps" Page 17 Fig. 6
#
# Created by Guedis Cardenas and Morgan McGuire at Williams College, 2011
# Released into the Public Domain.
#
# http://graphics.cs.williams.edu/data
# http://scholar.google.com/scholar?q=global+illumination+using+photon+maps+jensen&hl=en&as_sdt=0&as_vis=1&oi=scholart 
#
newmtl sphere						
	Ns 32
	d 1
	Tr 0
	Tf 1 1 1
	illum 2
	# Cyan
	Ka 0.486 0.631 0.663 
	Kd 0.486 0.631 0.663
	Ks 0.700 0.700 0.700
	Ke 0.000 0.000 0.000

newmtl shortBox
	Ns 10.0000
  Ni 1.0000
  illum 2
  # White
  Ka 0.725 0.71 0.68 
  Kd 0.725 0.71 0.68
  Ks 0 0 0
  Ke 0 0 0

newmtl floor
	Ns 80
	Ni 1.5000
	d 1.0000
	Tr 0.0000
	Tf 1.0000 1.0000 1.0000 
	illum 2
	Ka 0.7250 0.7100 0.6800
	Kd 0.7250 0.7100 0.6800
	Ks 0.3000 0.3000 0.3000
	Ke 0.0000 0.0000 0.0000

newmtl ceiling
	Ns 10.0000
	Ni 1.5000
	d 1.0000
	Tr 0.0000
	Tf 1.0000 1.0000 1.0000 
	illum 2
	Ka 0.7250 0.7100 0.6800
	Kd 0.7250 0.7100 0.6800
	Ks 0.0000 0.0000 0.0000
	Ke 0.0000 0.0000 0.0000

newmtl backWall
  Ns 10.0000
  Ni 1.0000
  illum 2
  # White
  Ka 0.725 0.71 0.68 
  Kd 0.725 0.71 0.68
  Ks 0 0 0
  Ke 0 0 0

newmtl leftWall
  Ns 10.0000
  Ni 1.5000
  illum 2
  # Red
  Ka 0.63 0.065 0.05 
  Kd 0.63 0.065 0.05
  Ks 0 0 0
  Ke 0 0 0


newmtl rightWall
  Ns 10.0000
  Ni 1.5000
  illum 2
  # Green
  Ka 0.3725 0.6480 0.3137 
  Kd 0.3725 0.6480 0.3137 
  Ks 0 0 0
  Ke 0 0 0
  

 newmtl light
	Ns 10.0000
	Ni 1.5000
	d 1.0000
	Tr 0.0000
	Tf 1.0000 1.0000 1.0000 
	illum 2
	Ka 0.7800 0.7800 0.7800
	Kd 0.7800 0.7800 0.7800
    Ke 10 10 10
	Ks 0 0 0
从上面可以看到关键字"newmtl shortBox'代表的就是Object shortBox的使用的材质了,下面介绍下对应的参数。

(1)Ns代表了镜面光指数.
(2)Ka代表了环境光材质属性.

(3)Kd代表了漫反射材质属性.

(4)Ks代表了镜面光材质属性。

本节教程使用的物体的材质属性就是上面四个,其它的我也不懂,更具体的参见大神的博客obj + mtl 格式说明

二,加载OBJ模型的代码实现。

ObjModelClass.h

#pragma once
#ifndef _OBJ_MODEL_CLASS_H
#define _OBJ_MODEL_CLASS_H

#include"CommonVertexFormat.h"
#include<d3d11.h>
#include<xnamath.h>
#include"Macro.h"  //包含辅助的宏
#include<vector>
#include<string>
using std::string;
using std::vector;

class ObjModelClass
{
public:
	struct VertexType
	{
		float x, y, z;
		float u, v;
		float nx, ny, nz;
	};

	struct OBJMaterial
	{
		XMFLOAT3 AmbientMaterial;
		XMFLOAT3 DiffuseMaterial;
		XMFLOAT3 SpecularMaterial;
		float SpecularPower; //镜面幂指数
	};
private:
	ID3D11Buffer* md3dVertexBuffer; //顶点缓存
	ID3D11Buffer* md3dIndexBuffer;  //索引缓存
	int mVertexCount;
	int mIndexCount;
	vector<VertexType*> mVertexData; //在外部加载的模型数据
	string ObjModelName;
	string ObjMaterialName;  //材质名字
	OBJMaterial* mMaterial;
private:
	//加载各种缓存
	bool InitializeBuffer(ID3D11Device* d3dDevice);

	//释放各种缓存
	void ShutdownBuffer();

	//设置(渲染各种缓存)
	void RenderBuffers(ID3D11DeviceContext* d3dDeviceContext);

	//释放外部的模型数据
	void ReleaseModelData();

	//释放材质数据
	void ReleaseMaterialData();

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

public:
	//Initialize是创建元素,Render是设置元素,Shutdown是Release
	bool Initialize(ID3D11Device* d3dDevice);
	void Shutdown();
	void Render(ID3D11DeviceContext* d3dDeviceContext);

	int GetIndexCount() { return mIndexCount; } //返回索引值 DrawIndexed();

	//返回数据模型数组的引用
	vector<VertexType*>& GetVertexArrayRef() { return mVertexData; }

	//Set函数
	void SetModelName(string ModelName) { ObjModelName = ModelName; }
	void SetMaterialName(string MaterialName) { ObjMaterialName = MaterialName; }
	void SetAmbientMaterial(float r, float g, float b) { mMaterial->AmbientMaterial = XMFLOAT3(r, g, b); }
	void SetDiffuseMaterial(float r, float g, float b) { mMaterial->DiffuseMaterial = XMFLOAT3(r, g, b); }
	void SetSpecularMaterial(float r, float g, float b) { mMaterial->SpecularMaterial = XMFLOAT3(r, g, b); }
	void SetSpecularPower(float SpecularPower) { mMaterial->SpecularPower = SpecularPower; }

	//Get函数
	string GetMaterialName() { return ObjMaterialName; }
	XMVECTOR GetAmbientMaterial() { return XMLoadFloat3(&mMaterial->AmbientMaterial); }
	XMVECTOR GetDiffuseMaterial() { return XMLoadFloat3(&mMaterial->DiffuseMaterial); }
	XMVECTOR GetSpecularMaterial() { return XMLoadFloat3(&mMaterial->SpecularMaterial); }
	float GetSpecularPower() { return mMaterial->SpecularPower; }
};
#endif // !_OBJ_MODEL_CLASS


ObjModelClass.CPP

#include"ObjModelClass.h"


ObjModelClass::ObjModelClass()
{
	md3dVertexBuffer = NULL; //顶点缓存
	md3dIndexBuffer = NULL;  //索引缓存
	mVertexCount = 0;
	mIndexCount = 0;
	mMaterial = NULL;
}


ObjModelClass::ObjModelClass(const ObjModelClass& other)
{

}

ObjModelClass::~ObjModelClass()
{

}

bool ObjModelClass::Initialize(ID3D11Device* d3dDevice)
{
	bool result;

	//初始化材质指针
	mMaterial = new OBJMaterial();
	if (!mMaterial)
	{
		MessageBox(NULL, L"Initialize OBJMaterial failure", L"ERROR", MB_OK);
		return false;
	}

	//初始化顶点缓存,索引缓存
	result = InitializeBuffer(d3dDevice);
	if (!result)
	{
		MessageBox(NULL, L"Initialize Buffer failure", L"ERROR", MB_OK);
		return false;
	}

	return true;
}

void ObjModelClass::Shutdown()
{
	ShutdownBuffer();
	ReleaseModelData();
	ReleaseMaterialData();
}


void ObjModelClass::Render(ID3D11DeviceContext* d3dDeviceContext)
{
	//设置渲染管线的顶点缓存和索引缓存(IA阶段)
	RenderBuffers(d3dDeviceContext);
}

bool ObjModelClass::InitializeBuffer(ID3D11Device* d3dDevice)
{
	Vertex* vertexs = NULL;
	unsigned long *indices = NULL;  //一个字为两个字节 

	//初始化顶点数量和索引数量
	mVertexCount = mIndexCount = mVertexData.size();

	//创建顶点数组
	vertexs = new Vertex[mVertexCount];
	if (!vertexs)
		return false;

	//创建索引数组
	indices = new unsigned long[mIndexCount];
	if (!indices)
		return false;

	//赋予顶点数组数据和索引数组数据
	for (size_t i = 0; i < mVertexCount; ++i)
	{
		vertexs[i].pos = XMFLOAT3(mVertexData[i]->x, mVertexData[i]->y, mVertexData[i]->z);
		vertexs[i].Tex = XMFLOAT2(mVertexData[i]->u, mVertexData[i]->v);
		vertexs[i].normal = XMFLOAT3(mVertexData[i]->nx, mVertexData[i]->ny, mVertexData[i]->nz);
	}
	//赋予索引数组数据
	for (int i = 0; i < mIndexCount; ++i)
	{
		indices[i] = i;
	}

	//第一,填充(顶点)缓存形容结构体和子资源数据结构体,并创建顶点缓存
	D3D11_BUFFER_DESC vertexBufferDesc;
	vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
	vertexBufferDesc.ByteWidth = sizeof(Vertex) * mVertexCount;
	vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	vertexBufferDesc.CPUAccessFlags = 0;
	vertexBufferDesc.MiscFlags = 0;
	vertexBufferDesc.StructureByteStride = 0;

	D3D11_SUBRESOURCE_DATA vertexData;
	vertexData.pSysMem = vertexs;
	vertexData.SysMemPitch = 0;
	vertexData.SysMemSlicePitch = 0;
	HR(d3dDevice->CreateBuffer(&vertexBufferDesc, &vertexData, &md3dVertexBuffer));

	//第二,填充(索引)缓存形容结构体和子资源数据结构体,并创建索引缓存
	D3D11_BUFFER_DESC  indexBufferDesc;
	indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
	indexBufferDesc.ByteWidth = sizeof(unsigned long) * mIndexCount;
	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;
	HR(d3dDevice->CreateBuffer(&indexBufferDesc, &indexData, &md3dIndexBuffer));

	//释放顶点数组和索引数组(这时数据已经载入缓存,不需要这些数组了)
	delete[]vertexs;
	vertexs = NULL;
	delete[]indices;
	indices = NULL;

	return true;
}

void ObjModelClass::ShutdownBuffer()
{
	//释放顶点缓存和索引缓存
	ReleaseCOM(md3dIndexBuffer);
	ReleaseCOM(md3dVertexBuffer);
}

void ObjModelClass::RenderBuffers(ID3D11DeviceContext* d3dDeviceContext)
{
	//设置顶点缓存
	UINT stride = sizeof(Vertex); //每个顶点元素的跨度大小,或者说每个顶点元素的大小
	UINT offset = 0;
	d3dDeviceContext->IASetVertexBuffers(0, 1, &md3dVertexBuffer, &stride, &offset);

	//设置索引缓存
	d3dDeviceContext->IASetIndexBuffer(md3dIndexBuffer, DXGI_FORMAT_R32_UINT, 0); //Word为两个字节

																				  //设置拓扑方式
	d3dDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
}


void ObjModelClass::ReleaseModelData()
{
	for (auto it = mVertexData.begin(); it != mVertexData.end(); ++it)
	{
		delete (*it);
		(*it) = NULL;
	}
}


void ObjModelClass::ReleaseMaterialData()
{
	if (mMaterial)
	{
		delete mMaterial;
		mMaterial = NULL;
	}
}




OBJLoadClass.h
#pragma once
#ifndef OBJ_LOAD_CLASS_H
#define OBJ_LOAD_CLASS_H
#include"ObjModelClass.h"
#include<fstream>
#include <sstream>
using std::ifstream;
using std::istringstream;

class OBJLoadClass
{
private:
	struct VertexPosition
	{
		float x, y, z;
		VertexPosition(float a, float b, float c) :x(a), y(b), z(c)
		{

		}
	};
	struct VertexNormal
	{
		float nx, ny, nz;
		VertexNormal (float a, float b, float c) : nx(a), ny(b), nz(c)
		{

		}
	};
	struct VertexTex
	{
	    float u, v;
		VertexTex(float a, float b) : u(a), v(b)
		{
		}
	};
	vector<VertexPosition> mModelPosArray; //在外部加载的模型顶点位置
	vector<VertexNormal> mModelNormalArray; //在外部加载的模型数据
	vector<VertexTex> mModelTexArray; //在外部加载的模型数据纹理坐标
	vector<ObjModelClass*> mOBJModelArray; //OBJ对象数组
	string ObjMaterialFileName;
private:

	bool InitializeOBJModelArray(ID3D11Device* d3dDevice);

	//释放OBJ对象
	void ReleaseOBJModelArray();

	//加载OBJ文件
	bool LoadOBJFile(string OBJFileName);

	//加载OBJ材质文件
	bool LoadOBJMaterialFile(string MaterialFileName);

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

	//Initialize是创建元素,Shutdown是Release
	bool Initialize(ID3D11Device* d3dDevice, string OBJFileName);
	void Shutdown();
     
	//返回对象数组的拷贝
	vector<ObjModelClass*> GetOBJModelArrayCopy() { return mOBJModelArray; }

};
#endif // !OBJ_LOAD_CLASS_H


OBJLoadClass.CPP

#include"OBJLoadClass.h"

OBJLoadClass::OBJLoadClass()
{

}


OBJLoadClass::OBJLoadClass(const OBJLoadClass&)
{

}


OBJLoadClass::~OBJLoadClass()
{

}

void OBJLoadClass::Shutdown()
{
	//释放OBJ对象数组
	ReleaseOBJModelArray();
}


//释放OBJ对象
void OBJLoadClass::ReleaseOBJModelArray()
{
	for (size_t i = 0; i < mOBJModelArray.size(); ++i)
	{
		mOBJModelArray[i]->Shutdown();
		delete mOBJModelArray[i];
		mOBJModelArray[i] = NULL;
	}
}


//Initialize是创建元素,Shutdown是Release
bool OBJLoadClass::Initialize(ID3D11Device* d3dDevice,string OBJFileName)
{
	bool result;

	//第一,加载OBJ文件,形成OBJModel类型的数组中
	result = LoadOBJFile(OBJFileName);
	if (!result)
	{

		MessageBox(NULL, L"LoadOBJFilefailure", NULL, MB_OK);
		return false;
	}


	//第二,初始化OBJModel对象数组的每个对象
	result = InitializeOBJModelArray(d3dDevice);
	if (!result)
	{

		MessageBox(NULL, L"OBJModelArray Initialize", NULL, MB_OK);
		return false;
	}


	//第三,加载OBJ的附属材质文件
	result = LoadOBJMaterialFile(ObjMaterialFileName);
	if (!result)
	{

		MessageBox(NULL, L"LoadOBJMaterialFile Initialize", NULL, MB_OK);
		return false;
	}



	return true;
}

//vector<VertexPosition> mModelPosArray; //在外部加载的模型顶点位置
//vector<VertexNormal> mModelNormalArray; //在外部加载的模型数据
//vector<VertexTex> mModelTexArray; //在外部加载的模型数据纹理坐标

//加载OBJ文件
bool OBJLoadClass::LoadOBJFile(string OBJFileName)
{

	ifstream in(OBJFileName);
	if (!in)
	{
		return false;
	}

	//line代表文件里的一整行,包含空格,字符等。 而word代表文件里的一个单词,无空格存在
	std::string line, word;

	//空行的数量
	UINT NullStrCount = 0;

	//对象的数量
	UINT OBJCount = 0;

	//首先在第一行读取出顶点数(这里顶点数每三个就是一个三角形)
	while (!in.eof())
	{
		getline(in, line);


		//如果line包含mtllib,则存储OBJ的材质文件的相对路径
		if (line.find("mtllib") != string::npos)
		{
			string MaterialFileName;
			istringstream record(line);
			record >> word;
			record >> word;
			MaterialFileName = string("MeshData/") + word;
			ObjMaterialFileName = MaterialFileName;
		}
		else if (line.find("Object") != string::npos)
		{			
            istringstream record(line);
			record >> word;	
			record >> word;
			record >> word;
			ObjModelClass* memObjModel = new ObjModelClass();
			if (!memObjModel)
			{
				return false;
			}
			vector<ObjModelClass::VertexType*>& mVertexArray= memObjModel->GetVertexArrayRef();;
				
			memObjModel->SetModelName(word);
				while (1)
				{
					getline(in, line);

					istringstream re(line);
					re>> word;

					//如果line包含faces,则结束一轮循环
					if (line.find("faces") != string::npos)
					{
						//退出一轮循环前把对象存入OBJ对象数组
						mOBJModelArray.push_back(memObjModel);
						break;
					}

					//如果line包含usemtl,存储每个OBJ对象在材质文件对应的材质名
					if (line.find("usemtl") != string::npos)
					{
						istringstream re1(line);
						re1 >> word;
						re1 >> word;
						memObjModel->SetMaterialName(word);
					}

					//判断是否为v开头
       					if (word == string("v"))
					{
						re >> word;
						float PosX = atof(&word[0]);
						re >> word;
						float PosY = atof(&word[0]);
						re >> word;
						float PosZ = atof(&word[0]);
						mModelPosArray.push_back(VertexPosition(PosX, PosY, PosZ));

					}
					else if (word == string("vn"))
					{
						re >> word;
						float NormalX = atof(&word[0]);
						re >> word;
						float NormalY = atof(&word[0]);
						re >> word;
						float NormalZ = atof(&word[0]);
						mModelNormalArray.push_back(VertexNormal(NormalX, NormalY, NormalZ));
					}
					else if (word == string("vt"))
					{
						re >> word;
						float u = atof(&word[0]);
						re >> word;
						float v = atof(&word[0]);
						mModelTexArray.push_back(VertexTex(u, v));
					}
					else if (word == string("f"))
					{
						//一个面三个顶点
						ObjModelClass::VertexType* vertex1 = new ObjModelClass::VertexType();
						if (!vertex1)
						{
							return false;
						}

						ObjModelClass::VertexType* vertex2 = new ObjModelClass::VertexType();
						if (!vertex2)
						{
							return false;
						}
						ObjModelClass::VertexType* vertex3 = new ObjModelClass::VertexType();
						if (!vertex3)
						{
							return false;
						}

						//是否存在纹理坐标,有些模型不存在纹理坐标
						bool IsTexExist;
						re >> word;


						if (word.find("//") == string::npos)
						{
							IsTexExist = true;
						}
						else
						{
							IsTexExist = false;
						}

						//如果不包含“//”,得读取顶点位置,法向量,纹理坐标
						if (IsTexExist)
						{
							/*******第一个顶点*******/
							/*解剖顶点的位置*/
							//求第一个"/"字符串在word中的位置
							UINT FirstIndex = word.find("/");		
							string NumericStr = word.substr(0, FirstIndex);
							UINT PosIndex = atoi(&NumericStr[0]);
							vertex1->x = mModelPosArray[PosIndex-1].x;
							vertex1->y = mModelPosArray[PosIndex-1].y;
							vertex1->z = mModelPosArray[PosIndex-1].z;

							/*解剖顶点的法向量*/
							//求第二个"/"字符串在word中的位置
							UINT SecondIndex = word.find("/",FirstIndex+1);
							NumericStr = word.substr(FirstIndex+1,SecondIndex-FirstIndex-1);
							PosIndex = atoi(&NumericStr[0]);
							vertex1->u = mModelTexArray[PosIndex - 1].u;
							vertex1->v = mModelTexArray[PosIndex - 1].v;

							/*解剖顶点的纹理坐标*/
							NumericStr = word.substr(SecondIndex+1);
							PosIndex = atoi(&NumericStr[0]);
							vertex1->nx = mModelNormalArray[PosIndex - 1].nx;
							vertex1->ny = mModelNormalArray[PosIndex - 1].ny;
							vertex1->nz = mModelNormalArray[PosIndex - 1].nz;
						

							/*******第二个顶点*******/
							re >> word;
							FirstIndex = word.find("/");
							NumericStr = word.substr(0, FirstIndex);
						    PosIndex = atoi(&NumericStr[0]);
							vertex2->x = mModelPosArray[PosIndex-1].x;
							vertex2->y = mModelPosArray[PosIndex-1].y;
							vertex2->z = mModelPosArray[PosIndex-1].z;

							/*解剖顶点的法向量*/
							//求第二个"/"字符串在word中的位置
							SecondIndex = word.find("/", FirstIndex + 1);
							NumericStr = word.substr(FirstIndex + 1, SecondIndex - FirstIndex-1);
							PosIndex = atoi(&NumericStr[0]);
							vertex2->u = mModelTexArray[PosIndex - 1].u;
							vertex2->v = mModelTexArray[PosIndex - 1].v;

							/*解剖顶点的纹理坐标*/
							NumericStr = word.substr(SecondIndex + 1);
							PosIndex = atoi(&NumericStr[0]);
							vertex2->nx = mModelNormalArray[PosIndex - 1].nx;
							vertex2->ny = mModelNormalArray[PosIndex - 1].ny;
							vertex2->nz = mModelNormalArray[PosIndex - 1].nz;


							/*******第三个顶点*******/
							re >> word;
							FirstIndex = word.find("/");
							NumericStr = word.substr(0, FirstIndex);
							PosIndex = atoi(&NumericStr[0]);
							vertex3->x = mModelPosArray[PosIndex-1].x;
							vertex3->y = mModelPosArray[PosIndex-1].y;
							vertex3->z = mModelPosArray[PosIndex-1].z;

							/*解剖顶点的法向量*/
							//求第二个"/"字符串在word中的位置
							SecondIndex = word.find("/", FirstIndex + 1);
							NumericStr = word.substr(FirstIndex + 1, SecondIndex - FirstIndex-1);
							PosIndex = atoi(&NumericStr[0]);
							vertex3->u = mModelTexArray[PosIndex - 1].u;
							vertex3->v = mModelTexArray[PosIndex - 1].v;
			

							/*解剖顶点的纹理坐标*/
							NumericStr = word.substr(SecondIndex + 1);
							PosIndex = atoi(&NumericStr[0]);
							vertex3->nx = mModelNormalArray[PosIndex - 1].nx;
							vertex3->ny = mModelNormalArray[PosIndex - 1].ny;
							vertex3->nz = mModelNormalArray[PosIndex - 1].nz;

						}

						//如果包含"//"
						else
						{

							/*******第一个顶点*******/
							/*解剖顶点的位置*/
							//求第一个"/"字符串在word中的位置
							UINT FirstIndex = word.find("//");
							string NumericStr = word.substr(0, FirstIndex);
							UINT PosIndex = atoi(&NumericStr[0]);
							vertex1->x = mModelPosArray[PosIndex-1].x;
							vertex1->y = mModelPosArray[PosIndex-1].y;
							vertex1->z = mModelPosArray[PosIndex-1].z;

							/*解剖顶点的法向量*/
							//求第二个"/"字符串在word中的位置
							NumericStr = word.substr(FirstIndex + 2);
							PosIndex = atoi(&NumericStr[0]);
							vertex1->nx = mModelNormalArray[PosIndex-1].nx;
							vertex1->ny = mModelNormalArray[PosIndex-1].ny;
							vertex1->nz = mModelNormalArray[PosIndex-1].nz;

							//随便给UV坐标赋值
							vertex1->u = 0.0f;
							vertex1->v = 0.0f;

							/*******第二个顶点*******/
							/*解剖顶点的位置*/
							re >> word;
							FirstIndex = word.find("//");
							NumericStr = word.substr(0, FirstIndex);
							PosIndex = atoi(&NumericStr[0]);
							vertex2->x = mModelPosArray[PosIndex-1].x;
							vertex2->y = mModelPosArray[PosIndex-1].y;
							vertex2->z = mModelPosArray[PosIndex-1].z;
							

							/*解剖顶点的法向量*/
							//求第二个"/"字符串在word中的位置
							NumericStr = word.substr(FirstIndex + 2);
							PosIndex = atoi(&NumericStr[0]);
							vertex2->nx = mModelNormalArray[PosIndex-1].nx;
							vertex2->ny = mModelNormalArray[PosIndex-1].ny;
							vertex2->nz = mModelNormalArray[PosIndex-1].nz;
							//随便给UV坐标赋值
							vertex2->u = 0.0f;
							vertex2->v = 0.0f;
							 


							/*******第三个顶点*******/
							/*解剖顶点的位置*/
							re >> word;
							FirstIndex = word.find("//");
							NumericStr = word.substr(0, FirstIndex);
							PosIndex = atoi(&NumericStr[0]);
							vertex3->x = mModelPosArray[PosIndex-1].x;
							vertex3->y = mModelPosArray[PosIndex-1].y;
							vertex3->z = mModelPosArray[PosIndex-1].z;

							/*解剖顶点的法向量*/
							//求第二个"/"字符串在word中的位置
							NumericStr = word.substr(FirstIndex + 2);
							PosIndex = atoi(&NumericStr[0]);
							vertex3->nx = mModelNormalArray[PosIndex-1].nx;
							vertex3->ny = mModelNormalArray[PosIndex-1].ny;
							vertex3->nz = mModelNormalArray[PosIndex-1].nz;
							//随便给UV坐标赋值
							vertex3->u = 0.0f;
							vertex3->v = 0.0f;

						}

						mVertexArray.push_back(vertex1);
						mVertexArray.push_back(vertex2);
						mVertexArray.push_back(vertex3);
					}
				}
		}

	}


	return true;
}


bool OBJLoadClass::InitializeOBJModelArray(ID3D11Device* d3dDevice)
{
	bool result;
	for (size_t i = 0; i < mOBJModelArray.size(); ++i)
	{
		result = mOBJModelArray[i]->Initialize(d3dDevice);
		if (!result)
		{
			return false;
		}
	}
	return true;
}

//加载OBJ材质文件
bool OBJLoadClass::LoadOBJMaterialFile(string MaterialFileName)
{
	ifstream in(MaterialFileName);
	if (!in)
	{
		return false;
	}


	//line代表文件里的一整行,包含空格,字符等。 而word代表文件里的一个单词,无空格存在
	std::string line, word;
	UINT index;

	//首先在第一行读取出顶点数(这里顶点数每三个就是一个三角形)
	while (!in.eof())
	{
		getline(in, line);

		//如果读取的这一行包含"newmtl",则开始读取相应的材质
		if (line.find("newmtl")!=string::npos)
		{
			istringstream record(line);
			record >> word;
			record >> word;

			//遍历整个OBJ对象数组,寻找是哪个材质
			for (size_t i = 0; i < mOBJModelArray.size(); ++i)
			{
				if (word == mOBJModelArray[i]->GetMaterialName())
				{
					index = i;
					break;
				}
			}

			//进入新一轮的循环,读取相应材质的属性 
			while (1)
			{
			
				getline(in, line);

				if (line.find("Ns")!=string::npos)
				{
					istringstream re(line);
					re >> word;
					re >> word;
					float SpecularPower= atof(&word[0]);
					mOBJModelArray[index]->SetSpecularPower(SpecularPower);
				}

				else if (line.find("Ka") != string::npos)
				{
					istringstream re(line);
					re >> word;
					re >> word;
					float r = atof(&word[0]);
					re >> word;
					float g = atof(&word[0]);
					re >> word;
					float b = atof(&word[0]);
					mOBJModelArray[index]->SetAmbientMaterial(r, g, b);
				}
				else if (line.find("Kd") != string::npos)
				{
					istringstream re(line);
					re >> word;
					re >> word;
					float r = atof(&word[0]);
					re >> word;
					float g = atof(&word[0]);
					re >> word;
					float b = atof(&word[0]);
					mOBJModelArray[index]->SetDiffuseMaterial(r, g, b);

				}
				else if (line.find("Ks") != string::npos)
				{
					istringstream re(line);
					re >> word;
					re >> word;
					float r = atof(&word[0]);
					re >> word;
					float g = atof(&word[0]);
					re >> word;
					float b = atof(&word[0]);
					mOBJModelArray[index]->SetSpecularMaterial(r, g, b);
				}
				else if (line.find("Ke") != string::npos)
				{
					//跳出内循环
					break;
				}

			}	
		}

	}


	return true;
}


渲染OBJ模型的代码:

bool GraphicsClass::RenderOBJModel()
{
	// 三个变换矩阵
	XMMATRIX WorldMatrix, ViewMatrix, ProjMatrix;
	bool result;
	vector<ObjModelClass*> mOBJModelArray;

	//获取加载的OBJ对象数组
	mOBJModelArray = mOBJLoadClass->GetOBJModelArrayCopy();

	//第一,(更新)获取ViewMatrix(根据CameraClass的mPostion和mRotation来生成的)
	mFirstCameraClass->UpdateViewMatrix();


	//第二,获取三个变换矩阵(WorldMatrix和ProjMatrixViewMatrix来自CameraClass)
	WorldMatrix = mD3D->GetWorldMatrix();

	ViewMatrix = mFirstCameraClass->GetViewMatrix();

	ProjMatrix = mD3D->GetProjMatrix();

	//缩放物体
	WorldMatrix = WorldMatrix*XMMatrixScaling(20.0f, 20.0f, 20.0f);

	//获取相机的位置
	XMVECTOR CameraPos = mFirstCameraClass->GetPositionXM();

	//第三,把每个OBJ对象的顶点数据和索引数据放入3D渲染流水线,并进行渲染
	
	for (size_t i = 0; i < mOBJModelArray.size(); ++i)
	{
		mOBJModelArray[i]->Render(mD3D->GetDeviceContext());
	
		//用ColorShaderClass进行绘制
		result = mShaderManageClass->RenderOBJShaderClass(mD3D->GetDeviceContext(), mOBJModelArray[i]->GetIndexCount(), WorldMatrix, ViewMatrix, ProjMatrix, mLightClass->GetAmbientColor(),mLightClass->GetDiffuseColor(), mLightClass->GetLightDirection(), mOBJModelArray[i]->GetAmbientMaterial(), mOBJModelArray[i]->GetDiffuseMaterial(), mOBJModelArray[i]->GetSpecularMaterial(), CameraPos, mOBJModelArray[i]->GetSpecularPower());
		if (!result)
		{
			MessageBox(NULL, L"RenderOBJShaderClass Render failure", NULL, MB_OK);
			return false;
		}
	}


	return true;
}



渲染OBJ模型的Shader代码:OBJShader.fx

SamplerState SampleType:register(s0);   //采样方式

//VertexShader
cbuffer CBMatrix:register(b0)
{
	matrix World;
	matrix View;
	matrix Proj;
	matrix WorldInvTranspose;
};

//这里DiffuseLight=SpecularLight,即大小颜色和方向都一样
cbuffer CBLight:register(b1)
{
	float4 AmbientLight;
	float4 DiffuseLight;
	float3 LightDirection;
	float pad1;
}


cbuffer CBMaterial:register(b2)
{
	float3 AmbientMaterial;
	float pad2;
	float3 DiffuseMaterial;
	float pad3;
	float3 SpecularMaterial;
	float SpecularPower; //镜面指数
};

cbuffer CBCamera:register(b3)
{
	float3 CameraPos;
	float pad4;
};

struct VertexIn
{
	float3 Pos:POSITION;
	float2 Tex:TEXCOORD0;  //多重纹理可以用其它数字
	float3 Normal:NORMAL;
};


struct VertexOut
{
	float4 Pos:SV_POSITION;
	float2 Tex:TEXCOORD0;
	float3 W_Normal:NORMAL;  //世界空间的法线
	float3 W_Pos:POSITION; //顶点在世界空间的位置
};


VertexOut VS(VertexIn ina)
{
	VertexOut outa;
	outa.Pos = mul(float4(ina.Pos,1.0f), World);

	//顶点位于世界空间的位置
	outa.W_Pos = outa.Pos.xyz;

	outa.Pos = mul(outa.Pos, View);
	outa.Pos = mul(outa.Pos, Proj);

	//将法线向量变换到世界空间
	outa.W_Normal = mul(ina.Normal, (float3x3)WorldInvTranspose);  //此事世界逆转置矩阵的第四行本来就没啥用
	outa.W_Normal = normalize(outa.W_Normal);
	outa.Tex= ina.Tex;

	return outa;
}


float4 PS(VertexOut outa) : SV_Target
{
	float DiffuseFactor;
    float SpecularFactor;
    float4 Ambient;
	float4 Diffuse;
	float4 Specular;
	float4 color;

	//初始化Ambient,Diffuse,Specular
	Ambient = float4(0.0f, 0.0f, 0.0f, 1.0f);
	Diffuse = float4(0.0f, 0.0f, 0.0f, 1.0f);
	Specular = float4(0.0f, 0.0f, 0.0f, 1.0f);

	/*第一,求出环境光颜色*/
	Ambient = AmbientLight*float4(AmbientMaterial, 1.0f);


	/*第二,求出漫反射颜色*/
	//规格化灯光方向
	float3 NormalLightDir = normalize(LightDirection);

	//求出漫反射因子
	float3 InvLightDir = -NormalLightDir;
	DiffuseFactor = saturate(dot(InvLightDir,outa.W_Normal));
	 
	//求出漫反射颜色
	 if (DiffuseFactor>0)
	 {
		 Diffuse = DiffuseFactor*DiffuseLight*float4(DiffuseMaterial, 1.0f);  
	 }

	 /*第三,求出漫镜面光颜色*/
	 float3 ReflectLightDir = reflect(LightDirection, outa.W_Normal);

	 //规格化反射光方向
	 ReflectLightDir= normalize(ReflectLightDir);

	 //求出反射点到相机之间的向量
	 float3 PixelToCameraPosVector = CameraPos - outa.W_Pos;

	 //规格化PixelToCameraPosVector
	 PixelToCameraPosVector= normalize(PixelToCameraPosVector);

	 //求出镜面因子
	 SpecularFactor = pow(saturate(dot(PixelToCameraPosVector, ReflectLightDir)), SpecularPower);

	 //求出镜面光颜色
	 if (SpecularFactor > 0)
	 {
		 Specular = SpecularFactor*DiffuseLight*float4(SpecularMaterial, 1.0f);
	 }
	 
	 //三种光加一起
	 color = saturate(Ambient+Diffuse+Specular);

	return color;
}


程序运行截图:




源代码链接:

http://download.csdn.net/detail/qq_29523119/9767773


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值