OSG渲染状态管理

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;

这里面包含两类(模式和属性),其中对于纹理也分了两种(纹理模式和纹理属性),针对纹理额外分出来是因为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版本)
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;
        }

默认情况下返回值是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
对于没有索引号的属性,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;

可以看到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手册。

  1. 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的状态时,需要重写这个函数,并且在里面写上对状态改变的一些函数。

参考文献:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值