我正在改变其他人的代码。他们使用通过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();
我已经遵循了这个:[如何管理内存与纹理在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);
答案 0 :(得分:2)
确保设置纹理采样模式。特别是最小过滤器: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)。
这是一个非常常见的错误,人们在被它咬了几次后往往会知道。
没有简单的方法来调试这样的东西。例如xcode中有很好的gl调试工具,但他们不会告诉你这个案例。
答案 1 :(得分: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内存信息的免费软件。
[编辑]: 我还建议使用opensource lib DevIL - 它非常适合加载各种图像格式。
答案 2 :(得分:1)
可能导致问题的一些想法:
glDeleteTextures
,如果在错误的时间调用,这可能会导致一些问题。答案 3 :(得分:1)
感谢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缓冲区检查会出错。正确的做法是不要将缓冲区大小加倍;在执行buffer.position(buffer.capacity())
之前使用flip()
填充缓冲区。
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
之前确保已正确绘制一个纹理是件好事。