27

我画了一个带纹理的梯形,但是结果并没有像我预期的那样出现。

不是显示为一个完整的四边形,而是在其两个组成三角形相交的对角线上出现不连续性。

这张插图说明了这个问题:(
展示源纹理(方格图案)、预期结果和实际(不正确)结果的插图
注意:最后一张图片并不是要 100% 忠实地表示,但它应该能说明问题。)

梯形是GL_TRIANGLE_STRIP在 OpenGL ES 2.0(在 iPhone 上)中绘制的。它完全面向屏幕绘制,并且没有倾斜(即,这不是您看到的 3D 草图!)

我已经明白我需要执行“透视校正”,大概是在我的顶点和/或片段着色器中,但我不清楚如何做到这一点。

我的代码包括一些简单的模型/视图/投影矩阵数学,但目前没有一个会影响我的纹理坐标值。更新:根据用户 infact 的评论,之前的说法是不正确的。

此外,我在 ES 2.0 规范中找到了这个花絮,但不明白它的含义:

不支持透视校正提示,因为OpenGL ES 2.0 要求对所有属性进行透视插值。

如何正确绘制纹理?


编辑:在下面添加代码:

// 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);

}
4

3 回答 3

21

(我在这里有点赌注,因为你的图片没有完全显示我对梯形纹理的期望,所以在你的情况下可能发生了其他事情 - 但一般问题是众所周知的)

纹理不会(默认情况下)在梯形上正确插值。绘制三角形时,将选择对角线之一作为边缘,而该边缘笔直穿过纹理的中间,而不是穿过梯形的中间(想象沿对角线划分的形状 -这两个三角形非常不相等)。

您需要提供的不仅仅是 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();

此处未使用第三个坐标,因为我们仅使用 2D 纹理。第四个坐标将在插值后划分其他两个,提供投影。显然,如果你在顶点处划分它,你会看到你得到了原始的纹理坐标。

这是两个渲染图的样子:

在此处输入图像描述

如果您的梯形实际上是转换四边形的结果,那么使用 GL 绘制该四边形可能更容易/更好,而不是在软件中转换它并将 2D 形状输入到 GL...

于 2013-03-06T10:40:57.243 回答
0

你在这里尝试的是倾斜的纹理。示例片段着色器如下:

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

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

看起来不同的两件事是:

1)我们正在使用varying vec4 vtexCoords;. 纹理坐标是 4 维的。2)texture2DProj()用于代替texture2D()

根据梯形的小边和大边的长度,您将分配纹理坐标。以下网址可能会有所帮助: http ://www.xyzw.us/~cass/qcoord/

于 2014-08-11T08:38:39.013 回答
0

接受的答案给出了正确的解决方案和解释,但对于那些在 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 的回答,你可以使用texture2DprojiOS / OpenGLES2 它仍然只支持vec3输入......

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

我还没有真正正确地对其进行基准测试,但对于我非常简单的情况(真的是一维纹理),除法版本似乎有点快。

于 2014-09-09T06:45:57.083 回答