2

我将复杂的 2D 场景绘制到 OpenGL 窗口。我希望用户能够截取场景的屏幕截图并将其保存为 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
    }
4

1 回答 1

1

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

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

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

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

一种策略是指示窗口系统将窗口前移到窗口堆栈的顶部,渲染,然后执行 glReadPixels() 调用。然而,这种方法仍然存在用户干预的风险,可能会混淆源窗口。

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

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

libtr如果你不能/不会使用 FBO 或 PBuffers,也有。

于 2013-02-20T00:25:41.083 回答