绘制OpenGL场景到C#位图;离屏区域被剪裁

3
我在一个OpenGL窗口中绘制了一个复杂的2D场景。我希望用户能够截取场景并将其保存为JPG格式,但我希望他们能够指定大于屏幕大小的场景可以被绘制(这将允许他们看到更多细节等)。当他们指定的数字比适合屏幕的视口更大时,所有对他们不可见的部分都不会被绘制到位图上。我感觉我对一些OpenGL函数的行为有误解,但我一直没有找到原因。以下代码在renderSize比我程序启动时的默认视口小的情况下完全正常。如果renderSize更大,则会创建一个适当大小的JPG,但是超出屏幕可见区域的右/顶部部分为空白。如果我点击并拖动我的视口到第二个监视器上,那么图片的更多部分会被绘制,但如果renderSize大于两个监视器的大小,仍然无法绘制全部。如何使此绘制与屏幕显示的视口无关?(我可以验证调用此函数时renderLocation和renderSize已正确设置,其中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
    }
1个回答

2
听起来你遇到了一个像素所有权的问题:
14.070 为什么当我调用glReadPixels()时,窗口的一部分被另一个窗口覆盖时,我无法获得重叠区域的有效像素数据?
这是由于OpenGL规范中的一个部分,称为像素所有权测试。如果一个窗口被另一个窗口遮挡,它不必存储被遮挡区域的像素数据。因此,glReadPixels()调用可能会返回未定义的遮挡区域的数据。
像素所有权测试在不同的OpenGL实现中有所不同。一些OpenGL实现将窗口的遮挡区域或整个窗口存储在离屏缓冲区中。这样的实现可以返回遮挡窗口的有效像素数据。然而,许多OpenGL实现将屏幕上的像素一对一地映射到帧缓冲存储位置,并且不存储(也无法返回)窗口的遮挡区域的像素数据。
一种策略是指示窗口系统将窗口置于窗口堆栈的顶部,进行渲染,然后执行glReadPixels()调用。然而,这种方法仍然存在用户干预的风险,可能会遮挡源窗口。
对于某些应用程序来说,可能适用的一种方法是渲染到一个不可见的窗口,例如X Windows下的一个Pixmap。这种类型的绘图表面不会被用户遮挡,其内容应始终通过像素所有权测试。从这样的绘图表面读取应始终产生有效的像素数据。不幸的是,使用图形硬件加速渲染到这样的绘图表面通常是不可能的。
查看FBOs或PBuffers以进行大于窗口和/或离屏渲染。
如果您不能/不想使用FBOs或PBuffers,还有libtr

1
谢谢,看起来这确实是问题所在。我编写了自己的平铺算法(类似于libtr)来克服这个问题,将场景渲染成许多视口大小的位图,然后稍后将它们全部绘制到一个大位图中。 - XenoScholar

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