OpenGL ES 2.0 纹理不会显示
OpenGL ES 2.0 Texture Won't Display
所以我目前正在使用 iOS 学习 OpenGL ES 2.0 并从事迷宫游戏。迷宫是随机生成的(所以不是加载模型),我的努力是在迷宫的墙壁和地板上做纹理。我的方法是将迷宫视为一系列立方体,并且我有单独绘制立方体各个面的代码(因此我可以通过简单地留下一些面来创建路径)。
使用捕获 GPU 帧,我确认纹理确实正确加载,帧缓冲区中的数据正确,我没有收到任何错误。我可以看到我的其他灯光效果(所以脸不是完全黑的),但没有出现纹理。
这是我定义立方体面的方式
GLfloat rightCubeVertexData[] =
{
0.5f, -0.5f, -0.5f,
0.5f, -0.5f, 0.5f,
0.5f, 0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
0.5f, -0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
};
GLfloat rightCubeNormalData[] =
{
-1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
};
GLfloat rightCubeTexCoords[] =
{
0.0, 0.0,
1.0, 0.0,
0.0, 1.0,
0.0, 1.0,
1.0, 0.0,
1.0, 1.0,
};
其他面基本上以相同的方式定义(除了它们分别在一个数组中,拆分位置、法线和 tex 坐标只是我尝试过的方法;我只是想让一张脸变成纹理然后我将扩展到其余部分)。
下面是我如何将数据加载到缓冲区
glGenVertexArraysOES(1, &_rightVertexArray);
glBindVertexArrayOES(_rightVertexArray);
glGenBuffers(3, _rightVertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, _rightVertexBuffer[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(rightCubeVertexData), rightCubeVertexData, GL_STATIC_DRAW);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 12, BUFFER_OFFSET(0));
glBindBuffer(GL_ARRAY_BUFFER, _rightVertexBuffer[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(rightCubeNormalData), rightCubeNormalData, GL_STATIC_DRAW);
glEnableVertexAttribArray(GLKVertexAttribNormal);
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 12, BUFFER_OFFSET(0));
glBindBuffer(GL_ARRAY_BUFFER, _rightVertexBuffer[2]);
glBufferData(GL_ARRAY_BUFFER, sizeof(rightCubeTexCoords), rightCubeTexCoords, GL_STATIC_DRAW);
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, 12, BUFFER_OFFSET(0));
同样,使用三个缓冲区是一项实验,其余的都在一个缓冲区中定义,并带有偏移量。
这是我加载纹理的方式
crateTexture = [self setupTexture:@"crate.jpg"];
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, crateTexture);
glUniform1i(uniforms[UNIFORM_TEXTURE], 0);
// Load in and set up texture image (adapted from Ray Wenderlich)
- (GLuint)setupTexture:(NSString *)fileName
{
CGImageRef spriteImage = [UIImage imageNamed:fileName].CGImage;
if (!spriteImage) {
NSLog(@"Failed to load image %@", fileName);
exit(1);
}
size_t width = CGImageGetWidth(spriteImage);
size_t height = CGImageGetHeight(spriteImage);
GLubyte *spriteData = (GLubyte *) calloc(width*height*4, sizeof(GLubyte));
CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width*4, CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), spriteImage);
CGContextRelease(spriteContext);
GLuint texName;
glGenTextures(1, &texName);
glBindTexture(GL_TEXTURE_2D, texName);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
free(spriteData);
return texName;
}
然后,在适当的时候,我简单地调用 glDrawArrays 来绘制脸部。我对此完全感到困惑,这可能是一个非常愚蠢的错误,但是任何人都可以提供任何帮助将不胜感激。
P.S。这是我的片段着色器
varying vec3 eyeNormal;
varying vec4 eyePos;
varying vec2 texCoordOut;
uniform sampler2D texture;
uniform vec3 flashlightPosition;
uniform vec3 diffuseLightPosition;
uniform vec4 diffuseComponent;
uniform float shininess;
uniform vec4 specularComponent;
uniform vec4 ambientComponent;
void main()
{
vec4 ambient = ambientComponent;
vec3 N = normalize(eyeNormal);
float nDotVP = max(0.0, dot(N, normalize(diffuseLightPosition)));
vec4 diffuse = diffuseComponent * nDotVP;
vec3 E = normalize(-eyePos.xyz);
vec3 L = normalize(flashlightPosition - eyePos.xyz);
vec3 H = normalize(L+E);
float Ks = pow(max(dot(N, H), 0.0), shininess);
vec4 specular = Ks*specularComponent;
if( dot(L, N) < 0.0 ) {
specular = vec4(0.0, 0.0, 0.0, 1.0);
}
gl_FragColor = (ambient + diffuse + specular) * texture2D(texture, texCoordOut);
//gl_FragColor = ambient + diffuse + specular;
gl_FragColor.a = 1.0;
}
是的,所有统一名称都是正确的,并且与主代码中的某些内容相对应。
编辑:这是顶点着色器
precision mediump float;
attribute vec4 position;
attribute vec3 normal;
attribute vec2 texCoordIn;
varying vec3 eyeNormal;
varying vec4 eyePos;
varying vec2 texCoordOut;
uniform mat4 modelViewProjectionMatrix;
uniform mat4 modelViewMatrix;
uniform mat3 normalMatrix;
void main()
{
eyeNormal = (normalMatrix * normal);
eyePos = modelViewMatrix * position;
texCoordOut = texCoordIn;
gl_Position = modelViewProjectionMatrix * position;
}
原来我在编译shader的时候忘了绑定属性location。需要添加行
glBindAttribLocation(_program, GLKVertexAttribTexCoord0, "texCoordIn");
加载着色器方法。
根据评论总结完成的过程...
在处理纹理时可能会出错,知道如何查明问题出在哪里是很好的。
纹理本身要注意什么:
检查是否设置了
等参数
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- 检查将制服设置为
glUniform1i(uniformName, 0)
,其中最后一个参数对应于活动纹理而不是纹理 ID。
- 其他包括检查统一名称是否正确,是否绑定了纹理。如果可能的话,检查调试器是否正确加载了纹理。
除此之外,您的纹理坐标可能会被弄乱,这似乎是一个非常普遍的问题。要进行调试,最好将片段着色器中从纹理获取的颜色替换为纹理坐标本身。例如。将 texture2D(texture, texCoordOut)
替换为 vec4(texCoordOut.x, texCoordOut.y, .0, 1.0)
。由于纹理坐标应在 [0,1] 范围内,您应该会在场景中看到红色和绿色之间的漂亮渐变。如果你没有看到它们,那么你的纹理坐标就乱七八糟了:如果所有的东西都是黑色的,那么你的坐标都是零,如果大部分是黄色的,那么你的坐标很可能太大了。
在你的例子中,纹理坐标都是黑色的,这意味着你总是从纹理中获取第一个像素,因此场景中的颜色是恒定的。此时要检查的是:
- 你推送给GPU的坐标是否正确
- 指针设置是否正确
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, 12, BUFFER_OFFSET(0))
(检查所有参数)
- 属性是否启用
glEnableVertexAttribArray(GLKVertexAttribTexCoord0)
- 属性是否绑定到正在编译的着色器。
在你的情况下,你忘记绑定纹理坐标属性,这是很容易错过的。
根据您提供给我们的信息,不可能找到这个错误,但您应该注意我为您提供的程序,以查明问题的实际所在。说不定以后用得上。
所以我目前正在使用 iOS 学习 OpenGL ES 2.0 并从事迷宫游戏。迷宫是随机生成的(所以不是加载模型),我的努力是在迷宫的墙壁和地板上做纹理。我的方法是将迷宫视为一系列立方体,并且我有单独绘制立方体各个面的代码(因此我可以通过简单地留下一些面来创建路径)。
使用捕获 GPU 帧,我确认纹理确实正确加载,帧缓冲区中的数据正确,我没有收到任何错误。我可以看到我的其他灯光效果(所以脸不是完全黑的),但没有出现纹理。
这是我定义立方体面的方式
GLfloat rightCubeVertexData[] =
{
0.5f, -0.5f, -0.5f,
0.5f, -0.5f, 0.5f,
0.5f, 0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
0.5f, -0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
};
GLfloat rightCubeNormalData[] =
{
-1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
};
GLfloat rightCubeTexCoords[] =
{
0.0, 0.0,
1.0, 0.0,
0.0, 1.0,
0.0, 1.0,
1.0, 0.0,
1.0, 1.0,
};
其他面基本上以相同的方式定义(除了它们分别在一个数组中,拆分位置、法线和 tex 坐标只是我尝试过的方法;我只是想让一张脸变成纹理然后我将扩展到其余部分)。
下面是我如何将数据加载到缓冲区
glGenVertexArraysOES(1, &_rightVertexArray);
glBindVertexArrayOES(_rightVertexArray);
glGenBuffers(3, _rightVertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, _rightVertexBuffer[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(rightCubeVertexData), rightCubeVertexData, GL_STATIC_DRAW);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 12, BUFFER_OFFSET(0));
glBindBuffer(GL_ARRAY_BUFFER, _rightVertexBuffer[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(rightCubeNormalData), rightCubeNormalData, GL_STATIC_DRAW);
glEnableVertexAttribArray(GLKVertexAttribNormal);
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 12, BUFFER_OFFSET(0));
glBindBuffer(GL_ARRAY_BUFFER, _rightVertexBuffer[2]);
glBufferData(GL_ARRAY_BUFFER, sizeof(rightCubeTexCoords), rightCubeTexCoords, GL_STATIC_DRAW);
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, 12, BUFFER_OFFSET(0));
同样,使用三个缓冲区是一项实验,其余的都在一个缓冲区中定义,并带有偏移量。
这是我加载纹理的方式
crateTexture = [self setupTexture:@"crate.jpg"];
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, crateTexture);
glUniform1i(uniforms[UNIFORM_TEXTURE], 0);
// Load in and set up texture image (adapted from Ray Wenderlich)
- (GLuint)setupTexture:(NSString *)fileName
{
CGImageRef spriteImage = [UIImage imageNamed:fileName].CGImage;
if (!spriteImage) {
NSLog(@"Failed to load image %@", fileName);
exit(1);
}
size_t width = CGImageGetWidth(spriteImage);
size_t height = CGImageGetHeight(spriteImage);
GLubyte *spriteData = (GLubyte *) calloc(width*height*4, sizeof(GLubyte));
CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width*4, CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), spriteImage);
CGContextRelease(spriteContext);
GLuint texName;
glGenTextures(1, &texName);
glBindTexture(GL_TEXTURE_2D, texName);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
free(spriteData);
return texName;
}
然后,在适当的时候,我简单地调用 glDrawArrays 来绘制脸部。我对此完全感到困惑,这可能是一个非常愚蠢的错误,但是任何人都可以提供任何帮助将不胜感激。
P.S。这是我的片段着色器
varying vec3 eyeNormal;
varying vec4 eyePos;
varying vec2 texCoordOut;
uniform sampler2D texture;
uniform vec3 flashlightPosition;
uniform vec3 diffuseLightPosition;
uniform vec4 diffuseComponent;
uniform float shininess;
uniform vec4 specularComponent;
uniform vec4 ambientComponent;
void main()
{
vec4 ambient = ambientComponent;
vec3 N = normalize(eyeNormal);
float nDotVP = max(0.0, dot(N, normalize(diffuseLightPosition)));
vec4 diffuse = diffuseComponent * nDotVP;
vec3 E = normalize(-eyePos.xyz);
vec3 L = normalize(flashlightPosition - eyePos.xyz);
vec3 H = normalize(L+E);
float Ks = pow(max(dot(N, H), 0.0), shininess);
vec4 specular = Ks*specularComponent;
if( dot(L, N) < 0.0 ) {
specular = vec4(0.0, 0.0, 0.0, 1.0);
}
gl_FragColor = (ambient + diffuse + specular) * texture2D(texture, texCoordOut);
//gl_FragColor = ambient + diffuse + specular;
gl_FragColor.a = 1.0;
}
是的,所有统一名称都是正确的,并且与主代码中的某些内容相对应。
编辑:这是顶点着色器
precision mediump float;
attribute vec4 position;
attribute vec3 normal;
attribute vec2 texCoordIn;
varying vec3 eyeNormal;
varying vec4 eyePos;
varying vec2 texCoordOut;
uniform mat4 modelViewProjectionMatrix;
uniform mat4 modelViewMatrix;
uniform mat3 normalMatrix;
void main()
{
eyeNormal = (normalMatrix * normal);
eyePos = modelViewMatrix * position;
texCoordOut = texCoordIn;
gl_Position = modelViewProjectionMatrix * position;
}
原来我在编译shader的时候忘了绑定属性location。需要添加行
glBindAttribLocation(_program, GLKVertexAttribTexCoord0, "texCoordIn");
加载着色器方法。
根据评论总结完成的过程...
在处理纹理时可能会出错,知道如何查明问题出在哪里是很好的。
纹理本身要注意什么:
检查是否设置了
等参数glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- 检查将制服设置为
glUniform1i(uniformName, 0)
,其中最后一个参数对应于活动纹理而不是纹理 ID。 - 其他包括检查统一名称是否正确,是否绑定了纹理。如果可能的话,检查调试器是否正确加载了纹理。
除此之外,您的纹理坐标可能会被弄乱,这似乎是一个非常普遍的问题。要进行调试,最好将片段着色器中从纹理获取的颜色替换为纹理坐标本身。例如。将 texture2D(texture, texCoordOut)
替换为 vec4(texCoordOut.x, texCoordOut.y, .0, 1.0)
。由于纹理坐标应在 [0,1] 范围内,您应该会在场景中看到红色和绿色之间的漂亮渐变。如果你没有看到它们,那么你的纹理坐标就乱七八糟了:如果所有的东西都是黑色的,那么你的坐标都是零,如果大部分是黄色的,那么你的坐标很可能太大了。
在你的例子中,纹理坐标都是黑色的,这意味着你总是从纹理中获取第一个像素,因此场景中的颜色是恒定的。此时要检查的是:
- 你推送给GPU的坐标是否正确
- 指针设置是否正确
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, 12, BUFFER_OFFSET(0))
(检查所有参数) - 属性是否启用
glEnableVertexAttribArray(GLKVertexAttribTexCoord0)
- 属性是否绑定到正在编译的着色器。
在你的情况下,你忘记绑定纹理坐标属性,这是很容易错过的。
根据您提供给我们的信息,不可能找到这个错误,但您应该注意我为您提供的程序,以查明问题的实际所在。说不定以后用得上。