如何使用 OpenGL 加载 8 位 bmp?

Posted

技术标签:

【中文标题】如何使用 OpenGL 加载 8 位 bmp?【英文标题】:How can I load 8 bit bmp with OpenGL? 【发布时间】:2012-08-16 04:34:18 【问题描述】:

这是我的情况:我需要预加载 2000 张图像并按顺序显示它们以成为 60 fps 的动画。目前,我正在使用 OpenGL 加载 bmp 文件,但由于内存限制,我最多只能预加载 500 多个图像。我怎么解决这个问题?到目前为止,我可以提出两个方向的解决方案:首先,也许我可以加载 8 位 bmp 图像以节省内存。但是我在使用 glDrawPixels 时遇到了困难。其次,如果可能的话,我可以直接加载 jpeg 吗?感谢您的建议!

不使用视频的原因是我需要通过跳过一个或多个图像来更改动画速度,如您在代码中看到的(imgCount+=stp;//stp表示要转义多少图像。它可以制作视频快点)。在我的动画中,帧率很重要,低于 50 的 FPS 会显示闪烁。

代码如下:

void Frame::LoadBMP(void)

 FILE *in;


 in=fopen(file,"rb");//open file 
 if(in==NULL)

     exit(0);
 
 fread(&(this->bmfh),sizeof(BITMAPFILEHEADER),1,in);//read bmp file header
 fread(&(this->bmih),sizeof(BITMAPINFOHEADER),1,in);//read bmp infomation header

 colours=new RGBQUAD[bmih.biBitCount];
 fread(colours,sizeof(RGBQUAD),bmih.biBitCount,in);//read bmp colour table

 size=bmfh.bfSize-bmfh.bfOffBits;
 tempPixelData=new GLubyte[size];

 if(tempPixelData==NULL) 
     fclose(in);
 
 fread(tempPixelData,sizeof(GLubyte),size,in);//read bmp image data
 fclose(in);

我会显示图像序列,显示代码:

void display(void)

static clock_t start=clock();
static clock_t end=clock();

CurrtempPixelData=msFrame[offset]->tempPixelData;

glEnable(GL_ALPHA_TEST);
glEnable(GL_BLEND);

glDrawPixels(frWidth, frHeight, GL_RGBA, GL_UNSIGNED_BYTE, msFrame[offset]->tempPixelData);

for(int i=0;i<m;i++)
    clock_t c=clock();


glutSwapBuffers();
imgCount+=stp; // stp means how many images to escape. it can make video faster.
offset=imgCount%numFrame;
glutPostRedisplay();


【问题讨论】:

你一开始就不能用 OpenGL 加载位图,所以我猜 JPEG 不会更容易。但请考虑使用压缩纹理,无论您如何加载实际图像数据。 【参考方案1】:

您不应该使用glDrawPixels,它已被弃用。最好的方法可能是绘制一个屏幕大小的四边形(-1,-1 => 1,1,没有任何矩阵变换),然后用这些图像纹理化。

对于纹理,您可以在glTexImage2D 和类似函数中指定几种内部格式。例如,您可以使用 GL_R3_G3_B2​ 格式来获得 8 位大小,但也可以使用压缩格式,如 S3TC。例如,您可以传递COMPRESSED_SRGB_S3TC_DXT1_EXT,这应该会将您的图像大小减少到每像素 4 位,可能比 8 位格式的质量更好。不能在 OpenGL 中使用 JPEG 作为压缩格式(太复杂了)。

最后,你为什么要通过 OpenGL 来做这件事?将图像传送到常规窗口可能会给您带来足够好的性能。然后,您甚至可以将图像序列存储为视频,然后对解码的帧进行 blit。在这种情况下,您不太可能会遇到内存问题。

【讨论】:

感谢 KillianDS 的有用建议。我稍后会尝试。而我使用 OpenGL 的原因只是为了方便。实际上,我最初使用 C# 来播放我的图像序列。但它有一个闪烁的问题,所以我改用 OpenGL。你能解释一下“将图像粘贴到常规窗口”是什么意思吗?如何做到这一点?这听起来像一个简单的解决方案!由于我是编程新手,感谢您的耐心等待! 在大多数窗口 API 中,您可以直接在窗口/屏幕上绘制一个矩形,这个一般过程称为 blitting(将一个矩形绘制到另一个矩形)。确切的接口取决于 API 到 API,例如在 SDL 中您将使用 SDL_BlitSurface【参考方案2】:

也许您根本不需要 2000 张图像并以 60fps 的速度显示它们?稳定的 25fps 足以应付任何电影。

我鼓励您重新考虑最初的问题并提出更合适的解决方案(视频、动画、矢量,也许还有其他东西)

至于原问题:

如果您只需要一次图像 - 在需要时将它们放入内存并在显示后立即丢弃。

使用 DXT 打包图像。随着质量的轻微下降,您将获得恒定的 x4/x8 压缩比。

如今,OpenGL 不太擅长使用调色板纹理(许多供应商的实现很差)。但是您可以使用着色器来实现它。

【讨论】:

谢谢你,克罗姆。你的建议非常鼓舞人心。你是对的,我可以使用视频。但就我而言,我需要在播放动画的过程中进行一些操作,特别是我需要改变动画的速度。总的来说,我有 7 种不同的速度条件。所以,一开始,我想将所有图像存储在一个数组中然后循环它可能更容易。因此我遇到了内存问题。

以上是关于如何使用 OpenGL 加载 8 位 bmp?的主要内容,如果未能解决你的问题,请参考以下文章

使用 SDL 加载 OpenGL 纹理

opengl打开本地bmp图片绘制

无法将 BMP 图像转换为矩形 OpenGL

如何让 GHCi 加载 Opengl 包?

如何纹理 Opengl glut 对象(C++)

加载 bmp 图像 OPENGL 3.x - 只加载一个像素?