web-dev-qa-db-ja.com

OpenGL ES2.0での台形のパースペクティブ正しいテクスチャリング

テクスチャのある台形を描画しましたが、意図したとおりに結果が表示されません。

単一の途切れのない四辺形として表示される代わりに、2つの構成三角形が交わる対角線で不連続性が発生します。

この図は、問題を示しています。
Illustration demonstrating the source texture (a checkered pattern), the expected result, and the actual (incorrect) result
(注:最後の画像は100%忠実な表現を意図したものではありませんが、要点を理解する必要があります。)

台形は、OpenGL ES 2.0(iPhone)のGL_TRIANGLE_STRIPを使用して描画されています。それは完全に画面に面して描かれており、傾いていません(つまり、それはあなたが見ている3Dスケッチではありません!)

おそらく頂点シェーダーやフラグメントシェーダーで「遠近法補正」を実行する必要があることを理解しましたが、その方法がわかりません。

私のコードにはいくつかの単純なモデル/ビュー/射影行列の計算が含まれていますが、現在、テクスチャ座標値に影響を与えるものはありません。 更新:ユーザーの事実によるコメントによると、前のステートメントは正しくありません。

さらに、私はES 2.0仕様でこの一口を見つけましたが、それが何を意味するのか理解していません。

OpenGL ES 2.0では、すべての属性を遠近法で補間する必要があるため、PERSPECTIVE CORRECTION HINTはサポートされていません。

テクスチャを正しく描画するにはどうすればよいですか?


編集:以下にコードを追加しました:

// Vertex shader
attribute vec4 position;
attribute vec2 textureCoordinate;

varying vec2 texCoord;

uniform mat4 modelViewProjectionMatrix;

void main()
{
    gl_Position = modelViewProjectionMatrix * position;
    texCoord = textureCoordinate;
}

// Fragment shader
uniform sampler2D texture;
varying mediump vec2 texCoord;

void main()
{
    gl_FragColor = texture2D(texture, texCoord);
}

// Update and Drawing code (uses GLKit helpers from iOS)

- (void)update
{
    float fov = GLKMathDegreesToRadians(65.0f);
    float aspect = fabsf(self.view.bounds.size.width / self.view.bounds.size.height);
    projectionMatrix = GLKMatrix4MakePerspective(fov, aspect, 0.1f, 50.0f);
    viewMatrix = GLKMatrix4MakeTranslation(0.0f, 0.0f, -4.0f); // zoom out
}

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glUseProgram(shaders[SHADER_DEFAULT]);

    GLKMatrix4 modelMatrix = GLKMatrix4MakeScale(0.795, 0.795, 0.795); // arbitrary scale

    GLKMatrix4 modelViewMatrix = GLKMatrix4Multiply(viewMatrix, modelMatrix);

    GLKMatrix4 modelViewProjectionMatrix = GLKMatrix4Multiply(projectionMatrix, modelViewMatrix);
    glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX], 1, GL_FALSE, modelViewProjectionMatrix.m);

    glBindTexture(GL_TEXTURE_2D, textures[TEXTURE_WALLS]);
    glUniform1i(uniforms[UNIFORM_TEXTURE], 0);

    glVertexAttribPointer(ATTRIB_VERTEX, 3, GL_FLOAT, GL_FALSE, 0, wall.vertexArray);
    glVertexAttribPointer(ATTRIB_TEXTURE_COORDINATE, 2, GL_FLOAT, GL_FALSE, 0, wall.texCoords);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, wall.vertexCount);

}
26
Neema

(私はここで少しパントをしています。あなたの写真は台形のテクスチャリングから期待するものを正確に示していないので、おそらくあなたのケースでは何か他のことが起こっています-しかし一般的な問題はよく知られています)

テクスチャは(デフォルトで)台形全体を正しく補間しません。形状が描画のために三角形分割されると、対角線の1つがエッジとして選択され、そのエッジはテクスチャの中央をまっすぐに通過しますが、台形の中央を通過しません(対角線に沿って分割された形状を想像してください- 2つの三角形はほとんど等しくありません)。

これを機能させるには、2Dテクスチャ座標以上のものを提供する必要があります-3D(またはむしろ射影)テクスチャ座標を提供し、フラグメントシェーダーでパースペクティブ分割を実行し、補間後(またはテクスチャルックアップを使用する必要があります)同じことをする関数)。

以下は、昔ながらのGL関数(デモンストレーション目的で少し読みやすい)を使用して台形のテクスチャ座標を提供する方法を示しています。コメントアウトされた線は2Dテクスチャ座標です。正しい補間を得るために、これを射影座標に置き換えました。

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0,640,0,480,1,1000);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

const float trap_wide = 600;
const float trap_narrow = 300;
const float mid = 320;

glBegin(GL_TRIANGLE_STRIP);
glColor3f(1,1,1);

//  glTexCoord4f(0,0,0,1);
glTexCoord4f(0,0,0,trap_wide);
glVertex3f(mid - trap_wide/2,10,-10);

//  glTexCoord4f(1,0,0,1);
glTexCoord4f(trap_narrow,0,0,trap_narrow);
glVertex3f(mid - trap_narrow/2,470,-10);

//  glTexCoord4f(0,1,0,1);
glTexCoord4f(0,trap_wide,0,trap_wide);
glVertex3f(mid + trap_wide/2,10,-10);

//  glTexCoord4f(1,1,0,1);
glTexCoord4f(trap_narrow,trap_narrow,0,trap_narrow);
glVertex3f(mid + trap_narrow/2,470,-10);

glEnd();

3番目の座標は、2Dテクスチャを使用しているため、ここでは使用されていません。 4番目の座標は、補間後に他の2つを分割し、射影を提供します。明らかに、頂点で分割すると、元のテクスチャ座標が得られることがわかります。

2つのレンダリングは次のようになります。

enter image description here

台形が実際にクワッドを変換した結果である場合、ソフトウェアで変換して2D形状をGLにフィードするよりも、GLを使用してそのクワッドを描画する方が簡単/優れている可能性があります...

17
JasonD

受け入れられた答えは正しい解決策と説明を提供しますが、OpenGL(ES)2.0パイプラインでもう少し助けを探している人のために...

const GLfloat L = 2.0;
const GLfloat Z = -2.0;
const GLfloat W0 = 0.01;
const GLfloat W1 = 0.10;

/** Trapezoid shape as two triangles. */
static const GLKVector3 VERTEX_DATA[] = {
    {{-W0,    0,  Z}},
    {{+W0,    0,  Z}},
    {{-W1,    L,  Z}},

    {{+W0,    0,  Z}},
    {{+W1,    L,  Z}},
    {{-W1,    L,  Z}},
};

/** Add a 3rd coord to your texture data.  This is the perspective divisor needed in frag shader */
static const GLKVector3 TEXTURE_DATA[] = {
    {{0, 0, 0}},
    {{W0, 0, W0}},
    {{0, W1, W1}},

    {{W0, 0, W0}},
    {{W1, W1, W1}},
    {{0, W1, W1}},
};

////////////////////////////////////////////////////////////////////////////////////

// frag.glsl

varying vec3 v_texPos;
uniform sampler2D u_texture;

void main(void) 
{
    // Divide the 2D texture coords by the third projection divisor
    gl_FragColor = texture2D(u_texture, v_texPos.st / v_texPos.p);
}

または、シェーダーでは、@ maverick9888の回答に従って、texture2Dprojを使用できますが、iOS/OpenGLES2の場合は、vec3入力のみをサポートします...

void main(void) 
{
    gl_FragColor = texture2DProj(u_texture, v_texPos);
}

私は実際にそれを適切にベンチマークしていませんが、私の非常に単純なケース(実際には1Dテクスチャ)の場合、分割バージョンは少しきびきびと見えます。

0

ここで試しているのは、歪んだテクスチャです。サンプルフラグメントシェーダーは次のとおりです。

_precision mediump float;
varying vec4 vtexCoords;
uniform sampler2D sampler;

void main()
{
   gl_FragColor = texture2DProj(sampler,vtexCoords);
}
_

異なって見えるべき2つのことは次のとおりです。

1)_varying vec4 vtexCoords;_を使用しています。テクスチャ座標は4次元です。 2)texture2DProj()の代わりにtexture2D()が使用されます

台形の小辺と大辺の長さに基づいて、テクスチャ座標を割り当てます。次のURLが役立つかもしれません: http://www.xyzw.us/~cass/qcoord/

0
maverick9888