OpenGLを学びます。 レッスン4.7-高度なデータ処理

画像

高度なデータ処理





主にOpenGLのバッファーを使用して、かなり長い間データを保存しました。 バッファを操作するより興味深い方法と、テクスチャを使用してシェーダーに大量のデータを転送する他の興味深い方法があります。 このガイドでは、より興味深いバッファ関数のいくつかと、大量のデータを格納するためにテクスチャオブジェクトを使用する方法について説明します(レッスンのテクスチャ部分はまだ記述されていません)。



内容
パート1.はじめに



  1. Opengl
  2. ウィンドウ作成
  3. こんにちはウィンドウ
  4. こんにちはトライアングル
  5. シェーダー
  6. テクスチャー
  7. 変換
  8. 座標系
  9. カメラ


パート2.基本的な照明



  1. 照明の基本
  2. 素材
  3. テクスチャマップ
  4. 光源
  5. 複数の光源




パート3. 3Dモデルをダウンロードする



  1. Assimpライブラリ
  2. メッシュポリゴンクラス
  3. モデルクラスモデル




パート4.高度なOpenGL機能



  1. 深度テスト
  2. ステンシルテスト
  3. 色混合
  4. 顔のクリッピング
  5. フレームバッファ
  6. キュービックカード




OpenGLのバッファは、メモリの特定の部分を管理するオブジェクトであり、それ以上のものはありません。 特定のターゲットバッファにバインドすることにより、バッファに値を付加します。 バッファーは、 GL_ARRAY_BUFFERにバインドするときは単なる頂点配列バッファーですがGL_ELEMENT_ARRAY_BUFFERに簡単に関連付けることもできます。 OpenGLはターゲットごとにバッファを保存し、ターゲットに基づいてバッファを異なる方法で処理します。



これまで、バッファオブジェクトによって制御されるメモリを埋めるためにglBufferDataを呼び出しました 。これはメモリの一部を割り当て、このメモリにデータを追加します。 データ引数としてNULLを渡した場合、関数はメモリを割り当て、それを埋めません。 これは、最初にメモリの特定の部分を予約し、将来このバッファに戻って徐々に満たす場合に便利です。



単一の関数呼び出しでバッファー全体を埋める代わりに、バッファーの特定の領域をglBufferSubDataで埋めることもできます。 この関数は、引数としてターゲットバッファ、オフセット、データサイズ、およびデータ自体を受け取ります。 この関数の新機能は、バッファの充填を開始する場所を示すオフセットを設定できることです。 これにより、バッファメモリの特定の部分のみを挿入/更新できます。 バッファーには十分なメモリが割り当てられている必要があるため、 glBufferData関数の呼び出しはglBufferSubDataの呼び出しの前に行う必要があることに注意してください。



glBufferSubData(GL_ARRAY_BUFFER, 24, sizeof(data), &data); // : [24, 24 + sizeof(data)]
      
      





データをバッファに転送する別の方法は、バッファメモリへのポインタを見つけて、自分で直接データをバッファにコピーすることです。 glMapBuffer関数を呼び出すと OpenGLは現在関連付けられているバッファーのメモリへのポインターを返し、それを操作します。



 float data[] = { 0.5f, 1.0f, -0.35f ... }; glBindBuffer(GL_ARRAY_BUFFER, buffer); //   void *ptr = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); //     memcpy(ptr, data, sizeof(data)); //  OpenGL,       glUnmapBuffer(GL_ARRAY_BUFFER);
      
      





OpenGLは、 glUnmapBuffer関数で指示するとすぐに、ポインターで処理が完了したことを認識します。 この関数の実行後、ポインターは無効になり、OpenGLがデータをバッファーに正常に配置した場合、関数はGL_TRUEを返します。



glMapBufferを使用すると、最初に一時メモリにデータを保存することなく、データをバッファに直接配置するのに役立ちます。



グループ化された頂点属性



glVertexAttribPointerを使用して、頂点配列バッファーの内容の属性の場所を指定できます。 頂点配列バッファーでは、属性を変更しました。 各頂点の位置、法線、テクスチャ座標を順番に配置しました。 バッファについてもう少し理解したので、別のアプローチを使用できます。



そのため、すべてのベクターデータを交互にではなく、各タイプの属性の大きなチャンクにパックできます。 代替データ123123123123のレイアウトの代わりに、バッチアプローチ111122223333を取得します。



ファイルから頂点データをロードすると、通常、位置の配列、法線の配列、および/またはテクスチャ座標の配列を取得します。 これらの配列をストライプデータの大きな配列に接続するには、ある程度の労力が必要になる場合があります。 バッチアプローチを使用することは、 glBufferSubData関数を使用して実装できる簡単なソリューションです。



 float positions[] = { ... }; float normals[] = { ... }; float tex[] = { ... }; //   glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(positions), &positions); glBufferSubData(GL_ARRAY_BUFFER, sizeof(positions), sizeof(normals), &normals); glBufferSubData(GL_ARRAY_BUFFER, sizeof(positions) + sizeof(normals), sizeof(tex), &tex);
      
      





このアプローチにより、予備処理を行わずに、属性の配列をバッファ全体に直接送信できます。 それらを1つの大きな配列に結合し、 glBufferDataを使用してすぐに入力することもできますが、 glBufferSubDataを使用すると 、このようなタスクに最適です。



これらの変更を反映するために、頂点属性へのポインターも更新する必要があります。



 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)(sizeof(positions))); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)(sizeof(positions) + sizeof(normals)));
      
      





頂点属性の次の属性はその3(または2)コンポーネントの直後に見つかるため、 ストライドパラメーターは頂点属性のサイズに等しいことに注意してください。



これにより、頂点属性を設定および定義するための別のアプローチが得られます。 両方のアプローチを使用しても、OpenGLに直接的な利点はありません;基本的に、頂点属性を設定するより組織化された方法です。 使用するアプローチは、設定とアプリケーションのタイプのみに基づいています。



コピーバッファ



バッファがデータでいっぱいになったら、このデータを他のバッファと共有するか、バッファの内容を別のバッファにコピーすることができます。 glCopyBufferSubData関数を使用すると、1つのバッファーから別のバッファーにデータを比較的簡単にコピーできます。 関数のプロトタイプは次のとおりです。



 void glCopyBufferSubData(GLenum readtarget, GLenum writetarget, GLintptr readoffset, GLintptr writeoffset, GLsizeiptr size);
      
      





readtargetおよびwritetarget引数は、コピー元およびコピー先のターゲットバッファー値を受け入れます。 たとえば、 VERTEX_ARRAY_BUFFERバッファーからVERTEX_ELEMENT_ARRAY_BUFFERバッファーにデータをコピーし、これらのバッファーターゲットをそれぞれ読み取りおよび書き込みターゲットとして指定できます。 これらのターゲットにバインドされているバッファが影響を受けます。



しかし、2つの異なるバッファー(頂点配列バッファー)に対してデータの読み取りと書き込みを行う場合はどうでしょうか。 2つのバッファを同じバッファターゲットに同時にバインドすることはできません。 このため、そしてそれだけのために、OpenGLはGL_COPY_READ_BUFFERおよびGL_COPY_WRITE_BUFFERと呼ばれる2つのストレージ目標を提供します。 次に、選択したバッファーをこれらの新しいターゲットバッファーに関連付け、これらのターゲットをreadtargetおよびwritetarget引数として設定します



glCopyBufferSubDataは、指定されたreadoffset値から指定されたサイズsizeのデータを読み取り、 writeoffsetとしてwritetarget書き込みバッファー書き込みます。 2つの頂点配列バッファーの内容をコピーする例を以下に示します。



 float vertexData[] = { ... }; glBindBuffer(GL_COPY_READ_BUFFER, vbo1); glBindBuffer(GL_COPY_WRITE_BUFFER, vbo2); glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, sizeof(vertexData));
      
      





また、writetargetバッファーを新しいタイプのターゲットバッファーの1つにバインドすることでこれを行うこともできます。



 float vertexData[] = { ... }; glBindBuffer(GL_ARRAY_BUFFER, vbo1); glBindBuffer(GL_COPY_WRITE_BUFFER, vbo2); glCopyBufferSubData(GL_ARRAY_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, sizeof(vertexData));
      
      





バッファの操作方法に関する追加の知識があれば、すでにもっと面白い方法でそれらを使用できます。 OpenGLに飛び込むと、これらのバッファを操作する方法がより便利になります。 Uniform Buffer Objectsについて説明する次のレッスンでは、 glBufferSubData関数が役立ちます。



All Articles