在OpenGL中绘制剪切UI元素的最佳方法

时间:2012-12-06 11:21:49

标签: c# opengl

我是一个复杂的UI系统,它允许很多东西,也可以用WPF完成,但支持多个平台(iOS,Android,Windows,...)。它还没有完成,现在我面临以下问题:

我的设计师想要旋转物体!旋转对象远比简单的轴对齐复杂,这是我不能使用glScissor的原因。一个可能有助于理解问题的小图:

Effect

你可以看到我需要剪切对象" 子容器"通过" 父容器"的范围。据我所知,几乎没有选择:

  • 使用模板缓冲区,在这种情况下我遇到了问题,因为我有不可见的对象,并且还必须影响模板缓冲区,因为它们可能会掩盖子对象。此外,我必须绘制每个对象两次,因为我需要在返回层次结构时减少模板缓冲区。
  • 切割用于绘制UI对象的平面(三角形;或任何其他ui模型),这看起来很有代价,因为它们可能会在不同的点处剪切(想象一个容器在一个旋转的容器中旋转容器...)而且它很难正确剪辑它们可能是性能问题的根源

然而,两者似乎都会导致很多不同的问题,并可能成为性能泄漏的根源。有没有其他方式来存档我想要的东西,或者有什么办法可以改善上述两种方法吗?

3 个答案:

答案 0 :(得分:4)

我最终使用了Stencil-Buffer,这会产生比深度方法更多的绘制调用,但更容易实现。

在我画画之前我写了这段代码:

if (_Mask)
{
    if (Stage.StencilMaskDepth++ == 0)
        GL.Enable(EnableFlags.STENCIL_TEST);

    GL.ColorMask(false, false, false, false);
    GL.DepthMask(false);
    GL.StencilFunc(StencilFunction.ALWAYS, Stage.StencilMaskDepth, Stage.StencilMaskDepth);
    GL.StencilOp(StencilOp.INCR, StencilOp.INCR, StencilOp.INCR);

    // Draw rectangle
    DrawColor(Colors.Black);

    GL.ColorMask(true, true, true, true);
    GL.DepthMask(true);
    GL.StencilFunc(StencilFunction.EQUAL, Stage.StencilMaskDepth, Stage.StencilMaskDepth);
    GL.StencilOp(StencilOp.KEEP, StencilOp.KEEP, StencilOp.KEEP);
}

在绘制完所有子节点后,将调用此代码:

if (_Mask)
{
    GL.ColorMask(false, false, false, false);
    GL.DepthMask(false);
    GL.StencilFunc(StencilFunction.ALWAYS, Stage.StencilMaskDepth, Stage.StencilMaskDepth);
    GL.StencilOp(StencilOp.DECR, StencilOp.DECR, StencilOp.DECR);
    // Draw rectangle
    DrawColor(Colors.Black);

    GL.ColorMask(true, true, true, true);
    GL.DepthMask(true);

    if (--Stage.StencilMaskDepth == 0)
        GL.Disable(EnableFlags.STENCIL_TEST);
}

也许我会在几个月内测试一些其他方法,但目前这是最容易实现的方法。

答案 1 :(得分:2)

这只是一个想法,但是如何使用深度缓冲来进行屏蔽?

  1. 启用深度缓冲并设置glDepthFunc(GL_LEQUAL);
  2. 在Z = 0
  3. 处渲染容器A及其框架
  4. 渲染容器Z = 1
  5. 的内部区域/背景(其他嵌套容器所在的位置)
  6. 现在你有一个“深度模板”,深度为0的容器框架和深度为1的容器内部。这意味着你在其间渲染的任何东西都将高于内部,但在框架下方(并被其剪裁)
  7. 现在使用下一个Container B,将其框架渲染到Z = 0.5(它将被GPU上的父容器A剪切)
  8. 在Z = 0.75
  9. 处渲染容器B内部区域
  10. 现在,您要在容器B中呈现的任何内容都必须在Z = 0.75处。它将覆盖容器内部区域,但将被容器A和B框架剪切。

答案 2 :(得分:1)

也许您可以尝试渲染纹理。创建父纹理。然后将所有子项渲染到该纹理。然后将父纹理渲染到屏幕变形并根据需要移动它。此解决方案可能存在问题,也可能不存在问题,具体取决于您希望实现的目标。特别是如果您为容器比例设置动画或者具有许多嵌套容器的非常复杂的树,您可能会遇到性能问题。