2013-01-15 42 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