作为带有渲染功能的 SDK ,在完成某些特定需求的情况下,需要在游戏引擎(Unity、Unreal、Native 等)的线程里进行绘制。同时,绘制某些物体需要特定的 OpenGL 环境,因此不得不改变 OpenGL Context 。
然而,在 SDK 进行切换 OpenGL Context 时,并不清楚之前在 游戏引擎 中的 OpenGL Context State。理论上,应该由游戏引擎每一帧开始绘制时重新重置 OpenGL Context 环境状态。但是,仍然会存在一些问题,而且,在某些其他的场景下,可能我们也想实现在我们的渲染命令执行完成后,恢复初始的 OpenGL 环境。因此,接下来这个 GlStateSave 类实在是很不错的设计。
#ifndef GLSTATESAVE_H
#define GLSTATESAVE_H
#include "GlUtils.h"
class GLStateSave
{
public:
// If our code only uses VertexArrayObjects, we don't need to
// save and restore all the buffer related state
static const bool exclusivelyVAO = true;
GLStateSave()
{
glGetIntegerv( GL_BLEND, &Blend );
glGetIntegerv( GL_BLEND_DST_ALPHA, &BlendDstAlpha );
glGetIntegerv( GL_BLEND_DST_RGB, &BlendDstRGB );
glGetIntegerv( GL_BLEND_EQUATION_ALPHA, &BlendEquationAlpha );
glGetIntegerv( GL_BLEND_EQUATION_RGB, &BlendEquationRgb );
glGetIntegerv( GL_BLEND_SRC_ALPHA, &BlendSrcAlpha );
glGetIntegerv( GL_BLEND_SRC_RGB, &BlendSrcRGB );
// We update an array for debug graphs, so this needs to
// be saved even though we use VAO, because GL_ARRAY_BUFFER_BINDING
// isn't part of the VAO state.
glGetIntegerv( GL_ARRAY_BUFFER_BINDING, &ArrayBuffer );
if ( !exclusivelyVAO ) {
glGetIntegerv( GL_ELEMENT_ARRAY_BUFFER_BINDING, &ElementArrayBuffer );
for ( int i = 0 ; i < MAX_ATTRIBS ; i++ ) {
glGetVertexAttribiv( i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &vertexAttribArrayEnabled[i] );
}
}
glGetIntegerv( GL_SCISSOR_TEST, &ScissorTest );
glGetIntegerv( GL_SCISSOR_BOX, ScissorBox );
glGetIntegerv( GL_DEPTH_TEST, &DepthTest );
glGetIntegerv( GL_DEPTH_FUNC, &DepthFunc );
glGetIntegerv( GL_DEPTH_WRITEMASK, &DepthWriteMask );
glGetIntegerv( GL_CULL_FACE, &CullFace );
}
~GLStateSave()
{
glBlendEquationSeparate( BlendEquationRgb, BlendEquationAlpha );
glBlendFuncSeparate( BlendSrcRGB, BlendDstRGB, BlendSrcAlpha, BlendDstAlpha );
GL_Enable( GL_BLEND, Blend );
glBindBuffer( GL_ARRAY_BUFFER, ArrayBuffer );
if ( exclusivelyVAO ) {
glBindVertexArrayOES_( 0 );
} else {
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ElementArrayBuffer );
for ( int i = 0 ; i < MAX_ATTRIBS ; i++ ) {
if ( vertexAttribArrayEnabled[i] ) {
glEnableVertexAttribArray(i);
} else {
glDisableVertexAttribArray(i);
}
}
}
GL_Enable( GL_SCISSOR_TEST, ScissorTest );
glScissor( ScissorBox[0], ScissorBox[1], ScissorBox[2], ScissorBox[3] );
GL_Enable( GL_DEPTH_TEST, DepthTest );
glDepthFunc( DepthFunc );
glDepthMask( DepthWriteMask );
GL_Enable( GL_CULL_FACE, CullFace );
}
void GL_Enable( const GLenum feature, GLboolean enabled )
{
if ( enabled ) {
glEnable( feature );
} else {
glDisable( feature );
}
}
GLint Blend;
GLint BlendDstAlpha;
GLint BlendDstRGB;
GLint BlendEquationAlpha;
GLint BlendEquationRgb;
GLint BlendSrcAlpha;
GLint BlendSrcRGB;
GLint ElementArrayBuffer;
GLint ArrayBuffer;
static const int MAX_ATTRIBS = 4;
GLint vertexAttribArrayEnabled[MAX_ATTRIBS];
GLint ScissorTest;
GLint ScissorBox[4];
GLint DepthTest;
GLint DepthFunc;
GLint DepthWriteMask;
GLint CullFace;
};
#endif // GLSTATESAVE_H
这样一来,在渲染相关函数的中就可以这么写了:
void Boundary::DoRender(int eye, GLuint fbIdMid, GLuint texIdMid)
{
GLStateSave glState;
GL(glDepthMask(GL_FALSE));
GL(glDisable(GL_CULL_FACE));
bool needFlush = RenderSeeThrough(eye, 0, 0);
needFlush |= RenderBoundary(eye);
RenderBackGround(eye);
RenderDialog(eye);
if (needFlush)
GL(glFlush());
return ;
}
程序执行到 DoRender
之后,因为声明了 GLStateSave
类,所以会在执行 GL(glDepthMask(GL_FALSE))
之前先调用 GLStateSave
类的构建函数,记录所有 OpenGL Context,当程序从 DoRender
返回时,由于 glState
是个局部变量,所以就会自动释放,自动调用 GLStateSave
类的析构函数,去还原所有的 GL Context。