【问题标题】:OpenGL - render directly to bitmapOpenGL - 直接渲染到位图
【发布时间】:2026-01-31 19:15:01
【问题描述】:

我正在制作一个应用程序,其中包含一堆小窗口和控件(2D 渲染),我想将每个窗口和控件渲染到它自己的位图上。这是到目前为止:

uses dglOpenGL;
...
var BMP: TBitmap;
    DC, RC: HDC;
...
function TMainForm.Init: Boolean;
begin
  Result := InitOpenGL;
  if Result = True then
  begin
    BMP := TBitmap.Create;
    BMP.PixelFormat := pf24bit;
    BMP.Width := 1280;
    BMP.Height := 1024;

    DC := (BMP.Canvas.Handle);
    RC := CreateRenderingContext(DC,
                                 [opGDI, opDoubleBuffered], // tried changing, didn't help
                                 24,
                                 24,
                                 0,
                                 0,
                                 0,
                                 0);
    ActivateRenderingContext(DC, RC);

    glClearColor(0.27, 0.4, 0.7, 0.0); // light blue
    glViewport(0, 0, 1280, 1024);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity;
    glOrtho(0, 1280, 0, 1024, -1, 10);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity;
  end;
end;

渲染过程:

  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);

  // red quad
  glColor3f(1, 0, 0);
  glBegin(GL_QUADS);
    glVertex2f(100, 100);
    glVertex2f(1280-100, 100);
    glVertex2f(1280-100, 1024-100);
    glVertex2f(100, 1024-100);
  glend;

  // swap
  SwapBuffers(DC);

但是没有输出。
如果我使用MainForm.Canvas.Draw(0, 0, BMP);,则会出现一个白色矩形。

我想在位图上进行渲染,因为我可以用位图做很多事情(绘制其他图像、绘制文本、模糊),但是如果有其他方法可以进行屏幕外渲染,那也没关系...

那么如何设置我的应用程序以进行离屏渲染?

【问题讨论】:

标签: windows delphi opengl rendering delphi-7


【解决方案1】:

OpenGL 上下文必须与目标设备上下文相匹配。对于窗口,它们的创建方式与位图不同。看 http://msdn.microsoft.com/en-us/library/windows/desktop/dd368826(v=vs.85).aspx 尤其是 dwFlags,其中有很多

PFD_DRAW_TO_WINDOW 缓冲区可以绘制到窗口或设备表面。

PFD_DRAW_TO_BITMAP 缓冲区可以绘制到内存位图。

但是,您应该尽快为您的 DIB DC 创建渲染上下文。为什么?因为它会非常慢,因为 DIB 部分上的 OpenGL 渲染上下文将使用仅支持在 CPU 上运行的 OpenGL-1.1 的软件光栅化器。

相反,您应该创建一个帧缓冲区对象,附加一个颜色渲染缓冲区附件,并在完成后将glReadPixels 放入您的 DIBSection。更容易,更快。

因评论请求而更新

(我不知道为什么 * 不能正确地进行语法着色,即弄清楚哪里是评论,哪里不是)

// flushes the OpenGL error queue and
// counts the total number of errors
int flushGLErrors(void)
{
    int i = 0;
    while( glGetError() != GL_NO_ERROR ) {
        i++;
    }

    return i;
}

// returns a HBITMAP or NULL.
// The HBITMAP must be freed using DeleteObject 
HBITMAP ReadPixelsToHBITMAP(
    int x,
    int y,
    int width,
    int height )
{
    void *pdata = NULL;

    /* Note that this values only makes sense if we know a target
     * output size like if we put the image to paper. */ 
    const int physical_resolution = 2835; /* 72 DPI */

    BITMAPINFOHEADER bmih = {
        /* .biSize          = */ sizeof(bmih),
        /* .biWidth         = */ width,
        /* .bi.Height       = */ height,
        /* .biPlanes        = */ 1,                   /* mandatory */
        /* .biBitCount      = */ 24,                  /* 8 bits per pixel */
        /* .biCompression   = */ BI_RGB,              /* uncompressed */
        /* .biSizeImage     = */ 0,                   /* implicit */
        /* .biXPelsPerMeter = */ physical_resolution, /* ignored */
        /* .biYPelsPerMeter = */ physical_resolution, /* ignored */
        /* .biClrUsed       = */ 0,                   /* no palette */
        /* .biClrImportant  = */ 0
    };

    HBITMAP hbm = CreateDIBSection(
        hdc, /* may be different than the DC used for OpenGL */
        (PBITMAPINFO)&bmih, /* can do this cast, because no palette is used */
        DIB_RGB_COLORS,
        &pdata,
        NULL,
        0
    );

    if( !hbm ) {
        return NULL;
    }

    flushGLErrors();

    glPixelStorei(GL_PACK_SWAP_BYTES,   GL_FALSE);
    glPixelStorei(GL_PACK_LSB_FIRST,    GL_TRUE);
    glPixelStorei(GL_PACK_ROW_LENGTH,   0);
    glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0);
    glPixelStorei(GL_PACK_SKIP_PIXELS,  0);
    glPixelStorei(GL_PACK_SKIP_ROWS,    0);
    glPixelStorei(GL_PACK_ALIGNMENT,    1);

    if( glGetError() != GL_NO_ERROR ) {
        DeleteObject(hbm);
        return NULL;
    }

    glReadPixels(x, y, width, height, GL_BGR, GL_UNSIGNED_BYTE, pdata);

    if( glGetError() != GL_NO_ERROR ) {
        DeleteObject(hbm);
        return NULL;
    }

    return hbm;
}

【讨论】:

  • 我是 Windows 新手。我使用什么代码将 glReadPixels 直接导入 DIB DC?
  • @thethuthinnang:查看我的代码更新。我只是破解了它,我没有测试它。如果它有效,那很好,否则它会给你一个很长的开始。
  • 谢谢,正是我需要的。