【问题标题】:Drawing an OpenGL scene to C# bitmap; off-screen gets clipped将 OpenGL 场景绘制到 C# 位图;屏幕外被剪辑
【发布时间】:2013-02-19 22:12:17
【问题描述】:

我在 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
    }

【问题讨论】:

    标签: c# opengl bitmap screenshot glreadpixels


    【解决方案1】:

    听起来你遇到了pixel ownership 问题:

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

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

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

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

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

    查看FBOs 或 PBuffers 以获得大于窗口和/或屏幕外的渲染。

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

    【讨论】:

    • 谢谢,这似乎是问题所在。我编写了自己的平铺算法(类似于 libtr)来解决这个问题,将场景渲染成许多视口大小的位图,然后将它们全部绘制成一个大位图。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-11
    • 2020-06-18
    相关资源
    最近更新 更多