私の目的は、ウィンドウなしでOpenGLシーンを直接ファイルにレンダリングすることです。シーンが私の画面解像度よりも大きい場合があります。
これどうやってするの?
可能であれば、レンダリング領域のサイズを任意のサイズ、たとえば10000x10000に選択できるようにしたいですか?
私の目的は、ウィンドウなしでOpenGLシーンを直接ファイルにレンダリングすることです。シーンが私の画面解像度よりも大きい場合があります。
これどうやってするの?
可能であれば、レンダリング領域のサイズを任意のサイズ、たとえば10000x10000に選択できるようにしたいですか?
すべてはで始まりglReadPixels
ます。これを使用して、GPUの特定のバッファーに格納されているピクセルをメインメモリ(RAM)に転送します。ドキュメントでわかるように、どのバッファを選択するかについての議論はありません。OpenGLで通常行われているように、読み取る現在のバッファは状態であり、。で設定できますglReadBuffer
。
したがって、非常に基本的なオフスクリーンレンダリング方法は次のようになります。私はc++擬似コードを使用しているので、エラーが含まれている可能性がありますが、一般的なフローを明確にする必要があります。
//Before swapping
std::vector<std::uint8_t> data(width*height*4);
glReadBuffer(GL_BACK);
glReadPixels(0,0,width,height,GL_BGRA,GL_UNSIGNED_BYTE,&data[0]);
これにより、現在のバックバッファ(通常は描画先のバッファ)が読み取られます。バッファを交換する前にこれを呼び出す必要があります。上記の方法でバックバッファを完全に読み取り、それをクリアして、スワップする前にまったく異なるものを描画することもできることに注意してください。技術的にはフロントバッファを読み取ることもできますが、理論的には実装でフロントバッファにゴミが含まれる可能性のある最適化を行うことが許可されているため、これは推奨されません。
これにはいくつかの欠点があります。まず第一に、私たちは実際にはオフスクリーンレンダリングを行いません。画面バッファにレンダリングし、それらから読み取ります。バックバッファをスワップしないことでオフスクリーンレンダリングをエミュレートできますが、正しく感じられません。その次に、フロントバッファとバックバッファは、ピクセルを読み戻すのではなく、表示するように最適化されています。そこで、フレームバッファオブジェクトが登場します。
基本的に、FBOを使用すると、デフォルト以外のフレームバッファ(FRONTバッファやBACKバッファなど)を作成して、画面バッファの代わりにメモリバッファに描画できます。実際には、テクスチャまたはレンダーバッファのいずれかに描画できます。前者は、OpenGL自体のピクセルをテクスチャとして再利用する場合(たとえば、ゲームのナイーブな「セキュリティカメラ」)に最適です。後者は、レンダリング/リードバックする場合に最適です。これにより、上記のコードは次のようになります。これも疑似コードです。入力ミスやステートメントを忘れた場合でも、私を殺さないでください。
//Somewhere at initialization
GLuint fbo, render_buf;
glGenFramebuffers(1,&fbo);
glGenRenderbuffers(1,&render_buf);
glBindRenderbuffer(render_buf);
glRenderbufferStorage(GL_RENDERBUFFER, GL_BGRA8, width, height);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER,fbo);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, render_buf);
//At deinit:
glDeleteFramebuffers(1,&fbo);
glDeleteRenderbuffers(1,&render_buf);
//Before drawing
glBindFramebuffer(GL_DRAW_FRAMEBUFFER,fbo);
//after drawing
std::vector<std::uint8_t> data(width*height*4);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glReadPixels(0,0,width,height,GL_BGRA,GL_UNSIGNED_BYTE,&data[0]);
// Return to onscreen rendering:
glBindFramebuffer(GL_DRAW_FRAMEBUFFER,0);
これは単純な例です。実際には、デプス(およびステンシル)バッファー用のストレージも必要になる可能性があります。テクスチャにレンダリングすることもできますが、これは演習として残しておきます。いずれの場合も、実際のオフスクリーンレンダリングを実行するようになり、バックバッファーを読み取るよりも高速に動作する可能性があります。
最後に、ピクセルバッファオブジェクトを使用して、読み取りピクセルを非同期にすることができます。問題はglReadPixels
、ピクセルデータが完全に転送されるまでブロックされ、CPUが停止する可能性があることです。PBOを使用すると、とにかくバッファを制御するため、実装はすぐに戻る可能性があります。パイプラインがブロックするのは、バッファーをマップするときだけです。ただし、PBOはRAMのみにデータをバッファリングするように最適化されている可能性があるため、このブロックにかかる時間は大幅に短縮されます。読み取られたピクセルコードは次のようになります。
//Init:
GLuint pbo;
glGenBuffers(1,&pbo);
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
glBufferData(GL_PIXEL_PACK_BUFFER, width*height*4, NULL, GL_DYNAMIC_READ);
//Deinit:
glDeleteBuffers(1,&pbo);
//Reading:
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
glReadPixels(0,0,width,height,GL_BGRA,GL_UNSIGNED_BYTE,0); // 0 instead of a pointer, it is now an offset in the buffer.
//DO SOME OTHER STUFF (otherwise this is a waste of your time)
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo); //Might not be necessary...
pixel_data = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
キャップの部分は必須です。glReadPixels
PBOにaを発行し、その後にそのPBOを発行するとglMapBuffer
、多くのコードしか得られません。確かにglReadPixels
すぐに戻る可能性がglMapBuffer
ありますが、読み取りバッファからPBOおよびメインRAMのメモリブロックにデータを安全にマップする必要があるため、は停止します。
また、私はどこでもGL_BGRAを使用していることに注意してください。これは、多くのグラフィックカードが内部でこれを最適なレンダリング形式(またはアルファなしのGL_BGRバージョン)として使用しているためです。これは、このようなピクセル転送の最速のフォーマットである必要があります。私はこれについて数ヶ月前に読んだnvidiaの記事を見つけようとします。
OpenGL ES 2.0を使用している場合GL_DRAW_FRAMEBUFFER
、使用できない可能性がありますGL_FRAMEBUFFER
。その場合にのみ使用してください。
メインコンテキストを作成するダミーウィンドウを作成すること(レンダリングしないでください。APIで作成する必要があるため、そこにあるだけです)は、許容できる実装戦略であると想定します。
オプションは次のとおりです。
ピクセルバッファ、またはpbuffer(ピクセルバッファオブジェクトではありません)は、何よりもまずOpenGLコンテキストです。基本的には、通常どおりウィンドウを作成してから、ピクセル形式を選択しますwglChoosePixelFormatARB
(pbuffer形式はここから取得する必要があります)。次に、を呼び出しwglCreatePbufferARB
て、ウィンドウのHDCと使用するピクセルバッファ形式を指定します。ああ、そして幅/高さ; 実装の最大幅/高さを照会できます。
pbufferのデフォルトのフレームバッファは画面に表示されません。最大幅/高さは、ハードウェアが使用できるようにするものです。したがって、それにレンダリングして、それからglReadPixels
読み戻すために使用できます。
ウィンドウコンテキストでオブジェクトを作成した場合は、特定のコンテキストとコンテキストを共有する必要があります。それ以外の場合は、pbufferコンテキストを完全に個別に使用できます。ウィンドウコンテキストを破棄しないでください。
ここでの利点は、実装サポートが強化されていることです(ただし、代替をサポートしていないほとんどのドライバーは、サポートされなくなったハードウェア用の古いドライバーでもあります。またはIntelハードウェアです)。
欠点はこれらです。PbufferはコアOpenGLコンテキストでは機能しません。それらは互換性のために機能するかもしれませんが、wglCreatePbufferARB
OpenGLのバージョンとプロファイルに関する情報を提供する方法はありません。
フレームバッファオブジェクトは、pbufferよりも「適切な」オフスクリーンレンダーターゲットです。FBOはコンテキスト内にあり、pbufferは新しいコンテキストの作成に関するものです。
FBOは、レンダリング先の画像の単なるコンテナです。実装で許可されている最大ディメンションを照会できます。あなたはそれを仮定することができますGL_MAX_VIEWPORT_DIMS
(FBOがバインドされているかどうかに基づいて変化するため、これをチェックする前にFBOがバインドされていることを確認してください)。
これらからテクスチャをサンプリングしていないため(値を読み戻すだけです)、テクスチャの代わりにレンダーバッファを使用する必要があります。それらの最大サイズは、テクスチャの最大サイズよりも大きい場合があります。
利点は使いやすさです。ピクセル形式などを処理する必要はなく、通話に適した画像形式を選択するだけです。glRenderbufferStorage
唯一の本当の欠点は、それらをサポートするハードウェアの帯域が狭いことです。一般に、AMDまたはNVIDIAが引き続きサポートしているもの(現在、GeForce6xxx以上[xの数に注意]およびRadeonHDカード)は、ARB_framebuffer_objectまたはOpenGL 3.0+(コア機能)にアクセスできます。 )。古いドライバーは、EXT_framebuffer_objectをサポートしているだけかもしれません(これにはいくつかの違いがあります)。Intelハードウェアは持ち寄りです。3.xまたは4.xのサポートを主張している場合でも、ドライバーのバグが原因で失敗する可能性があります。
GL実装の最大FBOサイズを超えるものをレンダリングする必要がある場合は、次のようになりますlibtr
。
TR(タイルレンダリング)ライブラリは、タイルレンダリングを行うためのOpenGLユーティリティライブラリです。タイルレンダリングは、大きな画像を断片(タイル)で生成するための手法です。
TRはメモリ効率が良いです。メインメモリにフルサイズの画像バッファを割り当てずに、任意の大きさの画像ファイルを生成できます。
最も簡単な方法は、フレームバッファオブジェクト(FBO)と呼ばれるものを使用することです。ただし、openglコンテキストを作成するにはウィンドウを作成する必要があります(ただし、このウィンドウは非表示にすることができます)。
目標を達成する最も簡単な方法は、FBOを使用してオフスクリーンレンダリングを行うことです。また、テクスチャにレンダリングしてからteximageを取得する必要はありません。バッファリングして関数 glReadPixelsを使用するだけです。このリンクは役に立ちます。フレームバッファオブジェクトの例を参照してください