将OpenGL场景绘制为C#位图;屏幕外被剪辑

本文关键字:屏幕 位图 OpenGL 绘制 | 更新日期: 2023-09-27 18:00:21

我在OpenGL窗口中绘制了一个复杂的2D场景。我希望用户能够拍摄场景的屏幕截图,并将其保存为JPG。然而,我希望他们能够指定场景可以绘制得比屏幕大(这将允许他们看到更多细节等)。

当他们指定的数字大于适合屏幕的视口时,他们看不到的所有内容都不会绘制到位图中。我觉得我对一些OpenGL函数的行为有一个错误的理解,我一直无法追踪到。

renderSize小于我启动程序时使用的默认视口时,下面的代码完全可以工作。如果renderSize更大,则会创建一个适当大小的JPG,但屏幕可见区域之外的右/上部分只是空白。如果我在第二个监视器上单击并拖动视口,则会绘制更多的图片,但如果renderSize比两个监视器都大,则仍然不会绘制全部图片。如何使此图形独立于屏幕上显示的视口?

(当调用此函数时,我可以验证renderLocationrenderSize是否设置正确,其中renderSize是一些大数字,如7000 x 2000)

public Bitmap renderToBitmap(RenderMode mode)
    {
        // load a specific ortho projection that will contain the entire graph
        GL.MatrixMode(MatrixMode.Projection);
        GL.PushMatrix();
        GL.LoadIdentity();
        GL.Ortho(0, renderSize.Width, 0, renderSize.Height, -1, 1);
        GL.Viewport(0, 0, renderSize.Width, renderSize.Height);
        GL.MatrixMode(MatrixMode.Modelview);
        GL.PushMatrix();
        GL.LoadIdentity();

        // move the graph so it starts drawing at 0, 0 and fills the entire viewport
        GL.Translate(-renderLocation.X, -renderLocation.Y, 0);
        GL.ClearColor(Color.White);
        GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
        // render the graph
        this.render(mode);
        // set up bitmap we will save to
        Bitmap bitmap = new Bitmap(renderSize.Width, renderSize.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
        BitmapData bData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
        // read the data directly into the bitmap's buffer (bitmap is stored in BGRA)
        GL.ReadPixels(0, 0, renderSize.Width, renderSize.Height, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, bData.Scan0);
        bitmap.UnlockBits(bData);
        bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY); // compensate for openGL/GDI y-coordinates being flipped
        // revert the stuff about openGL we changed
        GL.MatrixMode(MatrixMode.Projection);
        GL.PopMatrix();
        GL.MatrixMode(MatrixMode.Modelview);
        GL.PopMatrix();
        return bitmap;
    }

以下是我如何初始化GL窗口,以防出现这种情况。

private void initGL()
    {
        GL.ClearColor(Color.AntiqueWhite);
        GL.Disable(EnableCap.Lighting);
        GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
        resetCamera();
    }
    private void resetCamera()
    {
        offsetX = offsetY = 0; // Bottom-left corner pixel has coordinate (0, 0)
        zoomFactor = 1;
        setupViewport();
        glWindow.Invalidate();
    }
    private void setupViewport()
    {
        int w = glWindow.Width;
        int h = glWindow.Height;
        GL.MatrixMode(MatrixMode.Projection);
        GL.LoadIdentity();
        GL.Ortho(0 + offsetX, w + offsetX, 0 + offsetY, h + offsetY, -1, 1);
        GL.Viewport(0, 0, w, h); // Use all of the glControl painting area
    }

将OpenGL场景绘制为C#位图;屏幕外被剪辑

听起来你遇到了像素所有权问题:

14.070当我调用glReadPixels()时,为什么我不能获得重叠区域的有效像素数据窗户

这是由于OpenGL规范中称为Pixel的一部分所有权测试。如果一个窗口被另一个窗口遮挡,则不会必须存储被遮挡区域的像素数据。因此glReadPixels()调用可以返回遮挡区域的未定义数据。

像素所有权测试因OpenGL实现而异下一个一些OpenGL实现存储窗口的模糊区域,或者整个窗口。这样的实施可以返回遮挡窗口的有效像素数据。然而,许多OpenGL实现将屏幕上的像素一一映射到帧缓冲区存储位置和不存储(也不能返回)像素窗口的遮挡区域的数据。

一种策略是指示窗口系统打开窗口前进到窗口堆栈的顶部,进行渲染,然后执行glReadPixels()调用。然而,这种方法仍然会给用户带来风险可能会模糊源窗口的干预。

一种可能适用于某些应用程序的方法是将不可见的窗口,例如X窗口下的Pixmap。这种类型的绘图表面不能被用户遮挡,其内容应该始终通过像素所有权测试。从这样一幅画中阅读曲面应始终生成有效的像素数据。不幸的是,渲染对于这样的绘图表面,图形往往不会加速硬件

查看FBO或PBuffer以获得大于窗口和/或屏幕外渲染。

如果你不能/不会使用FBO或PBuffer,还有libtr