【问题标题】:How to DEBUG OpenGL a gray/black texture box?如何调试 OpenGL 灰色/黑色纹理框?
【发布时间】:2015-05-22 06:22:31
【问题描述】:

我正在修改别人的代码。他们使用通过 BufferedImage 加载的 PNG。我需要加载一个 TGA,它只是一个 18 字节的标头和 BGR 代码。我已加载并运行纹理,但我得到一个灰色框而不是纹理。我什至不知道如何调试这个。

纹理被加载到一个 ByteBuffer 中:

final static int datasize = (WIDTH*HEIGHT*3) *2; // Double buffer size for OpenGL // not +18 no header
static ByteBuffer buffer = ByteBuffer.allocateDirect(datasize);

FileInputStream fin = new FileInputStream("/Volumes/RAMDisk/shot00021.tga");
FileChannel inc = fin.getChannel();

inc.position(18); // skip header

buffer.clear(); // prepare for read
int ret = inc.read(buffer);
fin.close();

我遵循了这个:[how-to-manage-memory-with-texture-in-opengl][1] ...因为我每帧更新一次纹理,就像视频一样。

调用一次:

GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureID);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);

GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB, width, height, 0, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null);
assert(GL11.GL_NO_ERROR == GL11.glGetError());

反复调用:

GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureID);
GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, width, height, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, byteBuffer);
assert(GL11.GL_NO_ERROR == GL11.glGetError());

return textureID;

渲染代码没有改变,基于:

GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, this.vertexCount);

【问题讨论】:

  • 为什么要丢弃标题?您应该阅读并处理其中的信息才能正确读取图像。
  • 我可以控制写入和读取 TGA。所以,我可以硬编码我喜欢的任何标题。
  • 你是否在填充数据后回退缓冲区?

标签: debugging opengl textures java-6 tga


【解决方案1】:

确保您设置了纹理采样模式。特别是最小过滤器:glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)。默认设置是 mip 映射 (GL_NEAREST_MIPMAP_LINEAR),因此除非您上传 mip 映射,否则您将获得白色读取结果。

所以要么将纹理设置为无 mip,要么生成它们。一种方法是在 tex img 调用之后调用 glGenerateMipmap。

(请参阅https://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexParameter.xml)。

这是一个非常常见的 gl 陷阱,人们在被它咬过几次后就会知道这一点。

没有简单的方法来调试这样的东西。例如 xcode 中有很好的 gl 调试工具,但他们不会告诉你这个案例。

【讨论】:

  • 谢谢@starmole,我添加了你指定的电话。我之前尝试过一次,但没有帮助,所以我再次删除了它们。我更新了我最初问题的代码。现在,我有一个黑匣子。我将 AppCode 与 Java 一起使用,而不是 Xcode。
【解决方案2】:

调试 GPU 代码总是很麻烦。随着越来越多的公司发现 GPU 的强大功能,我会将我的钱押在该领域的重大行业进步上。直到那时;我将分享我的两个最好的 GPU 调试朋友:

1) 定义一个打印 OGL 错误的函数:

int printOglError(const char *file, int line)
{
    /* Returns 1 if an OpenGL error occurred, 0 otherwise. */
    GLenum glErr;
    int    retCode = 0;

    glErr = glGetError();
    while (glErr != GL_NO_ERROR) {
        printf("glError in file %s @ line %d: %s\n", file, line, gluErrorString(glErr));
        retCode = 1;
        glErr = glGetError();
    }
    return retCode;
}

#define printOpenGLError() printOglError(__FILE__, __LINE__)

并在您的渲染绘制调用之后调用它(可能还会出现更早的错误):

GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, this.vertexCount);
printOpenGLError();

如果您进行了一些无效操作(可能只是您的情况),这会发出警报,但您通常必须通过反复试验找到错误发生的位置。

2) 查看gDEBugger,免费软件包含大量 GPU 内存信息。

[编辑]: 我还建议使用开源库 DevIL - 它非常适合加载各种图像格式。

【讨论】:

  • Antiiee,谢谢你的回答,但我没有任何错误。我只是有一个黑色的纹理。我会尝试你的陈述。 =)
【解决方案3】:

感谢 Felix,通过不调用 glTexSubImage2D(使内存保持有效,但未初始化),我注意到默认内存留下了残余模式。这表明纹理正在显示,但负载很可能是问题所在。

**更新:

上面代码的问题本质上是缓冲区。缓冲区为 1024*1024,但它仅被读取部分填充,将 ByteBuffer 的 limit 标记留在 2359296(1024*768*3) 而不是 3145728(1024*1024*3)。这给出了错误:

Number of remaining buffer elements is must be ... at least ...

我认为 OpenGL 需要空间来返回数据,所以我将缓冲区的大小增加了一倍。 缓冲区大小加倍以补偿错误。

final static int datasize = (WIDTH*HEIGHT*3) *2; // Double buffer size for OpenGL // not +18 no header

这是错误的,需要 flip() 函数(非常感谢 Reto Koradi 对缓冲区倒带的小提示)将 ByteBuffer 置于读取模式。由于缓冲区只是半满的,因此 OpenGL 缓冲区检查会出错。正确的做法是不要将缓冲区大小加倍;在执行flip() 之前使用buffer.position(buffer.capacity()) 填充缓冲区。

final static int datasize = (WIDTH*HEIGHT*3); // not +18 no header

buffer.clear(); // prepare for read
int ret = inc.read(buffer);
fin.close();
buffer.position(buffer.capacity()); // make sure buffer is completely FILLED!
buffer.flip(); // flip buffer to read mode

要解决这个问题,对缓冲区的内存进行硬编码以确保 OpenGL 调用正常工作并隔离负载问题会很有帮助。然后当 OpenGL 调用正确时,专注于缓冲区的加载。正如 Felix K 所建议的,在重复调用glTexSubImage2D 之前,最好确保已正确绘制了一个纹理。

【讨论】:

  • 通过手动写入缓冲区来了解可能的不同类型的测试会很有趣。整个缓冲区中只有 R、G 和 B 是显而易见的,但还有其他可以测试其他情况的吗?
【解决方案4】:

可能导致问题的一些想法:

  • 您的纹理位于某处。我不知道整个代码,但我猜某处有一个 glDeleteTextures,如果在错误的时间调用这可能会导致一些问题。
  • 纹理宽度和高度是二的幂吗?如果不是,这可能是一个问题,具体取决于您的硬件。旧硬件有时不支持non-power of two images
  • 纹理参数在其他点的绘制调用之间发生了变化(使用glGetTexParameter 对参数进行调试检查)。
  • 加载下一张图片(edit: 甚至是第一张图片)时可能会出现加载问题。检查是否显示第一张图像而不加载下一张图像。如果是,则必须是上述情况之一。

【讨论】:

  • 菲利克斯,感谢您的回答。实际上我根本没有 glDeleteTextures 。我覆盖纹理内存并调用 glTexSubImage2D 以使用下一个图像(视频)更新纹理。是的,纹理是二的幂:1024^2。明天我会检查最后两点。
  • @PLG 哦,好吧,我想也许它发生在其他代码的某个点上。但是我看到您使用的是 OpenGL 1.1,这意味着没有使用着色器。所以我猜你根本没有使用顶点缓冲区?您是否正在使用诸如“glTexCoord”和glVertex 之类的已弃用功能?我猜你在那里没有改变任何东西? (错误的设置也可能导致错误)。另一个错误来源:您是否在渲染之前绑定了纹理?如果您更改现有代码,则纹理可能没有绑定,只会导致黑色纹理。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-06-28
  • 2012-02-07
  • 1970-01-01
  • 2014-11-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多