【问题标题】:What is the "OpenGL State Machine"?什么是“OpenGL 状态机”?
【发布时间】:2015-07-08 02:47:16
【问题描述】:

我的大脑倾向于以分层、面向对象、类似组件的方式构建概念。不幸的是,这使我无法理解 OpenGL——我怀疑我困惑的根源在于我误解了“OpenGL 状态机”是什么。你得到了graphics pipeline,但这确实是针对绘制用户图形组件的各个程序对象的,对吧?

  • 这个主状态机是什么?
    • 是 OpenGL api,还是我尝试绘制的帧,还是用户定义的东西——比如管道?
  • 它可以存在于哪些不同的状态?
    • 它也是事件驱动的吗?
  • 哪些输入会影响机器可以处于的不同状态?
  • 可以将分层 OOP 概念应用于此状态机吗?

【问题讨论】:

    标签: opengl opengl-4


    【解决方案1】:

    这个状态机是什么?

    想象一个带有几十个开关拨盘的总机。该配电盘连接到加工厂中的机器,根据开关的切换方式,从一侧进入加工厂的东西将沿特定路径穿过工厂。改变其中一些开关的状态,事情就会走向另一个方向。

    还有状态机,当东西经过某个处理步骤时,开关和刻度盘会发生变化; OpenGL 不是那种。

    OpenGL 上下文就是这样一个交换机,后面有一个加工厂。总机是你通过 API 控制的,加工厂是制作图片的。

    它可以存在哪些不同的状态?

    太多,无法列出。对于每个开关,状态数乘以开关可以处于的位置数。假设您有 20 个拨动开关,一个拨盘有 5 个位置,一个拨盘有 7 个位置,那么您有 2^20 * 5 * 7 = 36700160 可能不同状态。某些版本的 OpenGL 有超过 300 个状态变量,其中许多不仅仅是布尔值。因此,试图列出每一个可能的状态是徒劳的。

    它也是事件驱动的吗?

    不!

    哪些输入会影响机器可以处于的不同状态?

    OpenGL API 状态改变函数的显式调用;那和默认的初始状态。

    可以将分层的 OOP 概念应用于此状态机吗?

    没有。或者更确切地说,您可以尝试,但说实话,唯一真实的表示将是一个包含所有状态的 OpenGL 上下文类。

    在 OpenGL 上下文中有一些东西,它们的行为就像具有自己状态的单个对象。例如纹理,但是这些对象与 OpenGL 上下文本身密切相关,不能独立查看。


    关于您的评论的个人说明

    我的大脑倾向于以分层、面向对象、类似组件的方式来构建概念。

    别再那样做了! 计算机不是那样工作的。 OOP 是一种构建项目的方法,但它不是一个很好的工具来理解一些预先存在的系统。尤其是本质上非分层的系统。

    我强烈建议您通过学习其他构建项目的方法来扩展您的视野。学习函数式编程怎么样? Haskell 将成为目前流行的 FP 语言之一,并且由于它专注于成为一种纯语言,因此它的学习体验非常好。这是一本非常好的在线书籍/教程:http://learnyouahaskell.com/chapters – 准备好你的思想;有些事情看起来像是 OOP,但实际上不是;靠着这些墙跑可能看起来令人沮丧,但是当它最终在你的脑海中点击并且“你明白了”(= 顿悟)时,它的好处是真的值得。

    请注意,OpenGL 既不是功能性的,也不是真正的 OOP。而且由于不是函数式的,它不能很好地映射到纯函数式编程中。 Haskell 有 OpenGL 绑定,但它们是通过称为“monad”的东西实现的; monads 是函数式程序用来联系状态甚至驱动的环境。

    我推荐的另一个资源是https://mitpress.mit.edu/sicp/full-text/book/book.html - 每个程序员都应该读过它。

    【讨论】:

    • 关于OO设计和FP的优秀笔记!
    【解决方案2】:

    这是一个非常广泛的问题,所以我只给你一个概述。

    • 这个主状态机是什么?

    状态机是完整的 OpenGL API 和当前选择的渲染上下文的组合。渲染上下文包含构成状态机的所有状态,而 OpenGL API 提供改变这些状态的输入。

    • 它可以存在哪些不同的状态?

    太多了,无法一一列举。例如,glEnableglDisable 切换的功能(例如GL_TEXTURE_2DGL_FOGGL_BLEND)是状态的一部分。当前纹理集的名称用glBindTexture;当前着色器程序的名称来自glUseProgram;模型视图和投影矩阵的内容加上glMatrixMode 的状态,它告诉任何glMultMatrix(等)调用将影响哪个矩阵;等等,都是状态的一部分

    • 哪些输入会影响机器可以处于的不同状态?

    机器的初始状态由 OpenGL 规范给出,以 GPU 驱动程序为模。唯一能使机器进入不同状态的输入是对 OpenGL API 的调用。

    • 可以将分层 OOP 概念应用于此状态机吗?

    这个问题很模糊。什么是“分层的、OOP 概念”?

    可以采用面向对象的方法来包装 OpenGL API。您可以创建一个“纹理”对象类,在初始化时使用glGenTextures 分配纹理名称,并提供用于上传纹理图像或更改纹理参数的方法,这些方法总是在调用方法的相关API 之前调用glBindTexture。这种方法通常很棘手,因为您仍然必须通过对象和子对象方法调用来跟踪当前状态。

    OpenGL 提供了几个“堆栈”(参见glPushMatrixglPushAttrib),旨在帮助管理对象的层次结构。通过将当前状态的一部分压入堆栈,调用子对象的方法,然后将该状态从堆栈中弹出,您可以将状态恢复到父对象所期望的状态。

    使用 OpenGL API 的一般面向对象方法是通过场景图 - 对象的有向无环图,其中每个对象代表状态机状态的一些变化。对象推送到一个或多个 OpenGL 堆栈,使用 OpenGL API 调用应用其状态,调用其子对象的方法,然后从这些 OpenGL 堆栈中弹出以恢复之前的状态。

    【讨论】:

    • glMatrixMode, GL_FOG, glPushMatrix, glMultMatrixglPushAttrib 不在现代 OpenGL 中,这个问题被标记为“opengl-4”
    • 那么面向对象的方法就更狡猾了:您必须将您的对象认为 GL 处于的状态与 GL 的实际状态同步,方法是在整个地方调用 glIsEnabled,或者通过复制整个状态并可能通过包装 glEnabled 等来实现您自己的状态堆栈。
    • 什么?我已经完成了大量的 OpenGL 编程,但从来没有做过任何事情。它应该隐含在你的代码中,永远不要输入没有意义的函数;就这么简单。不管怎样,OpenGL 已经慢慢走上了无状态的道路。
    【解决方案3】:

    从我的 POV 来看,“状态机”的表述在这里具有误导性。当我听到“状态机”时,我会自动想到一个具有少量已定义状态的有限自动机。它始终处于其中一种状态,并对某些输入做出反应以确定下一个状态和输出将是什么。

    然而,这不是 OpenGL 所做的。 (从数学上讲,你可以把它想象成这样一个自动机,但如果你这样做了,你会人为地创建大量可能的状态,这无济于事)。

    是的,OpenGL API 是“有状态”API。你告诉它很多它在“状态”中记住的东西,它基本上是一组变量。然后,您要求它渲染一些东西,OpenGL 将使用您之前设置的状态来确定要对要渲染的数据执行什么操作。

    顺便说一句:我发现 Joey de Vries 的教程对理解 OpenGL 的作用非常有帮助。请参阅https://learnopengl.com/Getting-started/OpenGL 了解更多信息。

    【讨论】:

      【解决方案4】:

      说实话,我找到了以你想要理解的方式理解它的最佳方式,那就是面向对象编程

      根据我对 opengl 的了解,这里是我对 opengl api 的最佳解释

      首先,如果你将它与其他api如SDL进行比较,它可能更复杂和不同,使用api试图描述它真的不公平

      就面向对象编程而言

      想象一下,如果你有一个类,这个类是私有的,它的所有工作方法都不打算被用户查看,但是它也有一个默认状态,所以如果你使用它“开箱即用” " 它会做它在默认状态下所做的事情(这几乎什么都没有)

      当您创建上下文时,就像您创建了此类的一个实例(并且您可以创建任意数量的此类实例,即您也可以创建多个上下文),这是运行 opengl 所必需的,并且在OOP,因为您现在无法使用类,直到您从中创建对象/实例,唯一的区别是构造函数使用您的硬件/操作系统/其他 api/硬件信息来完成实例/对象,以便它可用,或者将其插入某个“屏幕”

      但是,此类中包含的方法(函数)和字段(数据存储)使您能够做任何您想做的事情并使用此实例,当然,您如何做某些事情是基于在方法上,如果不使用它的方法/字段(您被允许使用的方法/字段,即是公共的),您无法更改此类/实例/对象的任何内容,您不能只说我想自己这样做,或者选择存储字段的位置(外部等),所有这些都必须与实例、类和方法有关,在这个实例/对象/类中有数百个方法和字段(opengl 4.5 有超过 500 个命令(方法/字段) ) 影响这个实例/类) 以及你对它所做的任何事情,都会改变这个特定的实例/对象,就好像你有一些粘土并正在将它塑造成你想要的任何方式,当然有些东西你必须放在外部插入此实例,例如具有某些功能的着色器

      所以它真的有点像电视屏幕的遥控器,它的方法/按钮会影响根据您在遥控器/实例/方法/功能上按下的内容显示的上下文(屏幕/电视/监视器)这个类比的不同之处在于,它不仅需要按下按钮,还需要数据以及您可以呈现它的所有各种方式(这就是您在屏幕上绘制的内容)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-05-11
        • 1970-01-01
        • 1970-01-01
        • 2020-09-24
        • 2018-08-15
        • 2011-03-30
        • 1970-01-01
        相关资源
        最近更新 更多