本节是有关于如何加载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
(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 格式说明
#pragma once
#ifndef _OBJ_MODEL_CLASS_H
#define _OBJ_MODEL_CLASS_H
#include"CommonVertexFormat.h"
#include
#include
#include"Macro.h" //包含辅助的宏
#include
#include
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 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& 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
#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;
}
}
#pragma once
#ifndef OBJ_LOAD_CLASS_H
#define OBJ_LOAD_CLASS_H
#include"ObjModelClass.h"
#include
#include
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 mModelPosArray; //在外部加载的模型顶点位置
vector mModelNormalArray; //在外部加载的模型数据
vector mModelTexArray; //在外部加载的模型数据纹理坐标
vector 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 GetOBJModelArrayCopy() { return mOBJModelArray; }
};
#endif // !OBJ_LOAD_CLASS_H
#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 mModelPosArray; //在外部加载的模型顶点位置
//vector mModelNormalArray; //在外部加载的模型数据
//vector 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& 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 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;
}
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