【发布时间】:2014-12-15 17:32:35
【问题描述】:
猜猜这个,
我最近发布了一款适用于 Android 的游戏(play.google.com/store/apps/details?id=com.quackers,如果您想亲眼目睹这些问题),最初的反馈表明该游戏无法运行在某些设备上正确。从那以后,我得到了其中一款有问题的平板电脑(Samsung Galaxy Tab 2 7.0),结果发现它确实可以运行,只是渲染不正确。
经过几次挖掘,我发现这是一个纹理问题。纹理加载正常,但它们没有被渲染 - 不是在出现问题时你经常使用 OpenGL 得到的通常的黑色方块 - 什么都没有。
这是 OpenGL ES 2.0,做 SDL/C++/ndk 的事情。虽然网上也有类似的问题,但其中大部分涉及 ES 1.0 并考虑了不同的问题 - 纹理尺寸不是 2 的幂(例如 64x64、128x128、256x256 等)或一些不适用的古怪压缩内容。
我已经剥离了所有的渲染代码,回到了基础——渲染一个带纹理的正方形(以一种没有特别优化的方式)。
预循环代码:
SDL_Init(SDL_INIT_VIDEO);
SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
SDL_DisplayMode mode;
SDL_GetDisplayMode(0,0, &mode);
_currentWidth = mode.w;
_currentHeight = mode.h;
SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
_screen = SDL_CreateWindow("window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, _currentWidth, _currentHeight, SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_RESIZABLE);
SDL_GLContext context = SDL_GL_CreateContext(_screen);
SDL_GL_MakeCurrent(_screen, context);
glViewport(0, 0, _currentWidth, _currentHeight);
//---
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
const char *vs_source = "attribute highp vec2 coord2d; "
"attribute highp vec2 texcoord;"
"varying highp vec2 f_texcoord;"
"void main(void) { "
"gl_Position = vec4(coord2d, 0.0, 1.0); "
"f_texcoord = texcoord;"
"}";
glShaderSource(vs, 1, &vs_source, NULL);
glCompileShader(vs);
GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
const char *fs_source = "varying highp vec2 f_texcoord;"
"uniform sampler2D texture;"
"void main(void) { "
"vec2 flipped_texcoord = vec2(f_texcoord.x, 1.0 - f_texcoord.y);"
"gl_FragColor = texture2D(texture, flipped_texcoord);"
"}";
glShaderSource(fs, 1, &fs_source, NULL);
glCompileShader(fs);
_program = glCreateProgram();
glAttachShader(_program, vs);
glAttachShader(_program, fs);
glLinkProgram(_program);
//---
GLuint vs2 = glCreateShader(GL_VERTEX_SHADER);
const char *vs_source2 = "attribute vec2 coord2d; "
"void main(void) { "
"gl_Position = vec4(coord2d, 0.0, 1.0); "
"}";
glShaderSource(vs2, 1, &vs_source2, NULL);
glCompileShader(vs2);
GLuint fs2 = glCreateShader(GL_FRAGMENT_SHADER);
const char *fs_source2 = "uniform lowp vec4 u_colour;"
"void main(void) { "
"gl_FragColor = u_colour;"
"}";
glShaderSource(fs2, 1, &fs_source2, NULL);
glCompileShader(fs2);
_flatProgram = glCreateProgram();
glAttachShader(_flatProgram, vs2);
glAttachShader(_flatProgram, fs2);
glLinkProgram(_flatProgram);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
//---------------------------------------
_screenRect.x = -1.0;
_screenRect.y = -1.0;
_screenRect.w = 2.0;
_screenRect.h = 2.0;
_superDuperFrameBuffer = 0;
_depthRenderBuffer = 0;
glGenTextures(1, &_screenTexture);
glBindTexture(GL_TEXTURE_2D, _screenTexture);
if(_currentWidth < SCREENWIDTH*2 || _currentHeight < SCREENHEIGHT*2) {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
else {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, SCREENWIDTH, SCREENHEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glGenRenderbuffers(1, &_depthRenderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _depthRenderBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, SCREENWIDTH, SCREENHEIGHT);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// create a framebuffer object
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glGenFramebuffers(1, &_superDuperFrameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, _superDuperFrameBuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _screenTexture, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthRenderBuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _depthRenderBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
_defaultFrameBuffer = 0;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &_defaultFrameBuffer);
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
glEnable(GL_BLEND);
glEnable(GL_TEXTURE_2D);
SDL_Surface* testSurface = IMG_Load("graphics/bg_01_0.png");
uint32_t rmask;
uint32_t gmask;
uint32_t bmask;
uint32_t amask;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
rmask = 0xff000000;
gmask = 0x00ff0000;
bmask = 0x0000ff00;
amask = 0x000000ff;
#else
rmask = 0x000000ff;
gmask = 0x0000ff00;
bmask = 0x00ff0000;
amask = 0xff000000;
#endif
SDL_Surface *tempSurface = SDL_CreateRGBSurface(0, testSurface->w, testSurface->h, 32, rmask, gmask, bmask, amask);
SDL_SetSurfaceBlendMode(tempSurface, SDL_BLENDMODE_BLEND);
SDL_BlitSurface(testSurface, NULL, tempSurface, NULL);
testSurface = tempSurface;
SDL_FreeSurface(tempSurface);
GLint uniformTexture = glGetUniformLocation(_program, "texture");
_testTexture = 0;
glGenTextures(1, &_testTexture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _testTexture);
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_CLAMP_TO_EDGE);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glUniform1i(uniformTexture, /*GL_TEXTURE*/0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, testSurface->pixels);
_vboTest = 0;
_vbo_cube_texcoords = 0;
glGenBuffers(1, &_vboTest);
glGenBuffers(1, &_vbo_cube_texcoords);
循环:
...
_quadColour[0] = 0.0f;
_quadColour[1] = 255.0f;
_quadColour[2] = 0.0f;
_quadColour[3] = 1.0f;
drawSquare(0, 0, 20, 20);
glViewport(0, 0, SCREENWIDTH, SCREENHEIGHT);
GLfloat x1 = 0, x2 = 8, y1 = 0, y2 = 8;
glUseProgram(_program);
GLint attributeCoord2d = glGetAttribLocation(_program, "coord2d");
GLint attributeTexcoord = glGetAttribLocation(_program, "texcoord");
glEnableVertexAttribArray(attributeTexcoord);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _testTexture);
GLfloat cube_texcoords[] = {
0.0, 1.0,
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
};
glBindBuffer(GL_ARRAY_BUFFER, _vbo_cube_texcoords);
glVertexAttribPointer(attributeTexcoord, 2, GL_FLOAT, GL_FALSE, 0, 0);
glBufferData(GL_ARRAY_BUFFER, sizeof(cube_texcoords), cube_texcoords, GL_STATIC_DRAW);
glEnableVertexAttribArray(attributeCoord2d);
GLfloat triangle_vertices[] = {
x1, y2,
x1, y1,
x2, y1,
x2, y2,
};
glBindBuffer(GL_ARRAY_BUFFER, _vboTest);
glVertexAttribPointer(attributeCoord2d, 2, GL_FLOAT, GL_FALSE, 0, 0);
glBufferData(GL_ARRAY_BUFFER, sizeof(triangle_vertices), triangle_vertices, GL_STATIC_DRAW);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
SDL_GL_SwapWindow(_screen);
...
显然,我添加了一些东西只是为了测试目的,比如将纹理转换为 RGBA 等等。它会绘制一个绿色的小方块,然后是一个带纹理的方块。
代码可能很乱,但重点是 - 两个不同的结果:
Galaxy Tab 2 7.0 (bork) http://i.imgur.com/ht6LvFV.png
Nexus 7(正确) http://i.imgur.com/p4acmIq.png
我该如何解决这个问题?
【问题讨论】:
-
您是否在其他设备上尝试过由于 GPU 架构而获得了类似的结果? (不知道这是由于立即渲染还是延迟渲染)
-
我不够富有,买不起无数的设备 :P 但到目前为止,它在其他所有方面都有效。我相信较旧的 Kindle HDXes 可能存在类似的故障 - 亚马逊只是报告“黑屏”。我还被告知它在 2012 Nexus 7 上失败了(但不是我拥有的 2013 年)。在几款 HTC 设备上取得了成功,所以它非常成功。
-
在 Opengl ES 2 上仍然需要 2 纹理的幂。非 2 纹理的幂可能在某些设备上工作。
-
@RichardCritten ES 2.0 支持 NPOT 纹理,但有一些限制(没有 mipmapping,只有
CLAMP_TO_EDGE环绕模式)。
标签: android opengl-es android-ndk opengl-es-2.0