调整缓冲区大小时 OpenGL 失败
Posted
技术标签:
【中文标题】调整缓冲区大小时 OpenGL 失败【英文标题】:OpenGL fails when resizing buffer 【发布时间】:2014-08-11 20:02:13 【问题描述】:我需要每帧更新一个像素数组到屏幕上。它最初可以工作,但是当我尝试调整屏幕大小时,它会出现故障并最终抛出EXC_BAD_ACCESS 1
。我已经在每一帧之前检查了缓冲区是否分配到正确的大小,但是它似乎不会影响结果。
#include <stdio.h>
#include <stdlib.h>
#include <GLUT/GLUT.h>
unsigned char *buffer = NULL;
int width = 400, height = 400;
unsigned int screenTexture;
void Display()
for (int y = 0; y < height; y+=4)
for (int x = 0; x < width; x++)
buffer[(x + y * width) * 3] = 255;
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_TEXTURE_2D);
// This function results in EXC_BAD_ACCESS 1, although the buffer is always correctly allocated
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer);
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, width, height, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glBegin (GL_QUADS);
glTexCoord2f(0,0); glVertex2i(0, 0);
glTexCoord2f(1,0); glVertex2i(width,0);
glTexCoord2f(1,1); glVertex2i(width,height);
glTexCoord2f(0,1); glVertex2i(0, height);
glEnd ();
glFlush();
glutPostRedisplay();
void Resize(int w, int h)
width = w;
height = h;
buffer = (unsigned char *)realloc(buffer, sizeof(unsigned char) * width * height * 3);
if (!buffer)
printf("Error Reallocating buffer\n");
exit(1);
int main(int argc, char **argv)
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
glutInitWindowSize(width, height);
glutCreateWindow("Rasterizer");
glutDisplayFunc(Display);
glutReshapeFunc(Resize);
glGenTextures(1, &screenTexture);
glBindTexture(GL_TEXTURE_2D, screenTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glDisable(GL_DEPTH_TEST);
buffer = (unsigned char *)malloc(sizeof(unsigned char) * width * height * 3);
glutMainLoop();
调整屏幕大小后也无法正常显示:
是什么导致了这个问题?代码编译运行,你只需要链接 GLUT 和 OpenGL。
【问题讨论】:
【参考方案1】:正如@genpfault 提到的,OpenGL 每像素读取 4 个字节,而不是您假设的 3 个。
除了更改GL_UNPACK_ALIGNMENT
,您还可以通过简单的struct
将代码更改为每个像素4 字节的正确假设:
struct pixel
unsigned char r, g, b;
unsigned char unused;
;
然后,您可以使用更清晰的sizeof(struct pixel)
,而不是使用魔法常数 3。这使得阅读和传达代码的意图更容易,并且不会产生任何额外的代码(因为该结构“有效地”是一个 4 字节的数组)。
【讨论】:
我尝试将解包对齐设置为 1,所以没关系,但我也会尝试【参考方案2】:glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer);
^^^^^^
GL_UNPACK_ALIGNMENT
默认为4
,而不是1
。因此 OpenGL 将为每个像素读取 4 个字节,而不是您假设的 3 个字节。
使用glPixelStorei()
将GL_UNPACK_ALIGNMENT
设置为1
。
【讨论】:
GL_UNPACK_ALIGNMENT
是每行对齐,而不是每像素对齐,不是吗?
设置解包对齐无济于事,但将数据对齐到 4 个字节有效
@StasJaro - 仅供参考,有时您可以查看图像并立即判断一般问题是什么。您拥有的图像具有“楼梯”效果——一旦您看到它,几乎可以保证图像没有对齐。【参考方案3】:
听起来您找到了可行的方法,但我认为问题没有得到正确诊断。我认为最大的问题在于您在此处初始化纹理数据的方式:
for (int y = 0; y < height; y+=4)
for (int x = 0; x < width; x++)
buffer[(x + y * width) * 3] = 255;
这只会在每 4 行设置数据,然后只为这些行中的每 3 个字节设置数据。要将所有数据初始化为白色,您需要将行号 (y
) 增加 1 而不是 4,并在循环内设置所有 3 个组件:
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
buffer[(x + y * width) * 3 ] = 255;
buffer[(x + y * width) * 3 + 1] = 255;
buffer[(x + y * width) * 3 + 2] = 255;
您还需要将GL_UNPACK_ALIGNMENT
设置为1:
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
这控制行对齐(不是像素对齐,如其他几个答案中所建议的那样)。 GL_UNPACK_ALIGNMENT
的默认值为 4。但是在您使用的 GL_RGB
格式中每个像素 3 个字节,如果像素数是 4 的倍数,则行的大小仅为 4 个字节的倍数。所以对于 3 字节/像素的紧凑行,该值需要设置为 1。
【讨论】:
实际上我已经写了一个软件 3d rasterizer 但是在这里发布整个东西没有任何意义,所以纹理数据只是加载一个 RED (byte1[r],byte2[g],byte3 [b]) 为简单起见的条纹图案。 (在调整大小时用 memset 0 清除)以上是关于调整缓冲区大小时 OpenGL 失败的主要内容,如果未能解决你的问题,请参考以下文章
OpenGL如何在模板测试失败且深度测试成功时写入模板缓冲区?