2

私は OpenGL を初めて使用し、Red Book と Super Bible を使用しています。SB では、ファイルからロードされたオブジェクトの使用に関するセクションに進みました。これまでのところ、何が起こっているのか、どのように行うのかを理解するのに問題はないと思いますが、自分のアプリ (本質的にはモデリング アプリ) 内で独自のメッシュを作成することを考えるようになりました。私は参考文献とインターネットの両方で多くの検索を行いましたが、そのような機能を独自のアプリに実装するための優れたチュートリアルをまだ見つけていません。この機能を提供するだけのAPIを見つけましたが、実装を理解しようとしています。インターフェースだけではありません。

これまでのところ、クリックして頂点を追加できるビューを提供する「アプリ」(私はこの用語を軽く使用しています) を作成しました。頂点は接続されず、クリックした場所に表示されるだけです。私の懸念は、実験中に偶然見つけたこの方法が、このプロセスを実装する方法ではないということです。

私は Mac で作業しており、Xcode で Objective-C と C を使用しています。

MyOpenGLView.m #import "MyOpenGLView.h"

@interface MyOpenGLView () {

NSTimer *_renderTimer
Gluint VAO, VBO;

GLuint totalVertices;
GLsizei bufferSize;

}

@end

@implementation MyOpenGLView

/* Set up OpenGL view with a context and pixelFormat with doubleBuffering */

/* NSTimer implementation */

- (void)drawS3DView {

    currentTime = CACurrentMediaTime();

    NSOpenGLContext *currentContext = self.openGLContext;
    [currentContext makeCurrentContext];
    CGLLockContext([currentContext CGLContextObj]);

    const GLfloat color[] = {
        sinf(currentTime * 0.2),
        sinf(currentTime * 0.3),
        cosf(currentTime * 0.4),
        1.0
    };

    glClearBufferfv(GL_COLOR, 0, color);

    glUseProgram(shaderProgram);

    glBindVertexArray(VAO);

    glPointSize(10);
    glDrawArrays(GL_POINTS, 0, totalVertices);

    CGLFlushDrawable([currentContext CGLContextObj]);
    CGLUnlockContext([currentContext CGLContextObj]);

}

#pragma mark - User Interaction

- (void)mouseUp:(NSEvent *)theEvent {

    NSPoint mouseLocation = [theEvent locationInWindow];
    NSPoint mouseLocationInView = [self convertPoint:mouseLocation fromView:self];

    GLfloat x = -1 + mouseLocationInView.x * 2/(GLfloat)self.bounds.size.width;
    GLfloat y = -1 + mouseLocationInView.y * 2/(GLfloat)self.bounds.size.height;

    NSOpenGLContext *currentContext = self.openGLContext;
    [currentContext makeCurrentContext];
    CGLLockContext([currentContext CGLContextObj]);

    [_renderer addVertexWithLocationX:x locationY:y];

    CGLUnlockContext([currentContext CGLContextObj]);

}

- (void)addVertexWithLocationX:(GLfloat)x locationY:(GLfloat)y {

    glBindBuffer(GL_ARRAY_BUFFER, VBO);

    GLfloat vertices[(totalVertices * 2) + 2];

    glGetBufferSubData(GL_ARRAY_BUFFER, 0, (totalVertices * 2), vertices);

    for (int i = 0; i < ((totalVertices * 2) + 2); i++) {
        if (i == (totalVertices * 2)) {
            vertices[i] = x;
        } else if (i == (totalVertices * 2) + 1) {
            vertices[i] = y;
        }
    }

    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    totalVertices ++;

}

@end

アプリは、マウス クリックの位置を取得し、それが頂点の位置であることを前提としています。頂点を追加するたびに、まず VBO をバインドしてアクティブであることを確認します。次に、現在の頂点の位置 (totalVertices) ともう 1 つの頂点のスペース (x と y の + 2) を保持する新しい配列を作成します。次に、glGetBufferSubData を使用して VBO からデータを戻し、この配列に入れました。for ループを使用して、X と Y の数値を配列の末尾に追加します。最後に、このデータを GPU の VBO に送り返し、totalVertices++ を呼び出して、次に頂点を追加するときに配列内にいくつの頂点があるかを確認します。

これは私の質問につながります:私はこれを正しくやっていますか?別の言い方をすれば、CPU 側で BufferData のコピーを保持して、GPU を呼び出して編集のためにデータを送り返す必要がないようにする必要がありますか? その方法では、glGetBufferSubData を呼び出さず、より大きな配列を作成し、新しい頂点を最後に追加してから、glBufferData を呼び出して、更新された頂点データで VBO を再割り当てします。

** プログラミングの経験が非常に浅い私のような人が、私のやろうとしていることをうまく理解できるように、私の思考プロセスを含めようとしました。私がしたことについての私の説明によって、だれも気分を害したくありません。**

4

1 に答える 1

3

私は確かにデータを読み返すことを避けます。余分なデータ コピーのためだけでなく、CPU と GPU 間の同期を回避するためでもあります。

OpenGL 呼び出しを行う場合、ドライバーが GPU コマンドを構築し、後で GPU に送信するためにそれをキューに入れ、戻ってくる様子を想像できます。これらのコマンドは、後で GPU に送信されます。GPU は、アプリケーションを含む CPU で実行されるものから可能な限り独立して実行できるという考えです。CPU と GPU が最小限の依存関係で並行して動作することは、パフォーマンスにとって非常に望ましいことです。

ほとんどのglGet*()呼び出しでは、この非同期実行モデルは機能しません。多くの場合、GPU が保留中のすべての (または少なくともいくつかの) コマンドを完了するまで待機してから、データを返す必要があります。そのため、GPU のみが実行されている間に CPU がブロックされる可能性がありますが、これは望ましくありません。

そのため、データを読み返す必要がないように、データの CPU コピーを確実に保持する必要があります。

それ以外にも、いくつかのオプションがあります。それはすべて、使用パターン、特定のプラットフォームのパフォーマンス特性などに依存します。実際に最大限に活用するには、複数のバリエーションを実装してベンチマークする方法はありません。

あなたが説明していることについては、おそらくstd::vectorC++ の a と同様に機能するものから始めるでしょう。現時点で必要な量よりも大きな一定量のメモリ (通常は容量という名前) を割り当てます。その後、割り当てられた容量がいっぱいになるまで、再割り当てせずにデータを追加できます。その時点で、たとえば容量を 2 倍にすることができます。

glBufferData()これを OpenGL に当てはめると、NULL をデータポインタとして呼び出すことで、一定量のメモリを確保できます。割り当てた容量を追跡し、 への呼び出しでバッファを設定しますglBufferSubData()。サンプル コードに 1 つのポイントを追加するときはglBufferSubData()、新しいポイントだけで呼び出します。容量が不足した場合にのみglBufferData()、新しい容量を呼び出して、既存のすべてのデータを入力します。

疑似コードでは、初期化は次のようになります。

int capacity = 10;
glBufferData(GL_ARRAY_BUFFER,
    capacity * sizeof(Point), NULL, GL_DYNAMIC_DRAW);
std::vector<Point> data;

次に、ポイントを追加するたびに:

data.push_back(newPoint);
if (data.size() <= capacity) {
    glBufferSubData(GL_ARRAY_BUFFER,
        (data.size() - 1) * sizeof(Point), sizeof(Point), &newPoint);
} else {
    capacity *= 2;
    glBufferData(GL_ARRAY_BUFFER,
        capacity * sizeof(Point), NULL, GL_DYNAMIC_DRAW);
    glBufferSubData(GL_ARRAY_BUFFER,
        0, data.size() * sizeof(Point), &data[0]);
}

の代わりにglBufferSubData()glMapBufferRange()バッファ データを更新するために考慮すべき別のオプションがあります。さらに進んで、1 つのバッファーだけを更新するのではなく、複数のバッファーの使用を調べて、それらを循環させることができます。考えられるすべてのプラットフォームとユースケースに最適な単一のアプローチは存在しないため、ここでベンチマークが役立ちます。

于 2014-08-13T07:14:58.330 に答える