如何在 OpenTK 中裁剪圆形区域(小地图!)

How to clip circular region in OpenTK (mini-map!)

我的游戏是在 C# 中使用 OpenTK 编写的,OpenTK 是 OpenGL 的包装器。我正在渲染我的迷你地图,如下所示。问题是对象没有在边缘被剪裁,所以它们会溢出。我可以渲染一个更粗的边框来隐藏它,但这并不理想,而且它不适用于较大的对象。可能有轨迹线和其他超出界限的东西。我只是使用基本原语渲染这些。

我该如何裁剪这个区域?有什么方法可以将其渲染为隐藏 canvas 然后只复制圆形区域吗?


解决方案

@BDL 回答了这个问题,但我必须再调整一些东西才能让它工作,所以这里是完整的解决方案,以防有人觉得它有用:

在 GameWindow 构造函数 GraphicsMode 参数中启用模板缓冲区。

    private static OpenTK.Graphics.GraphicsMode GraphicsMode {
        get {
            var defaultMode = OpenTK.Graphics.GraphicsMode.Default;
            var custom = new OpenTK.Graphics.GraphicsMode(
                defaultMode.ColorFormat,
                defaultMode.Depth,
                1, // enable stencil buffer
                defaultMode.Samples,
                defaultMode.ColorFormat,
                defaultMode.Buffers,
                defaultMode.Stereo);

            return custom;

        }
    }

    public BaseHelioUI(int windowWidth, int windowHeight)
        : base(
              windowWidth, 
              windowHeight, 
              BaseHelioUI.GraphicsMode,
              "", 
              GameWindowFlags.Default )
    {

然后使用这个块来绘制模板,在我的例子中是一个圆圈:

        GL.Enable(EnableCap.StencilTest);
        GL.StencilFunc(StencilFunction.Always, 1, 0xFF);
        GL.StencilOp( StencilOp.Keep, StencilOp.Keep, StencilOp.Replace );
        GL.Clear(ClearBufferMask.StencilBufferBit);

        // draw background & outline
        this.Renderer.DrawCircle(
            this.MinimapScreenCenter,
            SENSOR_RANGE_IN_METERS * this.GameMetersToMinimapUnitsFactor,
            Colors.ReduceAlpha( Colors.Black, MINIMAP_BACKGROUND_OPACITY )
            //Colors.ReduceAlpha( Colors.DarkGrey, MINIMAP_BACKGROUND_OUTLINE_OPACITY),
            //MINI_MAP_BORDER_WIDTH
            );

        GL.Enable(EnableCap.StencilTest);
        GL.StencilFunc(StencilFunction.Equal, 1, 0xFF);
        GL.StencilOp(StencilOp.Keep, StencilOp.Keep, StencilOp.Keep);

然后在该区域内绘制您想要的所有其他内容,在我的例子中是小地图上的项目。

然后当您完成模板使用后,只需清除它,这样其他一切都会正常呈现。在我的例子中,这是在 RenderMinimap 方法的末尾。

        // disable stencil
        GL.Clear(ClearBufferMask.StencilBufferBit);
        GL.Disable(EnableCap.StencilTest);

这是生成的小地图,正确地裁剪了圆圈边缘的项目。看起来很棒。 (船在游戏中 space,不在小地图上)

可以使用模板缓冲区在任意区域进行裁剪:

首先用 0 (glClear) 清除模板缓冲区。

接下来,在启用以下模板操作的情况下渲染背景圆(剪裁区域):

glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 1, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);

这会将圆圈覆盖的所有像素的模板缓冲区设置为 1。

当您现在渲染应显示在圆圈内的内容时,请使用以下设置:

glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_EQUAL, 1, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

这里,只有当模板缓冲区中的那些位置已经存储了 1 时,模板测试才会成功(这仅适用于裁剪区域内的像素)。在其他任何地方,模板测试都会失败,并且不会呈现任何内容。

不要忘记确保您的帧缓冲区有可用的模板缓冲区。