web-dev-qa-db-ja.com

Openglピクセルパーフェクト2D描画

私は2Dエンジンに取り組んでいます。それは既にかなりうまくいきます、しかし私はピクセルエラーを得続けます。

たとえば、私のウィンドウは960x540ピクセルで、(0、0)から(959、0)まで線を引きます。私は走査線0のすべてのピクセルが色に設定されることを期待しますが、いいえ:右端のピクセルは描画されません。ピクセル539に垂直に描画する場合も同じ問題です。実際に描画するには、(960、0)または(0、540)に描画する必要があります。

私はピクセル時代に生まれたので、これは正しい結果ではないと確信しています。画面のサイズが320x200ピクセルの場合、0〜319および0〜199で描画でき、画面がいっぱいになります。今、私は右/下のピクセルが描かれていない画面になってしまいます。

これはさまざまな原因が考えられます。openglラインプリミティブがピクセルからピクセルまで描画されることを期待していますが、最後のピクセルは実際には排他的ですか?それですか?投影行列が正しくありませんか? 960x540のバックバッファーがある場合、実際には1ピクセル多いという誤った仮定の下にいますか?他に何か?

誰かが私を助けてくれますか?私は長い間この問題を調査してきましたが、大丈夫だと思ったときはいつも、実際にはそうではないことがわかりました。

これが私のコードの一部です。私はそれを可能な限り削除しようとしました。 line関数を呼び出すと、すべての座標に0.375、0.375が追加され、ATIアダプターとnvidiaアダプターの両方で正しく調整されます。

int width = resX();
int height = resY();

for (int i = 0; i < height; i += 2)
    rm->line(0, i, width - 1, i, vec4f(1, 0, 0, 1));
for (int i = 1; i < height; i += 2)
    rm->line(0, i, width - 1, i, vec4f(0, 1, 0, 1));

// when I do this, one pixel to the right remains undrawn

void rendermachine::line(int x1, int y1, int x2, int y2, const vec4f &color)
{
    ... some code to decide what std::vector the coordinates should be pushed into
    // m_z is a z-coordinate, I use z-buffering to preserve correct drawing orders
    // vec2f(0, 0) is a texture-coordinate, the line is drawn without texturing
    target->Push_back(vertex(vec3f((float)x1 + 0.375f, (float)y1 + 0.375f, m_z), color, vec2f(0, 0)));
    target->Push_back(vertex(vec3f((float)x2 + 0.375f, (float)y2 + 0.375f, m_z), color, vec2f(0, 0)));
}

void rendermachine::update(...)
{
    ... render target object is queried for width and height, in my test it is just the back buffer so the window client resolution is returned
    mat4f mP;
    mP.setOrthographic(0, (float)width, (float)height, 0, 0, 8000000);

    ... all vertices are copied to video memory

    ... drawing
    if (there are lines to draw)
        glDrawArrays(GL_LINES, (int)offset, (int)lines.size());

    ...
}

// And the (very simple) shader to draw these lines

// Vertex shader
    #version 120
    attribute vec3 aVertexPosition;
    attribute vec4 aVertexColor;
    uniform mat4 mP;
    varying vec4 vColor;
    void main(void) {
        gl_Position = mP * vec4(aVertexPosition, 1.0);
        vColor = aVertexColor;
    }

// Fragment shader
    #version 120
    #ifdef GL_ES
    precision highp float;
    #endif
    varying vec4 vColor;
    void main(void) {
        gl_FragColor = vColor.rgb;
    }
30
scippie

OpenGLでは、ラインは「Diamond Exit」ルールを使用してラスタライズされます。これはほぼ終了座標が排他的であると言っているのと同じですが、完全ではありません...

これはOpenGL仕様が言っていることです: http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node47.html

OpenGLのFAQもご覧ください http://www.opengl.org/archives/resources/faq/technical/rasterization.htm 、item "14.090どのようにしてラインの正確なピクセル化を取得できますか? 」 「OpenGL仕様では、幅広いラインレンダリングハードウェアが許可されているため、正確なピクセル化がまったくできない場合があります。」

多くの人は、OpenGLで行を使用すべきではないと主張します。それらの動作は、意味のあるものではなく、古代のSGIハードウェアの動作に基づいています。 (そして、幅> 1の線は見栄えの良い方法で使用することはほぼ不可能です!)

15
lurker number 5

OpenGL座標空間には整数の概念がないことに注意してください。すべてが浮動小数点数であり、OpenGLピクセルの「中心」は実際には左上隅ではなく0.5、0.5にあります。したがって、0,0から10,10までの1ピクセル幅の線が必要な場合、実際には0.5,0.5から10.5,10.5までの線を描画する必要がありました。

これは、アンチエイリアシングをオンにした場合、アンチエイリアシングがあり、50,0から50,100まで描画しようとした場合、ラインが2ピクセルの間にあるためにぼやけた2px幅のラインが表示される場合があります。

6
Lie Ryan