3

私は、ベクトル フィールドがどのように見えるかを示すことからなる、私の仕事のためのシンプルだが便利な OpenGL プログラムを書いています。したがって、プログラムはファイルからデータを取得して矢印を描画するだけです。数千本の矢を描く必要があります。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 はビジー状態になりません。しかし、とにかく何千もの矢を描かなければなりません。

並列化以外にこれに対する解決策はありますか?

4

2 に答える 2

5

openGLobj->Arrow メソッドをどのように実装したかについては指摘していませんが、これに 100% の CPU 時間を使用している場合は、おそらく即時モードで矢印をペイントしています。これは、glBegin() と glEnd() 内のすべての単一の命令で CPU から GPU にデータを転送する必要があるため、実際に CPU に負荷がかかります。GLUT を使用してデータを描画している場合、それも非常に非効率的です。

ここに行く方法は、GPU メモリと処理能力を使用してデータを表示することです。Phyatt は既にいくつかの指示を示していますが、より具体的に説明しようとします。Vertex Buffer Object (VBO)を使用します。

アイデアは、GPU でデータを表示するために必要なメモリを事前に割り当て、必要なときにこのメモリのチャンクを更新することです。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;
} 

最後に、これが最も効率的な方法であるとは断言できません。おそらく、大量のデータがある場合、コードを最適化するために、オクトリーシーングラフなどのデータ構造が必要になるでしょう。

OpenSceneGraphまたはVisualization ToolKitを調べて、そのメソッドがまだ実装されていないかどうかを確認することをお勧めします。これにより、多くの時間を節約できます。

于 2013-02-21T18:43:53.543 に答える
1

いくつかのアイデアについては、このリンクを試してください。

基本的に、FPS を上げて品質を落とすために人々が行っていることには、次のようなものがあります。

  • DisplayList の使用。(複雑なまたは反復的な行列スタックをキャッシュします)。

  • 頂点配列の使用。

  • 面の少ない単純なジオメトリを使用します。

  • よりシンプルな照明を使用します。

  • より単純なテクスチャを使用します。

OpenGL の主な利点は、多くの 4x4 マトリックス変換、乗算などを非常に迅速に実行するように構築された多くのグラフィックス カードで動作し、レンダリングまたは部分的にレンダリングされたオブジェクトを保存するためにより多くの RAM メモリを提供することです。

すべてのベクトルが非常に頻繁に変化し、レンダリングをキャッシュできないと仮定すると...

この問題に対する私のアプローチは、描画を線と点だけに単純化し、それを目的のフレーム レートで描画することです。(円柱の線と、方向の端にある色付きの点。)

描画が十分に速くなったら、線の代わりに四角柱、色付きの点の代わりにピラミッドなど、描画をより複雑にしてみてください。

丸みを帯びたオブジェクトは、通常、より多くのサーフェスと計算を必要とします。

私はこのテーマの専門家ではありませんが、最適化を扱う他の OpenGL チュートリアルをグーグルで検索します。

それが役立つことを願っています。

編集: コメントのため、NeHe チュートリアルへの参照を削除しました。

于 2013-02-21T17:17:56.927 に答える