Android OpenGL ES 2.0 - 某些设备上没有显示纹理

时间:2014-12-15 17:32:36

标签: android opengl-es android-ndk opengl-es-2.0

谜我这个,

我最近发布了一款适用于Android的游戏(play.google.com/store/apps/details?id=com.quackers,如果您想亲眼目睹这些问题),并且初步反馈表明该事情并非如此。在某些设备上正常运行。自从得到其中一个有问题的平板电脑(三星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

我该如何解决这个问题?

2 个答案:

答案 0 :(得分:1)

许多GLES 2.0设备仍然需要两个Power-of纹理。从GLES 2.0规范的第3.8.2节(https://www.khronos.org/registry/gles/specs/2.0/es_full_spec_2.0.25.pdf):

&#34;从片段着色器调用采样器将返回(R; G; B; A)= (0; 0; 0; 1)如果满足以下任一条件:...调用二维采样器,相应的纹理图像是非二次幂图像(如Mipmapping讨论中所述)部分3.7.7),纹理包装模式不是CLAMP_TO_EDGE,或者缩小过滤器既不是NEAREST也不是LINEAR。&#34;

假设SCREENHEIGHT / SCREENWIDTH是您设备的尺寸,您违反了此限制。如果您的设备支持某些NPOT扩展,例如GL_OES_texture_npot(https://www.khronos.org/registry/gles/extensions/OES/OES_texture_npot.txt),您可以忽略此限制,但根据我的经验,当npot纹理是a的颜色目标时,报告此扩展的某些设备仍会将纹理采样为黑色。帧缓冲区。最好的方法是始终在ES 2.0中使用POT渲染目标。

答案 1 :(得分:0)

此代码中存在一些错误和可能的问题:

  • 如果GLSL编译器严格检查错误,则不应编译片段着色器。由于未指定默认精度,并且float / vector / matrix类型没有默认精度,因此所有声明都需要显式精度。这个变量缺少:

    vec2 flipped_texcoord = vec2(f_texcoord.x, 1.0 - f_texcoord.y);
    

    如果你想坚持highp,那应该是:

    highp vec2 flipped_texcoord = vec2(f_texcoord.x, 1.0 - f_texcoord.y);
    
  • 此调用有一个错误的参数:

    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _screenTexture, 0);
    

    由于你要附加一个纹理,第三个参数必须是GL_TEXTURE_2D(你需要使用glFramebufferRenderbuffer来附加一个渲染缓冲区):

    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, screenTexture, 0);
    
  • 确保设备支持OES_packed_depth_stencil扩展,因为您在此处使用它:

    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, SCREENWIDTH, SCREENHEIGHT);
    
  • 此代码序列没有多大意义:

    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    
    _defaultFrameBuffer = 0;
    glGetIntegerv(GL_FRAMEBUFFER_BINDING, &_defaultFrameBuffer);
    

    你刚绑定帧缓冲区0,所以当前的帧缓冲区绑定在这里总是0。如果您担心默认的帧缓冲区可能不是0,则必须在第一次更改绑定之前查询该值。

  • 这不是ES 2.0中的有效调用:

    glEnable(GL_TEXTURE_2D);
    

    仅在固定功能OpenGL中需要启用纹理。一旦你使用着色器,它只会在着色器使用纹理时使用纹理。无需明确启用任何内容。