我正在为我的工作编写一个简单但有用的 OpenGL 程序,其中包括显示 vector 场的外观。所以程序只是从文件中获取数据并绘制箭头。我需要画几千支箭。我正在使用 Qt for windows 和 OpenGL API。
箭头单元是圆柱体和圆锥体,在函数 Arrow() 中组合在一起。
for(long i = 0; i < modifiedArrows.size(); i++) {
glColor4d(modifiedArrows[i].color.redF(),modifiedArrows[i].color.greenF(),
modifiedArrows[i].color.blueF(),modifiedArrows[i].opacity);
openGLobj->Arrow(modifiedArrows[i].fromX,modifiedArrows[i].fromY,
modifiedArrows[i].fromZ,modifiedArrows[i].toX,
modifiedArrows[i].toY,modifiedArrows[i].toZ,
simulationSettings->vectorsThickness);
}
现在的问题是运行一个无限循环来继续绘制它会使 CPU 完全忙碌,这不是很好。我尽可能地尝试从 paintGL() 函数中删除所有计算,只保留简单的计算。我用 glFlush() 和 glFinish() 结束了 paintGL() 函数,但我总是让主 CPU 满载。
如果我删除这个循环,CPU 就不会再太忙了。但无论如何我都要画上千支箭。
除了并行化之外,还有其他解决方案吗?
最佳答案
您没有指出您是如何实现 openGLobj->Arrow 方法的,但是如果您在这上面使用了 100% 的 CPU 时间,您可能正在用 immediate mode 绘制箭头。 .这确实是 CPU 密集型的,因为对于 glBegin() 和 glEnd() 中的每条指令,您都必须将数据从 CPU 传输到 GPU。如果您使用 GLUT 来绘制数据,它也确实很低效。
这里的方法是使用 GPU 内存和处理能力来展示您的数据。 Phyatt 已经为您指出了一些方向,但我会尝试更具体一些:使用 Vertex Buffer Object (VBO) .
想法是预先分配所需的内存以在 GPU 上显示数据,并在需要时更新这 block 内存。这可能会对代码的效率产生巨大影响,因为您将使用高效的视频卡驱动程序来处理 CPU->GPU 传输。
为了说明这个概念,我将在答案的最后向您展示一些伪代码,但这绝不是完全正确的。我没有测试它,也没有时间为您实现绘图,但这是一个可以理清思路的概念。
class Form
{
public:
Form()
{
// generate a new VBO and get the associated ID
glGenBuffers(1, &vboId);
// bind VBO in order to use
glBindBuffer(GL_ARRAY_BUFFER, vboId);
//Populate the buffer vertices.
generateVertices();
// upload data to VBO
glBufferData(GL_ARRAY_BUFFER_ARB, vertices.size(), vertices.data(), GL_STATIC_DRAW_ARB);
}
~Form()
{
// it is safe to delete after copying data to VBO
delete [] vertices;
// delete VBO when program terminated
glDeleteBuffersARB(1, &vboId);
}
//Implementing as virtual, because if you reimplement it on the child class, it will call the child method :)
//Generally you will not need to reimplement this class
virtual void draw()
{
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, 0);
//I am drawing the form as triangles, maybe you want to do it in your own way. Do it as you need! :)
//Look! I am not using glBegin() and glEnd(), I am letting the video card driver handle the CPU->GPU
//transfer in a single instruction!
glDrawElements(GL_TRIANGLES, vertices.size(), GL_UNSIGNED_BYTE, 0);
glDisableClientState(GL_VERTEX_ARRAY);
// bind with 0, so, switch back to normal pointer operation
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
}
private:
//Populate the vertices vector with the form vertices.
//Remember, any geometric form in OpenGL is rendered as primitives (points, quads, triangles, etc).
//The common way of rendering this is to use multiple triangles.
//You can draw it using glBegin() and glEnd() just to debug. After that, instead of rendering the triangles, just put
//the generated vertices inside the vertices buffer.
//Consider that it's at origin. You can use push's and pop's to apply transformations to the form.
//Each form(cone or cilinder) will have its own way of drawing.
virtual void generateVertices() = 0;
GLuint vboId;
std::vector<GLfloat> vertices;
}
class Cone : public Form
{
public:
Cone() : Form() {}
~Cone() : ~Form() {}
private:
void generateVertices()
{
//Populate the vertices with cone's formula. Good exercise :)
//Reference: http://mathworld.wolfram.com/Cone.html
}
GLuint vboId;
std::vector<GLfloat> vertices;
}
class Cilinder : public Form
{
public:
Cone() : Form() {}
~Cone() : ~Form() {}
private:
void generateVertices()
{
//Populate the vertices with cilinders's formula. Good exercise :)
//Reference: http://math.about.com/od/formulas/ss/surfaceareavol_3.htm
}
GLuint vboId;
std::vector<GLfloat> vertices;
}
class Visualizer : public QOpenGLWidget
{
public:
//Reimplement the draw function to draw each arrow for each data using the classes below.
void updateGL()
{
for(uint i = 0; i<data.size(); i++)
{
//I really don't have a clue on how you position your arrows around your world model.
//Keep in mind that those functions glPush, glPop and glMatrix are deprecated. I recommend you reading
//http://duriansoftware.com/joe/An-intro-to-modern-OpenGL.-Chapter-3:-3D-transformation-and-projection.html if you want to implement this in the most efficient way.
glPush();
glMatrix(data[i].transform());
cilinder.draw();
cone.draw();
glPop();
}
}
private:
Cone cone;
Cilinder cilinder;
std::vector<Data> data;
}
最后一点,我不能向您保证这是最有效的做事方式。可能,如果你有大量的数据,你可能需要一些像 Octrees 这样的数据结构。或 scene-graphs优化您的代码。
我建议你看看OpenSceneGraph或 Visualization ToolKit查看是否尚未为您实现这些方法,哪些方法可以为您节省大量时间。
关于c++ - 使用 OpenGL 绘图,无需杀死 CPU 且无需并行化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15007150/