1. 简介
OpenGL是一个状态机,在OpenGL中如果设置一种状态(比如设置当前的绘制颜色:glColor,那么实际上是设置了OpenGL颜色绘制状态为某种颜色,如果后续没有调用glColor去修改颜色,那么之后所有绘制的对象都使用之前设置的颜色。),在没有修改它的时候,该状态就会一直保持。在OpenGL使用中的状态有以下两种方式:
仅仅通过glEnable和glDisable开启和关闭状态
这里面也有两种不同的取值:
1.1 不涉及和其他状态相关,比如GL_DITHER、GL_POINT_SPRITE等;
1.2 涉及和其他状态的联动,比如GL_ALPHA_TEST、GL_BLEND等,需要使用glEnable开启状态和1.2种的某种状态对应,但是需要设置状态的值(比如glAlphaFunc、glBlendFunc)
在OSG中对OpenGL的状态进行了封装,在OSG中称第一种情形为Mode(模式),称第二种情况为Attribute(属性),使用osg::StateAttribute来封装OpenGL的状态。
2. OSG的状态
在OSG中涉及到状态管理的类有3个,分别是 osg::State, osg::StateSet, osg::StateAttribute,下面就这三个类的关系以及它们的使用方式做一些介绍:
- osg::State
这个类从某种程度上来说就是OpenGL的状态机的封装。它主要封装了当前OpenGL的模式、属性和顶点数组的设置,并且提供了一种lazy state updating的效果(只有当必须修改时,才修改状态),它也提供了查询当前OpenGL状态的函数。除此之外,osg::State还提供了以下特性:
(1)它使用了栈的方式来管理状态,一般来说很少需要我们来修改栈操作
(2)提供了glGet来获取当前的OpenGL状态
osg::State是一个非常大的类,从它的定义的代码中就可以看出来。通过查看它的成员变量,可以知道它包含的主要管理的状态包括:
ModeMap _modeMap;
AttributeMap _attributeMap;
TextureModeMapList _textureModeMapList;
TextureAttributeMapList _textureAttributeMapList;
这里面包含两类(模式和属性),其中对于纹理也分了两种(纹理模式和纹理属性),针对纹理额外分出来是因为OpenGL中的多重纹理的扩展,纹理可以使用多个,多个纹理可以相互作用最终影响物体渲染的效果,而普通的模式和属性没有这一特点。
2.1 模式的设置
根据我们上面的讨论,Mode只需要设置开启、关闭两种状态,因此它的记录比较简单,从感性上分析来说,如果用来记录OpenGL中所有的Mode,那么应该是一系列的键值对,也就是某一个模式和它开启和关闭状态的值,类似于
[ GL_LINGHT:true, GL_BLEND:false, …]这种样子。这是我们感性上的认识。在OSG中的实现略有不同,它考虑了状态的lazy updating的特性,因此对应的值是一个ModeStack,但是从原理上来说和我们的设想是一致的,都是提供一个值而已。
typedef std::map<StateAttribute::GLMode,ModeStack> ModeMap;
这其中StateAttribute::GLMode定义如下:(它的取值就是可以被设置在glEnable和glDisable中的枚举值,比如GL_LIGHTING, GL_BLEND, GL_DEPTH_TEST这样的值)
/** GLMode is the value used in glEnable/glDisable(mode) */
typedef GLenum GLMode;
2.2 属性的设置
对于属性的设置,就有点麻烦了,因为OpenGL中的属性的取值很不一样,有的属性多,有的属性少,有的属性和模式有关联(比如光照计算的属性,必须开启了光照计算的模式,之后设置的光照计算的属性才能生效),有的属性和模式没有关联,这样封装起来就有点麻烦。
OSG对此的封装如下:所有的需要设置属性的类都从osg::StateAttribute继承,通过下图可以看出osg封装的属性的类型(osg 3.4.0版本)
OSG通过StateAttribute提供的函数来找到与Attribute相关联的模式(Mode)
/** Return the modes associated with this StateAttribute.*/
virtual bool getModeUsage(ModeUsage&) const
{
// default to no GLMode's associated with use of the StateAttribute.
return false;
}
默认情况下返回值是false,也就是说没有和它相关联的Mode,如果存在和它相关联的Mode,那么就返回该Mode值,比如osg::Point属性,用来设置点的大小属性,它的实现如下:
virtual bool getModeUsage(StateAttribute::ModeUsage& usage) const
{
usage.usesMode(GL_POINT_SMOOTH);
return true;
}
从函数实现可以知道与它相关联的模式(Mode)是GL_POINT_SMOOTH,那么在State中的
AttributeMap _attributeMap;
到底是什么呢?我们从感性上来说应该就是所有OpenGL的属性的集合,那么它是怎么记录属性值的呢?先看看它的定义:
typedef std::map<StateAttribute::TypeMemberPair,AttributeStack> AttributeMap;
可以看到它是一个Map,那么对应的键值对是什么呢,先看看键:
/** Simple pairing between an attribute type and the member within that attribute type group.*/
typedef std::pair<Type,unsigned int> TypeMemberPair;
这里的type就是所有OpenGL封装的属性的类型,它定义如下:
enum Type {
TEXTURE, POLYGONMODE, POLYGONOFFSET, MATERIAL,
ALPHAFUNC, ANTIALIAS, COLORTABLE, CULLFACE,
FOG, FRONTFACE, LIGHT, POINT,
LINEWIDTH, LINESTIPPLE, POLYGONSTIPPLE, SHADEMODEL,
TEXENV, TEXENVFILTER, TEXGEN, TEXMAT,
LIGHTMODEL, BLENDFUNC, BLENDEQUATION, LOGICOP,
STENCIL, COLORMASK, DEPTH, VIEWPORT,
SCISSOR, BLENDCOLOR, MULTISAMPLE, CLIPPLANE,
COLORMATRIX, VERTEXPROGRAM, FRAGMENTPROGRAM, POINTSPRITE,
PROGRAM, CLAMPCOLOR, HINT, SAMPLEMASKI,
PRIMITIVERESTARTINDEX, CLIPCONTROL, VALIDATOR, VIEWMATRIXEXTRACTOR,
OSGNV_PARAMETER_BLOCK, OSGNVEXT_TEXTURE_SHADER, OSGNVEXT_VERTEX_PROGRAM, OSGNVEXT_REGISTER_COMBINERS,
OSGNVCG_PROGRAM, OSGNVSLANG_PROGRAM, OSGNVPARSE_PROGRAM_PARSER, UNIFORMBUFFERBINDING,
TRANSFORMFEEDBACKBUFFERBINDING, ATOMICCOUNTERBUFFERBINDING, PATCH_PARAMETER, FRAME_BUFFER_OBJECT,
VERTEX_ATTRIB_DIVISOR, SHADERSTORAGEBUFFERBINDING, CAPABILITY = 100
}
TypeMemberPair对应的第二个变量int值,对某些存在索引标号的属性有关(比如光照计算存在第0到第7号光源,这里的int值就对应0到7中的某一个值。除此之外还有裁剪面也存在Plane0到Plane7,但是这种带索引号的属性不是特别多),这样就可以区分出所有Attribute属性中哪一个变量的值是什么了。比如下面代码,当程序添加了两个裁剪面时,查看对应的TypeMemberPair值可以看到标号是0和1.
osg::ClipPlane *p0 = new osg::ClipPlane();
p0->setClipPlane(1, 0, 0, -10);
p0->setClipPlaneNum(0);
geometry->getOrCreateStateSet()->setAttribute(p0);
osg::ClipPlane *p1 = new osg::ClipPlane();
p1->setClipPlane(1, 0, 0, -50);
p1->setClipPlaneNum(1);
geometry->getOrCreateStateSet()->setAttribute(p1);
调试中查看到的TypeMemberPair的变量:
对于没有索引号的属性,TypeMemberPair的第二个成员都是0
- osg::StateSet
这个类StateSet只保存了OpenGL状态的一小部分,在OSG场景中的每个节点和可绘制对象(Drawable)都包含一个StateSet,一个StateSet可以被多个Node和Drawable共享。这个类记录的状态信息会在节点遍历的过程中添加到osg::State中,从而对场景中的节点的渲染起到作用。
osg::State和osg::StateSet有点类似于总公司和分公司的关系,osg::StateSet相当于贴近场景渲染Node和Drawable的基层分公司,它记录Node和Drawable中的状态信息(比如这个节点是否开启光照计算、是否开启混合、混合和光照的参数设置值),一般来说各个分公司记录的信息都不一样,并且相对来说不会记录完整的状态信息(因为很少有节点设置所有的OpenGL状态,大部分还都是默认的)。 之后分公司会把捕获到的这些信息反馈给总公司(osg::State),最后由总公司了解到一个渲染对象(Node和Drawable)的完整的状态,并最终正确的渲染它。
查看osg::StateSet的源码,主要看它的成员变量中记录的信息,我们感兴趣的几个信息如下:
ModeList _modeList;
AttributeList _attributeList;
TextureModeList _textureModeList;
TextureAttributeList _textureAttributeList;
可以看到osg::StateSet就是一个Node和Drawable模式和属性的合集。
ModeList的定义如下:
typedef std::map<StateAttribute::GLMode,StateAttribute::GLModeValue> ModeList;
看起来非常的自然,就是Mode和对应的取值。
AttributeList的定义如下:
/** Simple pairing between an attribute type and the member within that attribute type group.*/
typedef std::pair<Type,unsigned int> TypeMemberPair;
/** Simple pairing between an attribute and its override flag.*/
typedef std::pair<ref_ptr<StateAttribute>,StateAttribute::OverrideValue> RefAttributePair;
/** a container to map <StateAttribyte::Types,Member> to their respective RefAttributePair.*/
typedef std::map<StateAttribute::TypeMemberPair,RefAttributePair> AttributeList;
也就是说,AttributeList的类型是:
Map [<Type, int> : <StateAttirbute, OverrideValue>]
尝试着写一个Attribute的值:
<POINT, 0> : <osg::Point(), ON>
在osg::StateSet中提供了许多函数用来获取某个特定的模式(Mode)和属性(Attribute),并且可以对它们进行必要的修改。比如:
AttributeList& getAttributeList(); //获取和节点绑定的StateSet中的所有属性设置
ModeList& getModeList()//获取和节点绑定的StateSet中的所有模式设置
其他的许多修改和获取函数可以参考该类的API手册。
- osg::StateAttribute
这个类就是OpenGL具体的某一个状态的封装,在osg::State的讨论中已经给出了osg所有继承于osg::StateAttribute的子类。它们就是所有OpenGL的状态的组成。集成于osg::StateAttribute的类都需要实现下面的一个虚函数:
virtual void apply(State&) const {}
查看osg::Point这个状态类的实现:
void Point::apply(State& state) const
{
#ifdef OSG_GL_FIXED_FUNCTION_AVAILABLE
glPointSize(_size);
const GLExtensions* extensions = state.get<GLExtensions>();
if (!extensions->isPointParametersSupported)
return;
extensions->glPointParameterfv(GL_POINT_DISTANCE_ATTENUATION, (const GLfloat*)&_distanceAttenuation);
extensions->glPointParameterf(GL_POINT_FADE_THRESHOLD_SIZE, _fadeThresholdSize);
extensions->glPointParameterf(GL_POINT_SIZE_MIN, _minSize);
extensions->glPointParameterf(GL_POINT_SIZE_MAX, _maxSize);
#else
OSG_NOTICE<<"Warning: Point::apply(State&) - not supported."<<std::endl;
#endif
}
这个类将状态传递给了OpenGL的状态机osg::State,最终对渲染的结果产生影响。当我们自定义OSG的状态时,需要重写这个函数,并且在里面写上对状态改变的一些函数。
参考文献: