Directx11教程四十之加载OBJ模型

来源:互联网 发布:南京银行 总行 知乎 编辑:程序博客网 时间:2024/05/17 04:29

本节是有关于如何加载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 shortBoxv  -0.8677 -0.0001 0.0703v  -0.4117 0.0001 -0.4921v  0.1507 0.0001 -0.0362v  -0.3052 -0.0001 0.5262v  -0.8678 0.7239 0.0705v  -0.3054 0.7239 0.5264v  0.1506 0.7241 -0.0360v  -0.4118 0.7241 -0.4920# 8 verticesvn 0.0002 -1.0000 -0.0002vn -0.0002 1.0000 0.0002vn -0.6298 -0.0003 0.7768vn 0.7768 0.0000 0.6298vn 0.6298 0.0003 -0.7768vn -0.7768 0.0000 -0.6298# 6 vertex normalsvt 1.0000 0.0000 0.0000vt 1.0000 1.0000 0.0000vt 0.0000 1.0000 0.0000vt 0.0000 0.0000 0.0000# 4 texture coordsg shortBoxusemtl shortBoxs 2f 547/629/547 548/630/547 549/631/547 f 549/631/547 550/632/547 547/629/547 s 4f 551/632/548 552/629/548 553/630/548 f 553/630/548 554/631/548 551/632/548 s 8f 547/632/549 550/629/549 552/630/549 f 552/630/549 551/631/549 547/632/549 s 16f 550/632/550 549/629/550 553/630/550 f 553/630/550 552/631/550 550/632/550 s 32f 549/632/551 548/629/551 554/630/551 f 554/630/551 553/631/551 549/632/551 s 64f 548/632/552 547/629/552 551/630/552 f 551/630/552 554/631/552 548/632/552 # 12 faces## Object floorv   1.0000 0.0000 -1.0400v  -0.9900 0.0000 -1.0400v  -1.0100 0.0000  0.9900v   1.0000 0.0000  0.9900# 4 verticesvn 0.0000 1.0000 -0.0000# 1 vertex normalsg floorusemtl floors 1f 555//553 556//553 557//553 f 557//553 558//553 555//553 # 2 faces## Object ceilingv   1.0000 1.5900 -1.0400v   1.0000 1.5900  0.9900v  -1.0200 1.5900  0.9900v  -1.0200 1.5900 -1.0400# 4 verticesvn 0.0000 -1.0000 -0.0000# 1 vertex normalsg ceilingusemtl ceilings 1f 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 sphereNs 32d 1Tr 0Tf 1 1 1illum 2# CyanKa 0.486 0.631 0.663 Kd 0.486 0.631 0.663Ks 0.700 0.700 0.700Ke 0.000 0.000 0.000newmtl shortBoxNs 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 0newmtl floorNs 80Ni 1.5000d 1.0000Tr 0.0000Tf 1.0000 1.0000 1.0000 illum 2Ka 0.7250 0.7100 0.6800Kd 0.7250 0.7100 0.6800Ks 0.3000 0.3000 0.3000Ke 0.0000 0.0000 0.0000newmtl ceilingNs 10.0000Ni 1.5000d 1.0000Tr 0.0000Tf 1.0000 1.0000 1.0000 illum 2Ka 0.7250 0.7100 0.6800Kd 0.7250 0.7100 0.6800Ks 0.0000 0.0000 0.0000Ke 0.0000 0.0000 0.0000newmtl 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 0newmtl 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 0newmtl 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 lightNs 10.0000Ni 1.5000d 1.0000Tr 0.0000Tf 1.0000 1.0000 1.0000 illum 2Ka 0.7800 0.7800 0.7800Kd 0.7800 0.7800 0.7800    Ke 10 10 10Ks 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是Releasebool 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是Releasebool 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是Releasebool 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);   //采样方式//VertexShadercbuffer 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,SpecularAmbient = 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


0 0
原创粉丝点击