如何调试 OpenGL gray/black 纹理框?
How to DEBUG OpenGL a gray/black texture box?
我正在修改别人的代码。他们使用通过 BufferedImage 加载的 PNG。我需要加载一个 TGA,它只是一个 18 字节 header 和 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);
确保设置纹理采样模式。特别是最小过滤器: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 调试工具,但他们不会告诉你这种情况。
一些可能导致问题的想法:
- 你的纹理被放置在某处。我不知道整个代码,但我猜某处有一个
glDeleteTextures
如果在错误的时间调用这可能会导致一些问题。 - 纹理的宽度和高度是2的幂吗?如果不是,这可能是一个问题,具体取决于您的硬件。旧硬件有时不支持 non-power of two images.
- 纹理参数在其他某个点的绘制调用之间发生了变化(使用 glGetTexParameter 对参数进行调试检查)。
- 加载下一张图片(编辑:甚至第一张图片)时可能会出现加载问题。检查是否显示第一张图像而不加载下一张图像。如果是,那一定是上述情况之一。
调试 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 - 它非常适合加载各种图像格式。
感谢 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 需要 space 到 return 数据,所以我将缓冲区的大小加倍。 缓冲区大小加倍以补偿错误。
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
之前确保正确绘制了一个纹理是很好的。