Opengl 每次我画东西的时候,我应该打电话给glEnable和glDisable吗?

Opengl 每次我画东西的时候,我应该打电话给glEnable和glDisable吗?,opengl,Opengl,我应该多久调用一次OpenGL函数,如glEnable()或glEnableClientState()及其对应的glDisable?它们是在应用程序开始时被调用一次,还是我应该禁用它们,只启用那些我在绘图时立即需要的功能?是否存在性能差异?“视情况而定” 如果您整个应用程序只使用一种启用/禁用状态组合,那么请务必在开始时设置它,然后继续 大多数现实世界的应用程序需要混合,然后你必须调用glEnable()以启用某些特定状态,执行draw调用,然后在完成“清除阶段”时再次调用glDisable()

我应该多久调用一次OpenGL函数,如
glEnable()
glEnableClientState()
及其对应的
glDisable
?它们是在应用程序开始时被调用一次,还是我应该禁用它们,只启用那些我在绘图时立即需要的功能?是否存在性能差异?

“视情况而定”

如果您整个应用程序只使用一种启用/禁用状态组合,那么请务必在开始时设置它,然后继续

大多数现实世界的应用程序需要混合,然后你必须调用
glEnable()
以启用某些特定状态,执行draw调用,然后在完成“清除阶段”时再次调用
glDisable()


状态排序、状态跟踪和许多优化方案都源于此,因为状态切换有时很昂贵。

如果您发现经常检查状态变量的值,然后调用glEnable/glDisable,则可以使用属性堆栈(glPushAttrib/glPopAttrib)稍微清理一下

属性堆栈允许您隔离代码的各个区域,以便对一个部分中的属性所做的更改不会影响其他部分中的属性状态

void drawObject1(){
  glPushAttrib(GL_ENABLE_BIT);

  glEnable(GL_DEPTH_TEST);
  glEnable(GL_LIGHTING);    

  /* Isolated Region 1 */

  glPopAttrib();
}        

void drawObject2(){
  glPushAttrib(GL_ENABLE_BIT);

  glEnable(GL_FOG);
  glEnable(GL_GL_POINT_SMOOTH);    

   /* Isolated Region 2 */

  glPopAttrib();
}    

void drawScene(){
  drawObject1();
  drawObject2();
}
尽管在drawObject1中设置了
GL_LIGHTING
GL_DEPTH_TEST
,但它们的状态不会保留到drawObject2。如果没有glPushAttrib,情况就不会如此。另外-请注意,在函数调用结束时不需要调用glDisable,glPopAttrib将完成此任务

就性能而言,对glEnable/glDisable的单个函数调用造成的开销最小。如果您需要处理大量的状态,您可能需要创建自己的状态管理器或多次调用glGetInteger。。。然后采取相应的行动。添加的机制和控制流可能会降低代码的透明度,更难调试,更难维护。这些问题可能会使其他更有成效的优化更加困难

属性堆栈可以帮助维护抽象层并创建隔离区域


我学到的一条经验法则说,随意启用/禁用几乎总是比检查当前状态和仅在需要时更改更便宜


也就是说,Marc的答案肯定是可行的。

首先,您使用哪个OpenGL版本?您的目标群体拥有哪一代图形硬件?知道了这一点,就更容易给出更正确的答案。我的回答假设OpenGL 2.1

OpenGL是一个状态机,这意味着每当一个状态被更改时,该状态都会变为“当前”,直到程序员通过一个新的OpenGL API调用再次显式更改为止。此规则存在例外,例如客户端状态数组调用使当前顶点颜色未定义。但这些都是定义规则的例外

“应用程序开始时一次”没有多大意义,因为有时您需要在应用程序仍在运行时销毁OpenGL上下文。我想你的意思是在每次创建窗口之后。这适用于以后不需要更改的状态。示例:如果所有绘制调用都使用相同的顶点数组数据,则不需要在以后使用glDisableClientState禁用它们

有许多与旧的固定功能管道相关联的启用/禁用状态。简单的补救方法是:使用着色器!如果你的目标是最长5年的一代卡,它可能会模仿固定功能管道与着色器。通过使用着色器,您可以或多或少地完全控制变换和光栅化阶段中发生的事情,并且您可以使用制服创建自己的“状态”,这对于更改/更新来说非常便宜

知道OpenGL是一个状态机,就像我上面所说的,应该清楚地表明,应该尽可能地将状态更改保持在最小。但是,除了启用/禁用状态调用之外,很可能还有其他因素对性能的影响更大。如果你想了解他们,请往下读


与旧的固定函数状态调用无关的状态开销,也不是简单的启用/禁用状态,在成本上可能有很大的不同。值得注意的是,链接着色器和绑定名称(“纹理、程序、缓冲区对象的名称”)通常相当昂贵。这就是为什么很多游戏和应用程序都会根据纹理对网格的绘制顺序进行排序。这样,它们就不必绑定同一纹理两次。然而,如今,同样的情况也适用于着色器程序。如果不一定要绑定同一着色器程序两次,则不需要绑定。此外,并非特定OpenGL版本中的所有功能在所有卡上都是硬件加速的,即使这些卡的供应商声称它们符合OpenGL。遵从性意味着他们遵循规范,而不是他们必须高效地运行所有特性。在这方面,应记住GL______;成像中的一些函数,如glHistogram和glMinMax


结论:除非有明显的原因,否则不要使用着色器!因为你可以用制服代替,所以它可以避免很多不必要的州政府电话。你知道,OpenGL着色器已经存在了大约六年了。此外,启用/禁用状态更改的开销可能是一个问题,但通常可以从优化其他更昂贵的状态更改中获得更多好处,如glUseProgram、glCompileShader、glLinkprogram、glBindBuffer和glBindTexture

另外:OpenGL 3.0删除了客户端状态启用/禁用调用。它们是隐式启用的,因为在此版本中绘制数组是唯一的方法。立即模式被删除。gl..指针调用也被删除,因为一个指针调用实际上只是一个指针调用