"OpenGL State Machine" 是什么?
What is the "OpenGL State Machine"?
我的大脑倾向于以分层的、面向对象的、类似组件的方式来构建概念。不幸的是,这让我无法理解 OpenGL——我怀疑我的困惑源于我对 "OpenGL state machine" 是什么的误解。您得到了 graphics pipeline,但它确实特定于绘制用户图形组件的各个程序对象,对吗?
- 这个主状态机是什么?
- 是 OpenGL api,还是我正在 尝试 绘制的框架,还是用户定义的东西——比如管道?
- 它可以存在哪些不同的状态?
- 是否也是事件驱动?
- 哪些输入会影响机器可能处于的不同状态?
- 分层的 OOP 概念是否可以应用于此状态机?
这是一个非常广泛的问题,所以我只给你一个概述。
- 这个主状态机是什么?
状态机是完整的 OpenGL API 和当前选择的渲染上下文的组合。渲染上下文包含构成状态机的所有状态,而 OpenGL API 提供更改这些状态的输入。
- 它可以存在于哪些不同的状态?
太多了,无法一一列举。例如,由 glEnable
和 glDisable
切换的功能(例如 GL_TEXTURE_2D
、GL_FOG
、GL_BLEND
)是状态的一部分。 glBindTexture
当前纹理集的名称;来自 glUseProgram
的当前着色器程序的名称;模型视图和投影矩阵的内容加上 glMatrixMode
的状态,表明任何 glMultMatrix
(等)调用将影响哪个矩阵;等等,都是状态的一部分
- 哪些输入会影响机器可能处于的不同状态?
机器的初始状态由 OpenGL 规范以 GPU 驱动程序为模给出。唯一可以使机器进入不同状态的输入是调用 OpenGL API.
- 分层的、OOP 的概念可以应用到这个状态机吗?
这个问题含糊不清。 "hierarchical, OOP concepts" 是什么?
可以采用面向对象的方法来包装 OpenGL API。可以创建一个"Texture"对象class,在初始化时使用glGenTextures
分配纹理名称,并提供上传纹理图像或更改纹理参数的方法,之前总是调用glBindTexture
为方法调用相关的 APIs。这种方法通常很棘手,因为您仍然必须通过对象和子对象方法调用来跟踪当前状态。
OpenGL 提供了几个 "stacks"(相对于 glPushMatrix
、glPushAttrib
)来帮助管理对象的层次结构。通过将当前状态的某些部分压入堆栈,调用子对象的方法,然后将该状态从堆栈中弹出,您可以将状态恢复到父对象所期望的状态。
使用 OpenGL API 的一般面向对象方法是通过场景图 - 对象的有向无环图,其中每个对象代表状态机状态的一些变化。该对象推送到一个或多个 OpenGL 堆栈,使用 OpenGL API 调用应用其状态,调用其子对象的方法,然后弹出这些 OpenGL 堆栈以恢复先前的状态。
What is this state machine?
想象一下带有几十个开关拨号盘的总机。该配电盘连接到加工厂的机器,根据开关的切换方式,从一侧进入加工厂的物料将以特定路径穿过工厂。改变其中一些开关的状态,事情就会发生变化。
还有状态机,当东西经过某个处理步骤时,开关和拨盘会发生变化; OpenGL 不是那种类型。
OpenGL上下文就是这样一个配电盘,背后有一个处理设备。交换机是你通过API控制的,加工厂是制作图片的。
What are the different states it can exist in?
太多了,无法一一列举。对于每个开关,状态数乘以开关可以处于的位置数。假设您有 20 个拨动开关,一个拨盘有 5 个位置,一个拨盘有 7 个位置,那么您有 2^20 * 5 * 7 = 36700160
可能的不同状态。某些版本的 OpenGL 有超过 300 个状态变量,其中许多不仅仅是布尔值。因此,试图列出每一个可能的状态是徒劳的。
Is it also event-driven?
没有!
What inputs affect the different states the machine can be in?
显式调用 OpenGL 的状态改变函数API;那和默认的初始状态。
Can hierarchical, OOP concepts be applied to this state machine?
没有。或者更确切地说,您可以尝试,但说实话,唯一真实的表示是单个 OpenGL 上下文 class 保存所有状态。
在 OpenGL 上下文中有一些东西,它们的行为就像具有自己状态的单个对象。例如纹理,但是这些对象与 OpenGL 上下文本身紧密相关,不能单独查看。
关于您的评论的个人说明
My brain tends to favor structuring concepts in hierarchical, object-oriented, component-like ways.
别这样了! 计算机不是那样工作的。 OOP 是一种构建项目的方法,但它不是理解某些已有系统的好工具。尤其是那些本质上是非继承制的系统。
我强烈建议您通过学习其他构建项目的方法来扩大您的视野。学习 函数式编程 怎么样? Haskell 将成为目前流行的 FP 语言之一,并且由于它专注于成为一种纯语言,因此学习体验非常好。这是一个非常好的在线 book/tutorial:http://learnyouahaskell.com/chapters – 准备好让你的思想弯曲;有些东西看起来像是 OOP,但实际上不是; 运行 靠在那些墙上似乎令人沮丧,但当它最终在你的脑海中响起时,好处和 "you get it"(= 顿悟)是真正值得的。
请注意,OpenGL 既不是功能性的,也不是真正的 OOP。由于不是函数式的,它不能很好地映射到纯函数式编程中。 Haskell 有 OpenGL 绑定,但它们是通过称为 "monad" 的东西实现的; monads 是函数式程序用来联系状态 或 甚至驱动环境的东西。
我推荐的另一个资源是 https://mitpress.mit.edu/sicp/full-text/book/book.html – 每个程序员都应该读过它。
老实说,我发现以面向对象编程的方式理解这一点的最佳方式
根据我对 opengl 的了解,这里是我对 opengl api 是什么的最好解释
首先,如果您将它与其他 api(例如 SDL)进行比较,它可能更加复杂和不同,使用 api 来尝试描述它确实不公平
所以在面向对象编程方面
想象一下,如果你有一个 class ,这个 class 是私有的,它的所有工作方法都不打算被用户查看,但是它也有一个默认状态,这样如果你使用它 "right out of the box" 它会做它在默认状态下所做的事情(几乎什么都没有)
当您创建上下文时,就像您创建了此 class 的一个实例(并且您可以根据需要创建任意多个此 class 的实例,即您还可以创建多个上下文) 这是 运行 opengl 所必需的,而在 OOP 中你现在不能使用 class 直到你从它创建一个 object/instance ,唯一的区别是构造函数采用你的 hardware/os/other api/hardware 信息来完成 instance/object 以便它可用,或者将它插入一些 "screen"
然而,这个 class 中包含的方法(函数)和字段(数据存储)使您能够使用它做任何您想做的事情并使用这个实例,当然如何你做某些事情是基于你不能改变这个 class/instance/object 的任何东西而不使用它的 methods/fields(你被允许使用的那些 public ), 你不能只说我想自己做某事,或者选择存储字段的位置(外部等),所有这些都必须属于实例和 class 和方法,并且在这个 instance/object/class 有数百种方法和字段(opengl 4.5 有超过 500 个命令(methods/fields)会影响此 instance/class),无论您对它做什么,都会改变这个特定的instance/object 就好像你有一些粘土并将其塑造成你想要的样子,当然有些东西你必须从外部插入这个实例,例如具有特定功能的着色器
所以它真的有点像电视屏幕的遥控器,它有它的 methods/buttons 影响根据你在 [=40 上按下的内容显示的上下文 (screen/tv/monitor) =] 与此类比的不同之处在于,它不仅需要按下按钮,还需要数据以及您可以呈现它的所有各种方式(这就是您在屏幕上绘制的内容)
从我的观点来看,"state machine" 的表达在这里具有误导性。当我听到 "state machine" 时,我会自动想到具有少量已定义状态的有限自动机。它始终处于其中一种状态并对某些输入做出反应以确定下一个状态和输出将是什么。
但这不是 OpenGL 所做的。 (从数学上讲,您可以将其视为这样的自动机,但如果这样做,您会人为地创建大量可能的状态,这对您没有帮助)。
是的,OpenGL API 是 "stateful" API。你告诉它很多它记得在它的 "state" 里面的东西,它基本上是一组变量。然后,您要求它渲染一些东西,OpenGL 将使用您之前设置的状态来确定要对要渲染的数据执行的操作。
顺便说一下:我发现 Joey de Vries 的教程对于理解 OpenGL 的功能非常有帮助。有关详细信息,请参阅 https://learnopengl.com/Getting-started/OpenGL。
我的大脑倾向于以分层的、面向对象的、类似组件的方式来构建概念。不幸的是,这让我无法理解 OpenGL——我怀疑我的困惑源于我对 "OpenGL state machine" 是什么的误解。您得到了 graphics pipeline,但它确实特定于绘制用户图形组件的各个程序对象,对吗?
- 这个主状态机是什么?
- 是 OpenGL api,还是我正在 尝试 绘制的框架,还是用户定义的东西——比如管道?
- 它可以存在哪些不同的状态?
- 是否也是事件驱动?
- 哪些输入会影响机器可能处于的不同状态?
- 分层的 OOP 概念是否可以应用于此状态机?
这是一个非常广泛的问题,所以我只给你一个概述。
- 这个主状态机是什么?
状态机是完整的 OpenGL API 和当前选择的渲染上下文的组合。渲染上下文包含构成状态机的所有状态,而 OpenGL API 提供更改这些状态的输入。
- 它可以存在于哪些不同的状态?
太多了,无法一一列举。例如,由 glEnable
和 glDisable
切换的功能(例如 GL_TEXTURE_2D
、GL_FOG
、GL_BLEND
)是状态的一部分。 glBindTexture
当前纹理集的名称;来自 glUseProgram
的当前着色器程序的名称;模型视图和投影矩阵的内容加上 glMatrixMode
的状态,表明任何 glMultMatrix
(等)调用将影响哪个矩阵;等等,都是状态的一部分
- 哪些输入会影响机器可能处于的不同状态?
机器的初始状态由 OpenGL 规范以 GPU 驱动程序为模给出。唯一可以使机器进入不同状态的输入是调用 OpenGL API.
- 分层的、OOP 的概念可以应用到这个状态机吗?
这个问题含糊不清。 "hierarchical, OOP concepts" 是什么?
可以采用面向对象的方法来包装 OpenGL API。可以创建一个"Texture"对象class,在初始化时使用glGenTextures
分配纹理名称,并提供上传纹理图像或更改纹理参数的方法,之前总是调用glBindTexture
为方法调用相关的 APIs。这种方法通常很棘手,因为您仍然必须通过对象和子对象方法调用来跟踪当前状态。
OpenGL 提供了几个 "stacks"(相对于 glPushMatrix
、glPushAttrib
)来帮助管理对象的层次结构。通过将当前状态的某些部分压入堆栈,调用子对象的方法,然后将该状态从堆栈中弹出,您可以将状态恢复到父对象所期望的状态。
使用 OpenGL API 的一般面向对象方法是通过场景图 - 对象的有向无环图,其中每个对象代表状态机状态的一些变化。该对象推送到一个或多个 OpenGL 堆栈,使用 OpenGL API 调用应用其状态,调用其子对象的方法,然后弹出这些 OpenGL 堆栈以恢复先前的状态。
What is this state machine?
想象一下带有几十个开关拨号盘的总机。该配电盘连接到加工厂的机器,根据开关的切换方式,从一侧进入加工厂的物料将以特定路径穿过工厂。改变其中一些开关的状态,事情就会发生变化。
还有状态机,当东西经过某个处理步骤时,开关和拨盘会发生变化; OpenGL 不是那种类型。
OpenGL上下文就是这样一个配电盘,背后有一个处理设备。交换机是你通过API控制的,加工厂是制作图片的。
What are the different states it can exist in?
太多了,无法一一列举。对于每个开关,状态数乘以开关可以处于的位置数。假设您有 20 个拨动开关,一个拨盘有 5 个位置,一个拨盘有 7 个位置,那么您有 2^20 * 5 * 7 = 36700160
可能的不同状态。某些版本的 OpenGL 有超过 300 个状态变量,其中许多不仅仅是布尔值。因此,试图列出每一个可能的状态是徒劳的。
Is it also event-driven?
没有!
What inputs affect the different states the machine can be in?
显式调用 OpenGL 的状态改变函数API;那和默认的初始状态。
Can hierarchical, OOP concepts be applied to this state machine?
没有。或者更确切地说,您可以尝试,但说实话,唯一真实的表示是单个 OpenGL 上下文 class 保存所有状态。
在 OpenGL 上下文中有一些东西,它们的行为就像具有自己状态的单个对象。例如纹理,但是这些对象与 OpenGL 上下文本身紧密相关,不能单独查看。
关于您的评论的个人说明
My brain tends to favor structuring concepts in hierarchical, object-oriented, component-like ways.
别这样了! 计算机不是那样工作的。 OOP 是一种构建项目的方法,但它不是理解某些已有系统的好工具。尤其是那些本质上是非继承制的系统。
我强烈建议您通过学习其他构建项目的方法来扩大您的视野。学习 函数式编程 怎么样? Haskell 将成为目前流行的 FP 语言之一,并且由于它专注于成为一种纯语言,因此学习体验非常好。这是一个非常好的在线 book/tutorial:http://learnyouahaskell.com/chapters – 准备好让你的思想弯曲;有些东西看起来像是 OOP,但实际上不是; 运行 靠在那些墙上似乎令人沮丧,但当它最终在你的脑海中响起时,好处和 "you get it"(= 顿悟)是真正值得的。
请注意,OpenGL 既不是功能性的,也不是真正的 OOP。由于不是函数式的,它不能很好地映射到纯函数式编程中。 Haskell 有 OpenGL 绑定,但它们是通过称为 "monad" 的东西实现的; monads 是函数式程序用来联系状态 或 甚至驱动环境的东西。
我推荐的另一个资源是 https://mitpress.mit.edu/sicp/full-text/book/book.html – 每个程序员都应该读过它。
老实说,我发现以面向对象编程的方式理解这一点的最佳方式
根据我对 opengl 的了解,这里是我对 opengl api 是什么的最好解释
首先,如果您将它与其他 api(例如 SDL)进行比较,它可能更加复杂和不同,使用 api 来尝试描述它确实不公平
所以在面向对象编程方面
想象一下,如果你有一个 class ,这个 class 是私有的,它的所有工作方法都不打算被用户查看,但是它也有一个默认状态,这样如果你使用它 "right out of the box" 它会做它在默认状态下所做的事情(几乎什么都没有)
当您创建上下文时,就像您创建了此 class 的一个实例(并且您可以根据需要创建任意多个此 class 的实例,即您还可以创建多个上下文) 这是 运行 opengl 所必需的,而在 OOP 中你现在不能使用 class 直到你从它创建一个 object/instance ,唯一的区别是构造函数采用你的 hardware/os/other api/hardware 信息来完成 instance/object 以便它可用,或者将它插入一些 "screen"
然而,这个 class 中包含的方法(函数)和字段(数据存储)使您能够使用它做任何您想做的事情并使用这个实例,当然如何你做某些事情是基于你不能改变这个 class/instance/object 的任何东西而不使用它的 methods/fields(你被允许使用的那些 public ), 你不能只说我想自己做某事,或者选择存储字段的位置(外部等),所有这些都必须属于实例和 class 和方法,并且在这个 instance/object/class 有数百种方法和字段(opengl 4.5 有超过 500 个命令(methods/fields)会影响此 instance/class),无论您对它做什么,都会改变这个特定的instance/object 就好像你有一些粘土并将其塑造成你想要的样子,当然有些东西你必须从外部插入这个实例,例如具有特定功能的着色器
所以它真的有点像电视屏幕的遥控器,它有它的 methods/buttons 影响根据你在 [=40 上按下的内容显示的上下文 (screen/tv/monitor) =] 与此类比的不同之处在于,它不仅需要按下按钮,还需要数据以及您可以呈现它的所有各种方式(这就是您在屏幕上绘制的内容)
从我的观点来看,"state machine" 的表达在这里具有误导性。当我听到 "state machine" 时,我会自动想到具有少量已定义状态的有限自动机。它始终处于其中一种状态并对某些输入做出反应以确定下一个状态和输出将是什么。
但这不是 OpenGL 所做的。 (从数学上讲,您可以将其视为这样的自动机,但如果这样做,您会人为地创建大量可能的状态,这对您没有帮助)。
是的,OpenGL API 是 "stateful" API。你告诉它很多它记得在它的 "state" 里面的东西,它基本上是一组变量。然后,您要求它渲染一些东西,OpenGL 将使用您之前设置的状态来确定要对要渲染的数据执行的操作。
顺便说一下:我发现 Joey de Vries 的教程对于理解 OpenGL 的功能非常有帮助。有关详细信息,请参阅 https://learnopengl.com/Getting-started/OpenGL。