复杂的3D模型可以借助建模工具生成,这种工具能够在3D空间中构建任意形状并自动生成顶点、纹理坐标、顶点法向量等。模型生成后可导出成obj文件格式,这种格式有很多,OBJ文件很简单,我们可以相对容易地开发一个基本的导入器。在OBJ文件中,通过文本行的形式指定顶点几何数据、纹理坐标、法向量和其他信息。它有一些限制——例如,OBJ文件无法指定模型动画。
OBJ文件中的行,以字符标记开头,表示该行上的数据类型。一些常见的标签包括:
●v-几何(顶点位置)数据;
●vt-纹理坐标;
●vn-顶点法向量;
●f-面(通常是三角形中的顶点,每个元素的值分别是顶点列表、纹理坐标和法向量的索引。)。
obj格式文件导入器代码:
注意这个导入器非常有局限性,仅支持由单个三角形网格组成的OBJ模型(OBJ格式支持复合模型,但我们的简单导入器不支持),仅支持包含所有3个面属性字段的模型,它假设每行上的元素只用一个空格分隔。
ImportedModel.h,ImportedModel.cpp
1 #pragma once 2 #include <vector> 3 #include <glm\glm.hpp> 4 5 class ModelImporter 6 { 7 public: 8 ModelImporter(); 9 ~ModelImporter(); 10 void parseOBJ(const char *filePath); 11 int getNumVertices(); 12 std::vector<float> getVertices(); 13 std::vector<float> getTextureCoordinates(); 14 std::vector<float> getNormals(); 15 private: 16 // 从OBJ文件读取的数值 17 std::vector<float> vertVals; 18 std::vector<float> stVals; 19 std::vector<float> normVals; 20 // 保存为顶点属性以供后续使用的数值 21 std::vector<float> triangleVerts; 22 std::vector<float> textureCoords; 23 std::vector<float> normals; 24 }; 25 26 class ImportedModel 27 { 28 public: 29 ImportedModel(const char *filePath); 30 ~ImportedModel(); 31 int getNumVertices(); 32 std::vector<glm::vec3> getVertices(); 33 std::vector<glm::vec2> getTextureCoords(); 34 std::vector<glm::vec3> getNormals(); 35 36 private: 37 int numVertices; 38 std::vector<glm::vec3> vertices; 39 std::vector<glm::vec2> texCoords; 40 std::vector<glm::vec3> normalVecs; 41 ModelImporter a; 42 }; 43 44 #include "ImportedModel.h" 45 #include <fstream> 46 #include <sstream> 47 using namespace std; 48 49 ModelImporter::ModelImporter() {} 50 51 ModelImporter::~ModelImporter(){} 52 53 void ModelImporter::parseOBJ(const char *filePath) 54 { 55 float x, y, z; 56 string content; 57 ifstream fileStream(filePath, ios::in); 58 string line = ""; 59 while (!fileStream.eof()) 60 { 61 getline(fileStream, line); 62 if (line.compare(0, 2, "v ") == 0) // 顶点位置("v"的情况) 63 { 64 stringstream ss(line.erase(0, 1)); 65 ss >> x; // 提取顶点位置数值 66 ss >> y; 67 ss >> z; 68 vertVals.push_back(x); 69 vertVals.push_back(y); 70 vertVals.push_back(z); 71 } 72 73 if (line.compare(0, 2, "vt") == 0) // 纹理坐标("vt"的情况) 74 { 75 stringstream ss(line.erase(0, 2)); 76 ss >> x; // 提取纹理坐标数值 77 ss >> y; 78 stVals.push_back(x); 79 stVals.push_back(y); 80 } 81 82 if (line.compare(0, 2, "vn") == 0) // 顶点法向量("vn"的情况) 83 { 84 stringstream ss(line.erase(0, 2)); 85 ss >> x; // 提取法向量数值 86 ss >> y; 87 ss >> z; 88 normVals.push_back(x); 89 normVals.push_back(y); 90 normVals.push_back(z); 91 } 92 93 if (line.compare(0, 2, "f ") == 0) // 三角形面("f"的情况) 94 { 95 string oneCorner, v, t, n; 96 stringstream ss(line.erase(0, 2)); 97 for (int i = 0; i < 3; i++) 98 { 99 getline(ss, oneCorner, ' '); // 提取三角形面引用 100 stringstream oneCornerSS(oneCorner); 101 getline(oneCornerSS, v, '/'); 102 getline(oneCornerSS, t, '/'); 103 getline(oneCornerSS, n, '/'); 104 int vertRef = (stoi(v) - 1) * 3; // "stoi"将字符串转化为整型 105 int tcRef = (stoi(t) - 1) * 2; 106 int normRef = (stoi(n) - 1) * 3; 107 triangleVerts.push_back(vertVals[vertRef]); // 构建顶点向量 108 triangleVerts.push_back(vertVals[vertRef + 1]); 109 triangleVerts.push_back(vertVals[vertRef + 2]); 110 textureCoords.push_back(stVals[tcRef]); // 构建纹理坐标向量 111 textureCoords.push_back(stVals[tcRef + 1]); 112 normals.push_back(normVals[normRef]); // 法向量的向量 113 normals.push_back(normVals[normRef + 1]); 114 normals.push_back(normVals[normRef + 2]); 115 } 116 } 117 } 118 } 119 120 int ModelImporter::getNumVertices() 121 { 122 return (triangleVerts.size() / 3); 123 } 124 125 vector<float> ModelImporter::getVertices() 126 { 127 return triangleVerts; 128 } 129 130 vector<float> ModelImporter::getTextureCoordinates() 131 { 132 return textureCoords; 133 } 134 135 vector<float> ModelImporter::getNormals() 136 { 137 return normals; 138 } 139 140 ImportedModel::ImportedModel(const char *filePath) 141 { 142 ModelImporter modelImporter = ModelImporter(); 143 modelImporter.parseOBJ(filePath); // 使用modelImporter获取顶点信息 144 numVertices = modelImporter.getNumVertices(); 145 vector<float> verts = modelImporter.getVertices(); 146 vector<float> tcs = modelImporter.getTextureCoordinates(); 147 vector<float> normals = modelImporter.getNormals(); 148 149 for (int i = 0; i < numVertices; i++) 150 { 151 vertices.push_back(glm::vec3(verts[i * 3], verts[i * 3 + 1], verts[i * 3 + 2])); 152 texCoords.push_back(glm::vec2(tcs[i * 2], tcs[i * 2 + 1])); 153 normalVecs.push_back(glm::vec3(normals[i * 3], normals[i * 3 + 1], normals[i * 3 + 2])); 154 } 155 } 156 157 ImportedModel::~ImportedModel() 158 { 159 } 160 161 int ImportedModel::getNumVertices() 162 { 163 return numVertices; 164 } 165 166 vector<glm::vec3> ImportedModel::getVertices() 167 { 168 return vertices; 169 } 170 171 vector<glm::vec2> ImportedModel::getTextureCoords() 172 { 173 return texCoords; 174 } 175 176 vector<glm::vec3> ImportedModel::getNormals() 177 { 178 return normalVecs; 179 }
我用Blender生成一个立方体模型的OBJ文件,生成的OBJ文件中 "f" 面是四个点构成正方形,不是三角形,我们简单的导入器只读取了四个点中前三个点,形成的每个面都是三角形不是正方形。
为此我加了一个面,是第一个“f” 的另一个三角形,可以看到有一个面是完整的正方形。其他面不想加了,都同理。
1 # Blender v3.2.1 OBJ File: '' 2 # www.blender.org 3 mtllib untitled.mtl 4 o Cube 5 v 2.982061 2.981781 -3.035211 6 v 2.910542 0.983304 -3.066416 7 v 3.085501 2.946897 -1.038193 8 v 3.013981 0.948420 -1.069397 9 v 0.986019 3.051580 -2.930603 10 v 0.914499 1.053103 -2.961807 11 v 1.089458 3.016696 -0.933584 12 v 1.017939 1.018219 -0.964789 13 vt 0.625000 0.500000 14 vt 0.875000 0.500000 15 vt 0.875000 0.750000 16 vt 0.625000 0.750000 17 vt 0.375000 0.750000 18 vt 0.625000 1.000000 19 vt 0.375000 1.000000 20 vt 0.375000 0.000000 21 vt 0.625000 0.000000 22 vt 0.625000 0.250000 23 vt 0.375000 0.250000 24 vt 0.125000 0.500000 25 vt 0.375000 0.500000 26 vt 0.125000 0.750000 27 vn 0.0358 0.9992 0.0156 28 vn 0.0517 -0.0174 0.9985 29 vn -0.9980 0.0349 0.0523 30 vn -0.0358 -0.9992 -0.0156 31 vn 0.9980 -0.0349 -0.0523 32 vn -0.0517 0.0174 -0.9985 33 usemtl Material 34 s off 35 f 1/1/1 5/2/1 7/3/1 3/4/1 36 f 4/5/2 3/4/2 7/6/2 8/7/2 37 f 8/8/3 7/9/3 5/10/3 6/11/3 38 f 6/12/4 2/13/4 4/5/4 8/14/4 39 f 2/13/5 1/1/5 3/4/5 4/5/5 40 f 6/11/6 5/10/6 1/1/6 2/13/6 41 f 1/1/1 7/3/1 3/4/1
main.cpp
1 ... 2 #include "ImportedModel.h" 3 ... 4 ImportedModel myModel("untitled.obj"); 5 ... 6 void setupVertices(void) 7 { 8 //std::vector<int> ind = myTorus.getIndices(); //索引 9 std::vector<glm::vec3> vert = myModel.getVertices(); //顶点 10 std::vector<glm::vec2> tex = myModel.getTextureCoords(); //纹理 11 std::vector<glm::vec3> norm = myModel.getNormals(); //法向量 12 int numObjVertices = myModel.getNumVertices(); 13 14 std::vector<float> pvalues; //顶点位置 15 vector<float> tvalues; //纹理坐标 16 vector<float> nvalues; //法向量 17 18 for (int i = 0; i < numObjVertices; i++) 19 { 20 pvalues.push_back((vert[i]).x); 21 pvalues.push_back((vert[i]).y); 22 pvalues.push_back((vert[i]).z); 23 tvalues.push_back((tex[i]).s); 24 tvalues.push_back((tex[i]).t); 25 nvalues.push_back((norm[i]).x); 26 nvalues.push_back((norm[i]).y); 27 nvalues.push_back((norm[i]).z); 28 } 29 30 glGenVertexArrays(1, vao); // 创建一个vao,并返回它的整数型ID存进数组vao中 31 glBindVertexArray(vao[0]); // 激活vao 32 glGenBuffers(numVBOs, vbo);// 创建3个vbo,并返回它们的整数型ID存进数组vbo中 33 34 // 把顶点放入缓冲区 #0 35 glBindBuffer(GL_ARRAY_BUFFER, vbo[0]); 36 glBufferData(GL_ARRAY_BUFFER, pvalues.size()*4, &pvalues[0], GL_STATIC_DRAW); 37 // 把纹理坐标放入缓冲区 #1 38 glBindBuffer(GL_ARRAY_BUFFER, vbo[1]); 39 glBufferData(GL_ARRAY_BUFFER, tvalues.size() * 4, &tvalues[0], GL_STATIC_DRAW); 40 // 把法向量放入缓冲区 #2 41 glBindBuffer(GL_ARRAY_BUFFER, vbo[2]); 42 glBufferData(GL_ARRAY_BUFFER, nvalues.size() * 4, &nvalues[0], GL_STATIC_DRAW); 43 // 把索引放入缓冲区 #3 44 //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[3]); 45 //glBufferData(GL_ELEMENT_ARRAY_BUFFER, ind.size() * 4, &ind[0], GL_STATIC_DRAW); 46 } 47 ... 48 void display(GLFWwindow* window, double currentTime) 49 { 50 ... 51 glDrawArrays(GL_TRIANGLES, 0, myModel.getNumVertices()); 52 }
效果如下:
标签:11,std,obj,ImportedModel,图形学,back,vector,vt,push From: https://www.cnblogs.com/lely/p/16638633.html