Delphi中的OpenGL:将图像离屏渲染到文件

3
我需要关于在Delphi XE2中保存OpenGL ViewPort渲染内容到位图文件的帮助。
基本上我想要做的事情是,在进行一些渲染后,将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”函数将FrameBuffer读取到处理器内存(我假设这是L2缓存),而不是主内存,而且处理器内存无法容纳整个图像。是否是这种情况?如果是,您有没有任何关于如何将FrameBuffer读取到主内存的想法?
2)为了避免1)中的错误,在较小的视口上进行测试(25×25),当我尝试访问“pixels”数组中存储的任何值时,会导致访问冲突错误。这意味着glReadPixels未正确从缓冲区读取,我认为原因是我传递给函数的参数存在一些不一致。glReadPixels(0,0,dimensions[2],dimensions[3],GL_RGB,GL_UNSIGNED_BYTE,@pixels)

你是如何创建OpenGL上下文的?使用隐藏窗口+帧缓冲对象或PBuffer? - datenwolf
我使用以下代码进行默认上下文初始化: DC := GetDC(panelHandle); RC := CreateRenderingContext(DC, [opDoubleBuffered], 32, 24, 0, 0, 0, 0); ActivateRenderingContext(DC, RC) - kenny
2个回答

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;

1
顺便说一下,使用参数GL_BGRA读取像素,这样您就不需要后来交换RGB位,从而节省CPU。 - Little Helper

0
第一点:您需要从流中读取而不是写入: MS.Read(pixels,dimensions[2] * dimensions[3] * 3) ; 第二点:像素是一个动态数组,本质上是指向该数组的指针。要获取指向第一个字节的指针,请在glReadPixels中使用@pixels[0],以便将数据写入内存流中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接