使用 C++ 解析 Wavefront .obj 文件

发布于 2024-12-10 18:37:53 字数 319 浏览 0 评论 0原文

在尝试解析 wavefront .obj 文件时,我想到了两种方法:

  1. 创建一个大小为顶点数的 2D 数组。当面使用顶点时,从数组中获取它的坐标。
  2. 获取顶点列表的起始位置,然后当面使用顶点时,扫描线直到到达顶点。

IMO,选项 1 会占用大量内存,但速度要快得多。 由于选项 2 涉及大量文件读取(并且因为大多数对象中的顶点数量变得非常大),因此速度会慢得多,但内存占用较少。

问题是:比较内存和速度之间的权衡,哪个选项更适合普通计算机? 还有,有替代方法吗?

我计划使用 OpenGL 和 GLFW 来渲染该对象。

While trying to a parse a wavefront .obj file, I thought of two approaches:

  1. Create an 2D array the size of the number of vertices. When a face uses a vertex, get it's coordinates from the array.
  2. Get the starting position of the vertex list and then when a face uses a vertex, scan the lines until you reach the vertex.

IMO, option 1 will be very memory intensive, but much faster.
Since option 2 involves extensive file reading, (and because the number of vertices in most objects becomes very large) this will be much slower, but less memmory intensive.

The question is: Comparing the tradeoff between memory and speed, which option would be better suited to an average computer?
And, is there an alternative method?

I plan to use OpenGL along with GLFW to render the object.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

月依秋水 2024-12-17 18:37:53

IMO,选项 1 会占用大量内存,但速度要快得多。

无论如何,你必须将这些顶点存入内存。但是不需要二维数组,顺便说一句,二维数组会导致两个指针间接寻址,从而对性能造成重大影响。只需为您的数据使用一个简单的 std::vector 即可,矢量索引是随附面列表的索引。

因评论而编辑

class Vertex
{
    union { struct { float x, y, z }; float pos[3] };
    union { struct { float nx, ny, nz }; float normal[3] };
    union { struct { float s, t }; float pos[2] };
    Vertex &operator=();
}

std::vector<Vertex>;

IMO, Option 1 will be very memory intensive, but much faster.

You must get those vertices into memory anyway. But there's no need for a 2D array, which BTW would cause two pointer indirections, thus a major performance hit. Just use a simple std::vector<Vertex> for your data, the vector index is the index for the accompanying face list.

EDIT due to comment

class Vertex
{
    union { struct { float x, y, z }; float pos[3] };
    union { struct { float nx, ny, nz }; float normal[3] };
    union { struct { float s, t }; float pos[2] };
    Vertex &operator=();
}

std::vector<Vertex>;
鱼忆七猫命九 2024-12-17 18:37:53

通常,您将顶点列表读入数组中。解析 ASCII 文本非常慢;加载文件时只执行一次,然后将所有内容存储在内存中的数组中。

三角形/面也是如此。每个三角形通常由三个顶点索引的列表组成。这也应该存储在数组中。

您可能会发现 VTK 开源库中的 OBJ 阅读器很有用: http:// /www.vtk.org/doc/nightly/html/classvtkOBJReader.html。我们使用它并且没有理由编写我们自己的...直接使用VTK,或者您可能会发现研究源代码对于您自己的读者的进一步启发很有好处。

在我看来,OBJ 文件的主要缺点之一是使用 ASCII。 3D ASCII 文件(无论是 STL、PLY、OBJ 等)如果是 ASCII,则由于字符串解析,加载速度会非常慢。二进制格式文件速度要快得多,如果性能是一个问题,则应始终使用二进制格式文件:良好的二进制格式的加载时间是瞬时的。

Generally you read the list of vertices into an array. Parsing ASCII text is extremely slow; do it only once when loading the file and then store everything in arrays in memory.

Same goes with the triangles / faces. Each triangle generally is composed of a list of three vertex indexes. That should also be stored in an array.

You may find the OBJ reader in the VTK open source library to be useful: http://www.vtk.org/doc/nightly/html/classvtkOBJReader.html. We use it and have had no reason to write our own... Use VTK directly, or you may find studying the source code to be good for further inspiration of your own reader.

In my opinion, one of the major shortcomings with OBJ files is the use of ASCII. 3D ASCII files (be it STL, PLY, OBJ, etc.) are very slow to load if they are ASCII due to the string parsing. Binary format files are much faster and should always be used if performance is an issue: the load time for a good binary format is instantaneous.

别靠近我心 2024-12-17 18:37:53

只需将它们加载到数组中即可。内存应该不是问题。您的系统(通常)拥有比 GPU 更多的内存。如果您遇到内存问题,则可能是您加载的模型过于详细。 (我半假设您将在 OpenGL 中制作游戏。如果您对如此大的模型文件有特定需求,您仍然需要找到一种方法来加载适当的块。)

Just load them into arrays. Memory should not be an issue. Your system (usually) has way more memory than your GPU. If you are running into memory problems, you are probably loading a model that is too detailed. (I am semi-assuming that you are going to make a game in OpenGL. If you have a specific need for such large model files, you will still have to work out a way to load the appropriate chunks.)

思慕 2024-12-17 18:37:53

您不应该需要二维数组。您的模型应该被三角化,然后您可以使用 gluts obj loader 简单地加载 obj 文件。只需将点、面和法线存储在 3 个独立的数组/缓冲区中。 此处有一个如何执行此操作的示例,但如果您想快速执行此操作你应该选择二进制格式。

You shouldn't need a 2 dimensional array. Your models should be triangulated and then you can simply load the obj file using gluts obj loader. Simply store points, faces and normals in 3 seperate arrays/buffers. There is an example how you can do it here, but if you want to do it fast you should go for a binary format.

无所谓啦 2024-12-17 18:37:53

这是一个相当不错的原型解决方案,运行一个脚本来生成用于 OpenGL 或您首选的渲染 API 的数组。 obj2opengl.pl 是一个 Perl 脚本,您需要安装 Perl,您可以在此处。 GitHub 链接位于此处

运行 Perl 脚本时,您可能会在第 154 行收到有关 if(define(@center)) 的运行时错误。将其替换为 if(@center)

从示例中,一旦使用数据生成了头文件,您就可以使用它,如下所示:

/*
created with obj2opengl.pl

source file    : ./banana.obj
vertices       : 4032
faces          : 8056
normals        : 4032
texture coords : 4420


// include generated arrays
#import "./banana.h"

// set input data to arrays
glVertexPointer(3, GL_FLOAT, 0, bananaVerts);
glNormalPointer(GL_FLOAT, 0, bananaNormals);
glTexCoordPointer(2, GL_FLOAT, 0, bananaTexCoords);

// draw data
glDrawArrays(GL_TRIANGLES, 0, bananaNumVerts);
*/

This is a pretty decent solution for prototyping, running a script that generates the arrays for use in OpenGL or your preferred rendering API. obj2opengl.pl is a perl script, you'll need perl installed that you can find here. GitHub link is here.

While running the perl script you may get a runtime error on line 154 concerning if(defined(@center)). Replace it with if(@center).

From the example, once the header file is generated with the data, you can use the it as shown:

/*
created with obj2opengl.pl

source file    : ./banana.obj
vertices       : 4032
faces          : 8056
normals        : 4032
texture coords : 4420


// include generated arrays
#import "./banana.h"

// set input data to arrays
glVertexPointer(3, GL_FLOAT, 0, bananaVerts);
glNormalPointer(GL_FLOAT, 0, bananaNormals);
glTexCoordPointer(2, GL_FLOAT, 0, bananaTexCoords);

// draw data
glDrawArrays(GL_TRIANGLES, 0, bananaNumVerts);
*/
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文