2013-01-15 39 views
3

我需要一些帮助,关于在Delphi XE2中将OpenGL的ViewPort的渲染内容保存为位图文件。OpenGL与德尔福:离线渲染图像到文件

基本上,我想要做的事情是在完成一些渲染之后,将FrameBuffer的内容转储为位图(全彩色格式)文件。这是应该完成这个的代码摘录。

procedure TForm1.saveBtnClick(Sender: TObject); 
     var 
     //TBitmap object holding the newly created Bitmap. 
     srcBitmap: TBitmap; 
     // the array to hold pixels value while reading from the FrameBuffer 
     pixels: Array of GLUbyte; 
     dimensions: Array [0 .. 3] of Integer; 
     //Stream between the memory location of pixels and my bitmap. 
     MS: TMemoryStream; 
     I: Integer; 
     begin 
     if SaveDialog1.Execute then 
     begin 
     //create the bitmap and set it to Full Color Format; Open the Memory Stream 
     srcBitmap := TBitmap.Create; 
     srcBitmap.PixelFormat:=pf24bit; 
     MS:= TMemoryStream.Create; 
     //get the dimensions info for the current ViewPort 
     glGetIntegerv(GL_VIEWPORT, @dimensions); 
     srcBitmap.Width := dimensions[2]; 
     srcBitmap.Height :=dimensions[3]; 
     //allocate enough memory for pixels; 
     SetLength(pixels, dimensions[2] * dimensions[3] * 3); 

     //this is the function that is supposed to read the contents from the Frame 
     // Buffer and write them to pixels 
     glReadPixels(0, 0, dimensions[2], dimensions[3], GL_RGB, 
      GL_UNSIGNED_BYTE, @pixels); 

     //Do something if an error occured 
     ErrorHandler; 

     // Below I attempt to create a bitmap file from the read in pixels 
     MS.Read(pixels,dimensions[2] * dimensions[3] * 3) ; 
     srcBitmap.LoadFromStream(MS); 
     Edit2.Text := SaveDialog1.FileName; 
     srcBitmap.SaveToFile(Edit2.Text); 
     MS.Free; 
     srcBitmap.Free; 
     end; 
end; 

我遇到的主要问题是:

1)如果视口大小太大堆栈溢出错误(我得到这么错误试图保存的图像大小为256 * 256)。我认为这可能是因为'glReadPixels'函数将帧缓冲区读取到PROCESSOR MEMORY(我假设这是L2 Cache),而不是主存储器,并且这个不能适合整个图像。是这样吗?如果是这样,你有什么想法,我怎么读FrameBuffer到主存储器上?

2)为了避免1)中的错误,在较小的视口(25x25)上进行测试时,给我一个访问冲突错误,当我尝试访问存储在“像素”数组中的任何值时。这意味着glReadPixels不能正确地从缓冲区中读取数据,我相信其原因与我传递给函数glReadPixels(0, 0, dimensions[2], dimensions[3], GL_RGB,GL_UNSIGNED_BYTE, @pixels)的参数之间有些不一致。

+0

你是如何创建OpenGL上下文的?隐藏的窗口+ FBO或PBuffer? – datenwolf

+0

那么我做了默认的上下文初始化,使用 DC:= GetDC(panelHandle); RC:= CreateRenderingContext(DC,[opDoubleBuffered],32,24,0,0,0,0); ActivateRenderingContext(DC,RC) – kenny

回答

2
Procedure GetOGL_BMP(var BMP: TBitmap); 
var 
    Dimensions: array [0 .. 3] of Integer; 
    RGBBits: PRGBQuad; 
    Pixel: PRGBQuad; 
    Header: PBitmapInfo; 
    x, y: Integer; 
    Temp: Byte; 
begin 
    glGetIntegerv(GL_VIEWPORT, @Dimensions); 
    GetMem(RGBBits, Dimensions[2] * Dimensions[3] * 4); 
    glFinish; 
    glPixelStorei(GL_PACK_ALIGNMENT, 4); 
    glPixelStorei(GL_PACK_ROW_LENGTH, 0); 
    glPixelStorei(GL_PACK_SKIP_ROWS, 0); 
    glPixelStorei(GL_PACK_SKIP_PIXELS, 0); 
    glReadPixels(0, 0, Dimensions[2], Dimensions[3], GL_RGBA, GL_UNSIGNED_BYTE, 
    RGBBits); 
    if not Assigned(BMP) then 
     BMP := TBitmap.Create; 
    BMP.PixelFormat := pf32Bit; 
    BMP.Width := Dimensions[2]; 
    BMP.Height := Dimensions[3]; 
    GetMem(Header, SizeOf(TBitmapInfoHeader)); 
    with Header^.bmiHeader do 
    begin 
    biSize := SizeOf(TBitmapInfoHeader); 
    biWidth := Dimensions[2]; 
    biHeight := Dimensions[3]; 
    biPlanes := 1; 
    biBitCount := 32; 
    biCompression := BI_RGB; 
    biSizeImage := Dimensions[2] * Dimensions[3] * 4; 
    end; 
    // Rot und Blau vertauschen 
    Pixel := RGBBits; 
    for x := 0 to Dimensions[2] - 1 do 
    for y := 0 to Dimensions[3] - 1 do 
    begin 
     Temp := Pixel.rgbRed; 
     Pixel.rgbRed := Pixel.rgbBlue; 
     Pixel.rgbBlue := Temp; 
     inc(Pixel); 
    end; 
    SetDIBits(BMP.Canvas.Handle, BMP.Handle, 0, Dimensions[3], RGBBits, 
    TBitmapInfo(Header^), DIB_RGB_COLORS); 

    FreeMem(Header); 
    FreeMem(RGBBits); 
end; 
+2

完美工作。 – kenny

+0

@kenny你应该接受这个... –

+1

@Bummi顺便说一句,读参数为'GL_BGRA'的像素,所以你不需要以后交换RGB位,节省CPU。 –

0

首先: MS:你从流而不是写入读取。 阅读(像素,尺寸[2] *尺寸[3] * 3);

:像素是一个动态数组,这实质上是一个指针,它指向数组。要获得指向第一个字节的指针,请在glReadPixels中使用@pixels [0]并写入内存流。

+0

感谢您的提示! – kenny