【问题标题】:glScissor() call inside another glScissor()glScissor() 在另一个 glScissor() 中调用
【发布时间】:2016-08-25 14:57:39
【问题描述】:

我在我的应用程序中使用了glScissor(),它工作正常,但我遇到了一个问题: 我有我的Window 对象,其绘图区域由glScissor() 指定,并且在该区域内,我正在绘制我的ListView 对象,因为我不想要它的绘图区域也应该用glScissor() 指定全部画出来。

在代码中我可以将其表示为:

Window::draw() 
{
   glEnable(GL_SCISSOR_TEST);
    glScissor(x, y, width, height);
        // Draw some components...
        mListView.draw(); // mListView is an object of ListView type
   glDisable(GL_SCISSOR_TEST);
}

ListView::draw()
{
  glEnable(GL_SCISSOR_TEST);
    glScissor(x, y, width, height);
        // Draw a chosen part of ListView here
   glDisable(GL_SCISSOR_TEST);
}

当然在这种情况下启用/禁用调用是错误的:

glEnable(GL_SCISSOR_TEST);
   glEnable(GL_SCISSOR_TEST);
   glDisable(GL_SCISSOR_TEST);
glDisable(GL_SCISSOR_TEST);

如果我删除那些内部 glEnable/glDisable 调用(ListView 中的调用),我无论如何都会得到两个 glScissor() 调用,这似乎也是错误的。

编辑

我想以某种方式实现这两种剪刀效果,我的意思是 - Window 应该只在它的剪刀区域中绘制,而内部 ListView 也只能在它的剪刀区域中。

正如您在图片中看到的那样,我用红色矩形标记了剪刀区域Window,它可以工作,用蓝色矩形标记我想在其上绘制ListView 的区域。这就是我尝试使用嵌套剪刀的原因,但我知道它没用。所以基本上我的问题是,实现这一目标的最佳方法是什么?

【问题讨论】:

  • 一个有引用计数的对象
  • @PlasmaHH 抱歉,我不明白你的意思。什么具有引用计数的对象与我的问题有关?
  • 您有一个对象,您可以在其中调用启用/禁用,它会计算您执行此操作的频率,并且仅在它调用启用/禁用的第一个/最后一个。

标签: c++ user-interface opengl


【解决方案1】:

由于 OpenGL 是一个状态机,并且与任何其他状态一样,在下次调用 glScissor 时会覆盖剪裁矩形,因此您必须在绘制列表视图后正确恢复窗口的剪裁矩形。这可以通过让窗口管理它来完成:

Window::draw() 
{
    // draw some components with their own scissors
    mListView.draw(); // mListView is an object of ListView type

    glScissor(x, y, width, height);
    glEnable(GL_SCISSOR_TEST);
        // draw other stuff using window's scissor
    glDisable(GL_SCISSOR_TEST);
}

但是让各个组件自己恢复剪刀状态可能更灵活,尤其是在以这种分层方式使用时。为此,您可以使用已弃用的 glPush/PopAttrib 函数来保存和恢复剪式矩形:

ListView::draw()
{
    glPushAttrib(GL_SCISSOR_BIT);
    glEnable(GL_SCISSOR_TEST);
    glScissor(x, y, width, height);
        // Draw a chosen part of ListView here
    glDisable(GL_SCISSOR_TEST);
    glPopAttrib();
}

或者你自己保存和恢复剪刀状态:

ListView::draw()
{
    // save
    int rect[4];
    bool on = glIsEnabled(GL_SCISSOR_TEST);
    glGetIntegerv(GL_SCISSOR_BOX, rect);

    glEnable(GL_SCISSOR_TEST);
    glScissor(x, y, width, height);
        // Draw a chosen part of ListView here

    // restore
    glScissor(rect[0], rect[1], rect[2], rect[3]);
    if(!on)
        glDisable(GL_SCISSOR_TEST);
}

这当然可以通过一个不错的 RAII 包装器自动完成,但您可以免费练习。

【讨论】:

  • @Mosquito 好吧,实际上我发现 sheu 的方法更好、更灵活。我什至没有考虑到这个事实,listview 的剪刀可能不在封闭窗口的内部。
  • 这不适用于我的。可能是什么问题?
【解决方案2】:

已编辑:一般情况。

每次使用glScissor 设置剪刀状态时,都会设置剪刀状态。它不嵌套,也不堆叠,因此您不能通过对glScissor 的嵌套调用来“剪裁”。您必须手动计算ListViewWindow 边界矩形的矩形交集,然后在绘制ListView 时对其进行剪裁。

在一般情况下,您将手动维护一堆剪刀矩形。在绘制每个子元素时,将子元素的边界矩形与堆栈的当前顶部相交,并将其用作该子元素的剪刀。绘制子项时将新的子矩形压入堆栈,并在返回层次结构时将其弹出。

如果你将其他内容绘制到Window,你还必须确保正确处理过度绘制;通过设置 Z 排序并启用深度缓冲,或者通过禁用深度缓冲并从后到前绘制内容。剪切不会帮助您掩盖ListView 后面的Window 内容,因为剪刀只能是矩形区域。

【讨论】:

    【解决方案3】:

    OpenGL 是一个状态机。您可以根据需要随时调用 glScissor、glEnable 和 glDisable。它们不像必须匹配的开闭大括号。如果你的电话像这样“折叠”,那没问题。只是不要指望一把剪刀会与另一把剪刀合并;它只会更改/覆盖以前的设置。

    【讨论】:

    • 嗯,是的,这就是我害怕的事实。 :-) 也许我的问题并没有真正解释这一点,但我想以某种方式实现两种剪刀效果,我的意思是 - Window 应该只在它的剪刀区域和内部 ListView 也只能在它的剪刀区域绘制。我将编辑我的问题以使其更清楚。
    • @Mosquito 列表视图不总是完全在窗口内吗?在这种情况下,列表视图的内部(较小)剪刀应该足够了。您只需要在绘制列表视图后正确恢复窗口的(更大)剪刀(以便正确呈现以下元素)。除此之外(如果 listview scissor 可能比 window scissor 大),只需计算 window 和 listview 的相交矩形并将其用作 listview 的 scissor rect。
    • @ChristianRau 是的,你是对的,在绘制列表视图后恢复窗口的剪刀应该可以解决问题。 :-) 是否有可能获得当前的 glScissor rect?
    • @Mosquito 好吧,无论如何您都知道,因为您在窗口绘制例程中设置了它。但是如果你很懒,你也可以将列表视图(和任何其他窗口的)绘图代码包装在glPushAttrib(GL_SCISSOR_BIT); ... glPopAttrib(); 块中。这将保存和恢复剪式矩形和启用标志。但请注意,这些功能在现代(3+ 核心)GL 中已弃用。否则,可以使用glGetIntegerv(GL_SCISSOR_BOX, int_4_array); 获得当前剪式矩形。我想我会写一个答案。
    • @Mosquito:glScissor 的参数在窗口坐标中。这只是一些最小/最大比较和一些减法来“堆叠”它们的问题。我建议你拿一张纸,试着自己弄清楚。如果您找不到解决方案,我会发布一些代码。
    猜你喜欢
    • 1970-01-01
    • 2022-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-28
    • 1970-01-01
    相关资源
    最近更新 更多