如何使用 OpenTK 捕获绘制的图像

3

我的目标是在GL_Control(一个GUI控件)上使用C#中的OpenTK绘制东西,并且每次调用paint事件时也将其保存到Bitmap对象中。我有以下代码:

private void glControl1_Paint(object sender, PaintEventArgs e)
{
  // do lots of drawing here
  GL.Finish();
  GL.Flush();
  glControl1.SwapBuffers();
  gl_image = TakeScreenshot();
}

public Bitmap TakeScreenshot()
{
    if (GraphicsContext.CurrentContext == null)
        throw new GraphicsContextMissingException();
    int w = glControl1.ClientSize.Width;
    int h = glControl1.ClientSize.Height;
    Bitmap bmp = new Bitmap(w, h);
    System.Drawing.Imaging.BitmapData data =
        bmp.LockBits(glControl1.ClientRectangle, System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
    GL.ReadPixels(0, 0, w, h, PixelFormat.Bgr, PixelType.UnsignedByte, data.Scan0);
    bmp.UnlockBits(data);

    bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
    return bmp;
}

在交换缓冲区后的绘图事件(paint event)中,我进行截屏。问题是所捕获的图像处于绘制之前的状态。这意味着如果我想捕获图像,我需要运行两次绘图事件。我尝试过GL.flush、finish和swapbuffer。有没有人知道如何解决这个问题。还要注意的是,我尝试使用异步方式,但失败了,因为不能从另一个线程访问opentk图像数据。
2个回答

2

我曾经遇到过完全相同的问题,这是我解决它的方法。 当你调用 glControl1.Invalidate() 来刷新图像时,OpenTK 实际上是在最后才执行。因此,如果你在那一帧内抓取屏幕截图,缓冲区不会在下一个循环之前更新。

你需要做的是强制 glControl1 刷新,这里是代码。

public void GLrefresh()
{
    glControl1.Invalidate();
    glControl1.Update();
    glControl1.Refresh();
}

在获取屏幕截图之前,请调用此函数。
GLrefresh();
Image I = TakeScreenshot();
//Now Image I should be the image of the current buffer

0
Chris,谢谢。你的想法很有效。我希望尽可能地提高效率。 这是一个改进版本:
bool must_redraw = true;
private void glControl1_Paint(object sender, PaintEventArgs e){
  must_redraw = !must_redraw;
  // ...
}

private void timer1_Tick(object sender, EventArgs e)
{
    if (must_redraw)
    {
        glControl1.Refresh();// redraws and updates
        gl_image = TakeScreenshot();
    }
}

不过,它会使绘制操作翻倍,从而将绘图速度减慢了2倍,因此如果有其他替代方案,请发表。


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