调整缓冲区大小时 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如何在模板测试失败且深度测试成功时写入模板缓冲区?

如何定义 QT Opengl 帧缓冲区的大小

是否可以仅使用 OpenGL 确定默认帧缓冲区的大小?

实现 pyglet 会破坏我曾经工作的帧缓冲区 OpenGL 代码

OpenGL FBO 与 MRT 写入后台缓冲区

在C和/或C ++中创建和管理内存中的字节缓冲区,可以根据需要自动调整大小