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

问题描述 投票:0回答:1

我将复杂的 2D 场景绘制到 OpenGL 窗口。我希望用户能够截取场景并将其保存为 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
    }
c# opengl bitmap screenshot glreadpixels
1个回答
2
投票

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

14.070 当我调用 glReadPixels() 时,为什么我没有获得重叠区域的有效像素数据,其中窗口的一部分与另一个窗口重叠 窗户?

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

像素所有权测试因 OpenGL 实现的不同而异 下一个。一些 OpenGL 实现存储窗口的遮挡区域, 或整个窗口,在屏幕外缓冲区中。这样的实现 可以返回被遮挡窗口的有效像素数据。然而,许多 OpenGL 实现将屏幕上的像素一对一映射到 帧缓冲区存储位置并且不存储(并且无法返回)像素 窗口被遮挡区域的数据。

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

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

研究 FBOs 或 PBuffers 以实现大于窗口和/或离屏渲染。

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

libtr

© www.soinside.com 2019 - 2024. All rights reserved.