2

OpenGL で画面外にレンダリングしてから、画像を QImage に渡す必要があります。さらに、演習のために、深度とステンシル バッファも CPU に転送したいと思います。

オフスクリーンの描画には、フレーム バッファ オブジェクトをレンダー バッファと共に使用しました (テクスチャは必要ないため、テクスチャでは使用しません)。

カラー バッファ (実際の生の画像) を使用したピクセル転送操作は機能します。しかし、深さとステンシルが機能していません..深さの奇妙な画像とステンシルの何もありません。

まず、簡単な部分、私が実際に描いているもの:

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    glTranslatef(-1.5f,0.0f,-6.0f);
    glBegin(GL_TRIANGLES);
    glColor3f(1.0f,0.0f,0.0f);
    glVertex3f( 0.0f, 1.0f, 0.0f);
    glColor3f(0.0f,1.0f,0.0f);
    glVertex3f(-1.0f,-1.0f, -1.0f);
    glColor3f(0.0f,0.0f,1.0f);
    glVertex3f( 1.0f,-1.0f, 0.0f);
    glEnd();

ここでは、FBO と 3 レンダー バッファーの初期化を行います。

// frame buffer object
glGenFramebuffers(1, &fboId);
glBindFramebuffer(GL_FRAMEBUFFER, fboId);

// render buffer as color buffer
glGenRenderbuffers(1, &colorBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// attach render buffer to the fbo as color buffer
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBuffer);

// render buffer as depth buffer
glGenRenderbuffers(1, &depthBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// attach render buffer to the fbo as depth buffer
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer);

glGenRenderbuffers(1, &stencilBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, stencilBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// attach render buffer to the fbo as stencil buffer
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_BUFFER, GL_RENDERBUFFER, stencilBuffer);

最後に、ピクセル転送の 3 つの方法を次に示します。

QImage FBO::getImage()
{
    // this is called Pixel Transfer operation: http://www.opengl.org/wiki/Pixel_Transfer
    uchar *pixels;
    pixels = new uchar[width * height * 4];
    for(int i=0; i < (width * height * 4) ; i++ ) {
        pixels[i] = 0;
    }

    glBindFramebuffer(GL_FRAMEBUFFER, fboId);
    glReadPixels( 0,0,  width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);

    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    QImage qi = QImage(pixels, width, height, QImage::Format_ARGB32);
    qi = qi.rgbSwapped();

    return qi;
}

QImage FBO::getDepth()
{
    // this is called Pixel Transfer operation: http://www.opengl.org/wiki/Pixel_Transfer
    uchar *pixels;
    pixels = new uchar[width * height * 4];
    for(int i=0; i < (width * height * 4) ; i++ ) {
        pixels[i] = 0;
    }

    glBindFramebuffer(GL_FRAMEBUFFER, fboId);
    glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
    glReadPixels( 0,0,  width, height, GL_DEPTH_COMPONENT, GL_FLOAT, pixels);

    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    QImage qi = QImage(pixels, width, height, QImage::Format_ARGB32);
    qi = qi.rgbSwapped();

    return qi;
}

QImage FBO::getStencil()
{
    // this is called Pixel Transfer operation: http://www.opengl.org/wiki/Pixel_Transfer
    uchar *pixels;
    pixels = new uchar[width * height * 4];
    for(int i=0; i < (width * height * 4) ; i++ ) {
        pixels[i] = 0;
    }

    glBindFramebuffer(GL_FRAMEBUFFER, fboId);
    glBindRenderbuffer(GL_RENDERBUFFER, stencilBuffer);
    glReadPixels( 0,0,  width, height, GL_STENCIL_INDEX, GL_FLOAT, pixels);

    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    QImage qi = QImage(pixels, width, height, QImage::Format_ARGB32);
    qi = qi.rgbSwapped();

    return qi;

}

ここに2つのスクリーンショット(色と深さ、ステンシルを使用すると空のQImageが得られます):

ここに画像の説明を入力

ここに画像の説明を入力

カラーのものはまさに私が描いているものです(反転していますが、それは正常だと思います)。深さ..グラデーショングレーの三角形を持つ白い画像を期待しています..確かに、画像の形式に間違いがありますが( GL_FLOAT?)、いくつかの組み合わせを試しましたが、これが最良の結果です..紫の背景にグリッチ色が入っている..ステンシル バッファ..黒の背景に白い三角形の輪郭を期待しているのですが..なぜ何も見えないのかわかりません..

編集:

ステンシル バッファを単独で使用しないでください。そのため、少しリファクタリングしました。

FBO を宣言する場合:

// render buffer for both depth and stencil buffer
glGenRenderbuffers(1, &depthStencilBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthStencilBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// attach render buffer to the fbo as depth buffer
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthStencilBuffer);

深度のピクセル転送:

QImage FBO::getDepth()
{
    std::vector<uchar> pixels;
    pixels.reserve(width * height*4);
    for(int i=0; i < (width * height*4) ; i++ ) {
        pixels.push_back(0);
    }

    glBindFramebuffer(GL_FRAMEBUFFER, fboId);
    glBindRenderbuffer(GL_RENDERBUFFER, depthStencilBuffer);
    glReadPixels( 0,0,  width, height,  GL_DEPTH24_STENCIL8, GL_UNSIGNED_BYTE, pixels.data());

    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    std::vector<uchar> _pixels;
    _pixels.reserve(width * height*4);
    for(int i=0; i < (width * height*4) ; i++ ) {
        uchar p_red = pixels[i];
        uchar p_green = pixels[i+1];
        uchar p_blue = pixels[i+2];

        uchar p_stencil = pixels[i+3];

        _pixels.push_back(p_red);
        _pixels.push_back(p_green);
        _pixels.push_back(p_blue);

        _pixels.push_back(255); // alpha
    }

    QImage qi = QImage(_pixels.data(), width, height, QImage::Format_ARGB32);
    //qi = qi.rgbSwapped();

    return qi;
}

ステンシルは似ていますがp_stencil、rgb コンポーネントを使用しています

..結果の画像は、深度とステンシルの両方の黒い画像です

編集

Nicolas answer のおかげで、深度とステンシル バッファーの両方にレンダー バッファーを使用し、深度コンポーネントを抽出して、次のQImage::Format_ARGB32コードに適合させることができました。

QImage FBO1::getDepth()
{
    // sizeof( GLuint ) = 4 byte
    // sizeof( uchar ) = 1 byte

    std::vector<GLuint> pixels(width * height);
    glBindFramebuffer(GL_FRAMEBUFFER, fboId);
    glBindRenderbuffer(GL_RENDERBUFFER, depthStencilBuffer);
    glReadPixels( 0,0,  width, height,  GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, pixels.data());
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    std::vector<uchar> _pixels;
    for(int i=0; i < (width * height) ; i++ ) {
        GLuint color = pixels[i];

        float temp = (color & 0xFFFFFF00) >> 8; // 24 bit of depth component

        // remap temp that goes from 0 to 0xFFFFFF (24 bits) to
        // _temp that goes from 0 to 0xFF (8 bits)
        float _temp = (temp / 0xFFFFFF) * 0xFF;

        _pixels.push_back((uchar)_temp);
        _pixels.push_back((uchar)_temp);
        _pixels.push_back((uchar)_temp);
        _pixels.push_back(255);
    }

    QImage qi = QImage(_pixels.data(), width, height, QImage::Format_ARGB32);
    qi = qi.rgbSwapped();

    return qi;
}

.. ステンシル コンポーネントにはまだいくつかの問題があります (次のコードは機能しません: グリッチ画像が生成されます):

QImage FBO1::getStencil()
{
    // sizeof( GLuint ) = 4 byte
    // sizeof( uchar ) = 1 byte

    std::vector<GLuint> pixels(width * height);
    glBindFramebuffer(GL_FRAMEBUFFER, fboId);
    glBindRenderbuffer(GL_RENDERBUFFER, depthStencilBuffer);
    glReadPixels( 0,0,  width, height,  GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, pixels.data());
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    std::vector<uchar> _pixels;
    for(int i=0; i < (width * height); i++ ) {
        GLuint color = pixels[i];

        uchar temp = (color & 0x000000FF); // last 8 bit of depth component

        _pixels.push_back(temp);
        _pixels.push_back(temp);
        _pixels.push_back(temp);
        _pixels.push_back(255);
    }

    QImage qi = QImage(_pixels.data(), width, height, QImage::Format_ARGB32);
    qi = qi.rgbSwapped();

    return qi;
}
4

2 に答える 2

3

深さ..グラデーショングレーの三角形の白い画像を期待しています..

あなたのコードはそれを示唆していません。

uchar *pixels;
pixels = new uchar[width * height * 4];

これにより、整数の配列が作成されます。これはメモリ リークでもあります。代わりに使用std::vector<uchar>します。

glReadPixels( 0,0,  width, height, GL_DEPTH_COMPONENT, GL_FLOAT, pixels);

これにより、整数の配列にfloatを書き込むように OpenGL に指示します。そのため、 OpenGL は書き込み時に の配列として扱います。pixelsGLfloat

QImage qi = QImage(pixels, width, height, QImage::Format_ARGB32);

これは、コンポーネントごとに 8 ビットの整数イメージ形式のようなものとして解釈されると仮定します。floatしたがって、 s を 8 ビット整数として解釈しています。これが合理的に動作すると期待する理由はありません。

深度バッファをフロートとして読み取り、それをより合理的な方法でピクセルの色に変換するか、深度バッファを整数値として読み取り、OpenGL にグレースケール変換を行わせる必要があります。

また、2 つの個別のレンダーバッファではなく、結合された深度/ステンシル イメージを常に使用する必要があります。


glReadPixels( 0,0,  width, height,  GL_DEPTH24_STENCIL8, GL_UNSIGNED_BYTE, pixels.data());

ピクセル転送の仕組みを理解していないようです。

まず、ピクセル転送形式は、読み取るコンポーネントを指定します。サイズは指定されていません。GL_DEPTH24_STENCIL8画像フォーマットであり、ピクセル転送フォーマットではありません画像から深度とステンシルを読み取りたい場合は、 を使用しますGL_DEPTH_STENCIL。ピクセル転送形式にはサイズがありません。

したがって、この関数は OpenGL エラーを表示しているだけです。

サイズは、2 番目のパラメーターであるピクセル転送タイプから取得されます。この場合GL_UNSIGNED_BYTE、深度とステンシルを読み取り、それぞれを符号なしの 8 ビット値に変換し、ピクセルごとにそのうちの 2 つを に格納することを意味しpixels.data()ます。

深度バッファーは、ピクセルごとに 1 つの値のみを格納します。深度/ステンシルのみが 2 を格納します。OpenGL を使用してピクセルあたり 4 コンポーネントの形式にコピーすることはできません。したがって、後で QImage を作成する場合は、ピクセルごとに 1 つまたは 2 つの値を取る何らかの方法が必要です。

一般的に言えば、深度バッファを読み取りたい場合、および深度バッファのデータを実際に意味のあるものにしたい場合は、次のようにします。

std::vector<GLuint> pixels(width * height);  //std::vector value-initializes its elements.

glBindFramebuffer(GL_FRAMEBUFFER, fboId);
glReadPixels( 0,0,  width, height,  GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, pixels.data());

視覚化のためにそれらを QImage にどのように配置するかはあなた次第です。ただし、これにより unsigned int の配列が得られます。上位 24 ビットは深度コンポーネントで、下位 8 ビットはステンシルです。

于 2013-07-17T23:27:08.300 に答える
2

さて、深さの画像は、次のコードから期待できるように見えます。

 glReadPixels( 0,0,  width, height, GL_DEPTH_COMPONENT, GL_FLOAT, pixels);

 glBindFramebuffer(GL_FRAMEBUFFER, 0);

 QImage qi = QImage(pixels, width, height, QImage::Format_ARGB32);

QImage はフロートの処理方法を認識していないため、32 ビット (フロート内) の各グループをそれぞれ 8 ビットの ARGB コンポーネントであるかのように解釈します。float の下位ビットには 25 ビットの仮数があり、これらのコンポーネントに適切にマップされます。表示されるこれらのバンドは単純に増加する仮数であり、いくつかのビットにトリミングされています。

QImage は実際には非常に制限されたものです (チャンネルあたり 16 ビットなどの HDR 構成を処理することさえできず、イライラします)。とにかく、あなたの最善の策は、このフロートを 0 ~ 255 の範囲に変換し、それをグレースケール イメージとして QImage に渡すことです。

また、深度ステンシルを分離すると、パフォーマンスが低下します。可能であれば、常に結合形式を使用してください。

……まあ、ニコル・ボーラスの答えが来ました、それはまさに私が書いたものと同じです。さらに、彼はメモリリークを指摘しました。

于 2013-07-17T23:33:24.293 に答える