OSG渲染状态管理

来源:互联网 发布:剑灵男召唤师捏脸数据 编辑:程序博客网 时间:2024/06/05 06:34

转载:点击打开链接

1. 简介

OpenGL是一个状态机,在OpenGL中如果设置一种状态(比如设置当前的绘制颜色:glColor,那么实际上是设置了OpenGL颜色绘制状态为某种颜色,如果后续没有调用glColor去修改颜色,那么之后所有绘制的对象都使用之前设置的颜色。),在没有修改它的时候,该状态就会一直保持。在OpenGL使用中的状态有以下两种方式:

  1. 仅仅通过glEnable和glDisable开启和关闭状态 
    这里面也有两种不同的取值: 
    1.1 不涉及和其他状态相关,比如GL_DITHER、GL_POINT_SPRITE等; 
    1.2 涉及和其他状态的联动,比如GL_ALPHA_TEST、GL_BLEND等,需要使用glEnable开启状态

  2. 和1.2种的某种状态对应,但是需要设置状态的值(比如glAlphaFunc、glBlendFunc)

在OSG中对OpenGL的状态进行了封装,在OSG中称第一种情形为Mode(模式),称第二种情况为Attribute(属性),使用osg::StateAttribute来封装OpenGL的状态。

2. OSG的状态

在OSG中涉及到状态管理的类有3个,分别是 osg::State, osg::StateSet, osg::StateAttribute,下面就这三个类的关系以及它们的使用方式做一些介绍:

  1. osg::State 
    这个类从某种程度上来说就是OpenGL的状态机的封装。它主要封装了当前OpenGL的模式、属性和顶点数组的设置,并且提供了一种lazy state updating的效果(只有当必须修改时,才修改状态),它也提供了查询当前OpenGL状态的函数。除此之外,osg::State还提供了以下特性: 
    (1)它使用了栈的方式来管理状态,一般来说很少需要我们来修改栈操作 
    (2)提供了glGet来获取当前的OpenGL状态 
    osg::State是一个非常大的类,从它的定义的代码中就可以看出来。通过查看它的成员变量,可以知道它包含的主要管理的状态包括:
        ModeMap                   _modeMap;        AttributeMap              _attributeMap;        TextureModeMapList        _textureModeMapList;        TextureAttributeMapList   _textureAttributeMapList;
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

这里面包含两类(模式和属性),其中对于纹理也分了两种(纹理模式和纹理属性),针对纹理额外分出来是因为OpenGL中的多重纹理的扩展,纹理可以使用多个,多个纹理可以相互作用最终影响物体渲染的效果,而普通的模式和属性没有这一特点。

2.1 模式的设置

根据我们上面的讨论,Mode只需要设置开启、关闭两种状态,因此它的记录比较简单,从感性上分析来说,如果用来记录OpenGL中所有的Mode,那么应该是一系列的键值对,也就是某一个模式和它开启和关闭状态的值,类似于 
[ GL_LINGHT:true, GL_BLEND:false, …]这种样子。这是我们感性上的认识。在OSG中的实现略有不同,它考虑了状态的lazy updating的特性,因此对应的值是一个ModeStack,但是从原理上来说和我们的设想是一致的,都是提供一个值而已。

typedef std::map<StateAttribute::GLMode,ModeStack> ModeMap;
  • 1
  • 1

这其中StateAttribute::GLMode定义如下:(它的取值就是可以被设置在glEnable和glDisable中的枚举值,比如GL_LIGHTING, GL_BLEND, GL_DEPTH_TEST这样的值)

        /** GLMode is the value used in glEnable/glDisable(mode) */        typedef GLenum          GLMode;
  • 1
  • 2
  • 1
  • 2

2.2 属性的设置

对于属性的设置,就有点麻烦了,因为OpenGL中的属性的取值很不一样,有的属性多,有的属性少,有的属性和模式有关联(比如光照计算的属性,必须开启了光照计算的模式,之后设置的光照计算的属性才能生效),有的属性和模式没有关联,这样封装起来就有点麻烦。

OSG对此的封装如下:所有的需要设置属性的类都从osg::StateAttribute继承,通过下图可以看出osg封装的属性的类型(osg 3.4.0版本) 
StateAttributePicture

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;        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

默认情况下返回值是false,也就是说没有和它相关联的Mode,如果存在和它相关联的Mode,那么就返回该Mode值,比如osg::Point属性,用来设置点的大小属性,它的实现如下:

        virtual bool getModeUsage(StateAttribute::ModeUsage& usage) const        {            usage.usesMode(GL_POINT_SMOOTH);            return true;        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

从函数实现可以知道与它相关联的模式(Mode)是GL_POINT_SMOOTH,那么在State中的 
AttributeMap _attributeMap; 
到底是什么呢?我们从感性上来说应该就是所有OpenGL的属性的集合,那么它是怎么记录属性值的呢?先看看它的定义:

 typedef std::map<StateAttribute::TypeMemberPair,AttributeStack> AttributeMap;
  • 1
  • 1

可以看到它是一个Map,那么对应的键值对是什么呢,先看看键:

  /** Simple pairing between an attribute type and the member within that attribute type group.*/        typedef std::pair<Type,unsigned int> TypeMemberPair;
  • 1
  • 2
  • 1
  • 2

这里的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 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

调试中查看到的TypeMemberPair的变量:

TypeMemberPair
对于没有索引号的属性,TypeMemberPair的第二个成员都是0

  1. 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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

可以看到osg::StateSet就是一个Node和Drawable模式和属性的合集。 
ModeList的定义如下:

 typedef std::map<StateAttribute::GLMode,StateAttribute::GLModeValue>  ModeList;
  • 1
  • 1

看起来非常的自然,就是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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

也就是说,AttributeList的类型是:

Map [<Type, int> : <StateAttirbute, OverrideValue>]
  • 1
  • 1

尝试着写一个Attribute的值:

<POINT,  0> : <osg::Point(),  ON>
  • 1
  • 1

在osg::StateSet中提供了许多函数用来获取某个特定的模式(Mode)和属性(Attribute),并且可以对它们进行必要的修改。比如:

AttributeList& getAttributeList(); //获取和节点绑定的StateSet中的所有属性设置ModeList& getModeList()//获取和节点绑定的StateSet中的所有模式设置
  • 1
  • 2
  • 1
  • 2

其他的许多修改和获取函数可以参考该类的API手册。

  1. osg::StateAttribute 
    这个类就是OpenGL具体的某一个状态的封装,在osg::State的讨论中已经给出了osg所有继承于osg::StateAttribute的子类。它们就是所有OpenGL的状态的组成。集成于osg::StateAttribute的类都需要实现下面的一个虚函数:
 virtual void apply(State&) const {}
  • 1
  • 1

查看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}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

这个类将状态传递给了OpenGL的状态机osg::State,最终对渲染的结果产生影响。当我们自定义OSG的状态时,需要重写这个函数,并且在里面写上对状态改变的一些函数。

参考文献:

  1. 最长的一帧26:关于渲染状态机的一些讨论
  2. OpenGL glEnable&glDisable