我画了一个带纹理的梯形,但结果并没有像我原先想象的那样出现。
不是出现为单个完整的四边形,而是在对角线处出现不连续性,其中两个包含三角形相交。
此图说明了问题:
(注意:最后一张图片不是100%忠实的代表,但它应该得到重点。)
使用OpenGL ES 2.0中的GL_TRIANGLE_STRIP
(在iPhone上)绘制梯形图。它被完全面向屏幕绘制,并且没有被倾斜(即,这不是您正在看到的3D草图!)
我已经明白我需要执行“透视校正”,大概是在我的顶点和/或片段着色器中,但我不清楚如何做到这一点。
我的代码包含一些简单的模型/视图/投影矩阵数学,但它们当前都没有影响我的纹理坐标值。 更新:根据用户infact的评论
,上一条语句不正确此外,我在ES 2.0规范中找到了这个小窍门,但不明白这意味着什么:
不支持PERSPECTIVE CORRECTION HINT,因为 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);
}
答案 0 :(得分:17)
(我在这里采取了一些措施,因为你的图片没有准确显示我对纹理化梯形的期望,所以在你的情况下可能会发生其他事情 - 但一般问题是众所周知的)< / p>
纹理不会(默认情况下)在梯形上正确插值。当形状被绘制成三角形以进行绘制时,将选择其中一个对角线作为边缘,并且当该边缘直接穿过纹理的中间时,它不是通过梯形的中间(图片沿着对角线划分的形状 - 两个三角形非常不相等。
您需要提供超过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绘制四边形可能更容易/更好,而不是用软件转换它并将二维形状输入GL ...
答案 1 :(得分: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/
答案 2 :(得分: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的回答,您可以使用texture2Dproj
但对于iOS / OpenGLES2,它仍然只支持vec3
输入...
void main(void)
{
gl_FragColor = texture2DProj(u_texture, v_texPos);
}
我还没有真正对它进行正确的基准测试,但对于我非常简单的情况(真正的1d纹理),除法版本看起来有点笨拙。