我在绘制一些3d对象时遇到了一些麻烦。我写了一个非常简单的.obj解析器,我知道它并不支持所有类型的.obj文件,但这对我来说并不合适。我只需要在屏幕上正确渲染一些东西。这是我的解析器
Mesh * Loader::loadObj(char * path) {
Mesh * objectMesh;
vector<Vertex *> indexedVertices;
vector<Vector3 * > normals;
vector<TextureCoordinate *> textureCoords;
vector<vector<GLushort>> meshIndices;
vector<GLushort> verticesIndexed;
vector<GLushort> texturesIndexed;
vector<GLushort> normalsIndexed;
ifstream inFile;
inFile.open(path);
if (!inFile.good())
{
cerr << "Can't open texture file " << path << endl;
return false;
}
std::cout << "Loading .obj file" << endl;
while (!inFile.eof()) {
string line;
std::getline(inFile, line);
if (line.substr(0, 2) == "v ") {//vertices
std::vector<string> elements = Operations::split(Operations::trim(line.substr(3)), *" ");
if (elements.size() > 1) {
std::cout << "Loading vertex data: " << line << endl;
Vertex * v = new Vertex(std::atof(elements.at(0).c_str()), std::atof(elements.at(1).c_str()), std::atof(elements.at(2).c_str()));
indexedVertices.push_back(v);
}
}
else if (line.substr(0, 2) == "vn") {//normals
std::vector<string> elements = Operations::split(Operations::trim(line.substr(3)), *" ");
std::cout << "Loading normal data: " << line << endl;
if (elements.size() > 1) {
Vector3 * v = new Vector3(std::atof(elements.at(0).c_str()), std::atof(elements.at(1).c_str()), std::atof(elements.at(2).c_str()));
normals.push_back(v);
}
}
else if (line.substr(0, 2) == "vt") {//tex coords
std::cout << "Loading texture data: " << line << endl;
std::vector<string> elements = Operations::split(Operations::trim(line.substr(3)), *" ");
if (elements.size() > 1) {
TextureCoordinate * t = new TextureCoordinate(std::atof(elements.at(0).c_str()), std::atof(elements.at(1).c_str()));
textureCoords.push_back(t);
}
}
else if (line.substr(0, 2) == "f ") {//faces / indices
std::cout << "Loading face data: " << line << endl;
std::vector<string> elements = Operations::split(Operations::trim(line.substr(2)), *" ");
if (elements.size() > 1) {
for (int i = 0; i < elements.size(); i++) {
std::vector<string> e = Operations::split(Operations::trim(elements.at(i)), *"/");
if (e.size() > 1) {
verticesIndexed.push_back(std::atof(e.at(0).c_str()) - 1);
texturesIndexed.push_back(std::atof(e.at(1).c_str()) - 1);
normalsIndexed.push_back(std::atof(e.at(2).c_str()) - 1);
}
}
}
}
}
meshIndices.push_back(verticesIndexed);
meshIndices.push_back(texturesIndexed);
meshIndices.push_back(normalsIndexed);
std::cout << "Face count: " << verticesIndexed.size() << " " << texturesIndexed.size() << " " << normalsIndexed.size() << endl;
objectMesh = new Mesh(indexedVertices, normals, textureCoords, meshIndices);
std::cout << "Vertices count: " << indexedVertices.size() << endl;
std::cout << "Normals count: " << normals.size() << endl;
std::cout << "Textures count: " << textureCoords.size() << endl;
inFile.close();
return objectMesh;
}
我相信这会正确解析数据,因为矢量的结束大小与文件中的数据匹配,例如它会打印4700个顶点,文件中会有4700个顶点。
我使用以下
绘制对象glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
for (int i = 0; i < mesh->meshIndices.size(); i++) {
glVertexPointer(3, GL_FLOAT, 0, mesh->indexedVertices[0]);
glNormalPointer(GL_FLOAT, 0, mesh->normals[0]);
glTexCoordPointer(2, GL_FLOAT, 0, mesh->textureCoords[0]);
glDrawElements(GL_TRIANGLES, mesh->meshIndices[i].size(), GL_UNSIGNED_SHORT, &mesh->meshIndices[i][0]);
}
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
为什么对象没有正确绘制?
编辑:这有效,但速度非常慢
glBegin(GL_TRIANGLES);
for (int i = 0; i < mesh->meshIndices[0].size(); i++)//36
{
glTexCoord2fv(&mesh->textureCoords[mesh->meshIndices[1][i]]->u);
glNormal3fv(&mesh->normals[mesh->meshIndices[2][i]]->x);
glVertex3fv(&mesh->indexedVertices[mesh->meshIndices[0][i]]->x);
}
答案 0 :(得分:0)
问题在于您使用索引的方式。代码的相关关键部分是在网格中存储顶点,纹理坐标和法线索引的位置:
meshIndices.push_back(verticesIndexed);
meshIndices.push_back(texturesIndexed);
meshIndices.push_back(normalsIndexed);
然后使用这些索引绘制:
for (int i = 0; i < mesh->meshIndices.size(); i++) {
glVertexPointer(3, GL_FLOAT, 0, mesh->indexedVertices[0]);
glNormalPointer(GL_FLOAT, 0, mesh->normals[0]);
glTexCoordPointer(2, GL_FLOAT, 0, mesh->textureCoords[0]);
glDrawElements(GL_TRIANGLES, mesh->meshIndices[i].size(),
GL_UNSIGNED_SHORT, &mesh->meshIndices[i][0]);
}
这样做是将网格绘制3次。一旦使用顶点索引,一次使用纹理坐标索引,一次使用正常索引。这可能不会很好。您需要绘制一次,使用顶点,纹理坐标和位置的正确组合。
难点在于OBJ格式对每个顶点属性(位置,纹理坐标,法线)使用单独的索引,而OpenGL仅支持对所有属性使用单组索引。
有两种主要方法可以实现这一目标:
不要使用索引。这是通过在解析时应用索引查找以及构建新数组来完成的,然后可以使用它们进行渲染而无需编制索引。使用以下方法勾画出代码的关键部分:
if (line.substr(0, 2) == "v ") {
...
indexedVertices.push_back(v);
} else if (line.substr(0, 2) == "vn") {
...
indexedNormals.push_back(v);
} else if (line.substr(0, 2) == "vt") {
...
indexedTextureCoords.push_back(t);
} else if (line.substr(0, 2) == "f ") {
...
unindexedVertices.push_back(
indexedVertices[std::atof(e.at(0).c_str()) - 1]);
unindexedTextureCoords.push_back(
indexedTextureCoords[std::atof(e.at(1).c_str()) - 1]);
unindexedNormals.push_back(
indexedNormals[std::atof(e.at(2).c_str()) - 1]);
}
然后,您可以使用未编入索引的属性,并使用glDrawArrays()
绘制没有索引的属性。
缺点是这会复制共享顶点,因此会消耗更多内存,并且速度会变慢。
为顶点,纹理坐标和法线的每个唯一索引组合创建一个OpenGL顶点。我在这里的旧答案包含一些伪代码,说明如何做到这一点:OpenGL - Index buffers difficulties。