OSG 全面支持 OpenGL的光照特性,包括材质属性(material property),光照属性(light property)和光照模型(lighting model)。与OpenGL相似,OSG中的光源也是不可见的,而非渲染一个灯泡或其他自然形状。同样,光源会创建着色效果,但是并不创建阴影——osgShadow可以用来创建阴影。
如果要在用户程序中使用光照,需要遵循下面的步骤:
- 指定几何体法线。
- 允许光照并设置光照状态。
- 指定光源属性并关联到场景图形。
- 指定表面材质属性。
只有几何体数据中设有单位长度法线时,才可以实现正确的光照。在大多数3D API中,法线数据必须单位化以得到正确的结果。注意缩放变 换的动作会改变法线的长度。如果你的Geometry对象中已经包含了单位长度的法线数组,但是光照的计算结果过于明亮或过于暗淡,那么这一现象可能是缩放变换造成的。最有效的解决方案是在StateSet中允许法线的重放缩模式。
osg::StateSet* state = geode->setOrCreateStateSet();
state->setMode(GL_RESCALE_NORMAL, osg::StateAttribute::ON);
与 OpenGL中相同,这一特性可以保证法线在均匀放缩变换时仍然保持单位长度。如果场景中的放缩变换是非均匀的,那么你可以允许法线归一化模式,以保证法线为单位长度。
osg::StateSet* state = geode->setOrCreateStateSet();
state->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
由于要进行法线的重新放缩,归一化模式往往会耗费大量的时间。编程时要尽量避免。
要在 OSG 中获得光照效果,你需要允许光照并至少允许一个光源。程序 osgviewer在缺省情况下就是这样做的,它在根节点的StateSet中已经设置了相应的模式。下面的代码段用于允许光照并为根节点的StateSet允许两个光源(GL_LIGHT0 和 GL_LIGHT1)。
osg::StateSet* state = root->getOrCreateStateSet();
state->setMode(GL_LIGHTING,osg::StateAttribute::ON);
state->setMode(GL_LIGHT0, osg::StateAttribute::ON);
state->setMode(GL_LIGHT1, osg::StateAttribute::ON);
OSG 还提供了从 StateAttribute 派生的 osg::LightModel 属性,用以控制全局的环境颜色,局部视图,双面光照,以及分离镜面颜色(separate specular color)等 OpenGL 特性。
要在场景中添加一个光源,可以创建一个osg::Light对象以定义光源参数。 然后将Light添加到一个osg::LightSource 节点中,并将 LightSource节点添加到场景图形。LightSource 是一个包含了唯一的 Light定义的高效的组节点。而由 Light定义的光源将对整个场景产生影响。
OSG 支持最多八个光源,从GL_LIGHT0 到GL_LIGHT7,这与你的OpenGL版本也有关系。你可以使用前述的setMode()方法来允许这些光源。如果要把一个Light 对象与OpenGL的光源联系起来,可以使用设置光的位置数的方法。
例如要把一个Light对象与GL_LIGHT2相关联,则设置位置数为2:
// 创建一个 Light 对象来控制 GL_LIGHT2 的参数。
osg::ref_ptr<osg::Light> light = new osg::Light;
light->setLightNum(2);
缺省情况下光源的位置数为 0。
Light 类实现了OpenGL中glLight()命令的大部分功能。用户程序可以使用其方法设置光的环境色,散射颜色,镜面反射颜色。用户可以创建点光,直线光或者锥光,也可以指定衰减参数,使得光的密度根据距离的不同逐渐削减。下面 的代码创建了一个 Light 对象并设置了一些常用的参数。
// 创建一个白色的锥光光源。
osg::ref_ptr<osg::Light> light = new osg::Light;
light->setAmbient(osg::Vec4(.1f, .1f, .1f, 1.f));
light->setDiffuse(osg::Vec4(.8f, .8f, .8f, 1.f));
light->setSpecular(osg::Vec4(.8f, .8f, .8f, 1.f));
light->setPosition( osg::Vec3( 0.f, 0.f, 0.f ));
light->setDirection( osg::Vec3( 1.f, 0.f, 0.f ));
light->setSpotCutoff(25.f );
当一个 OpenGL 程序使用glLight()来设置光的位置时,OpenGL根据当前的模型-视图(model-view)矩阵来变换位置。在 OSG 中,这一概念被称为位置状态(positional state)。在拣选(cull)遍历中,OSG向一个位置状态容器中追加各种位置状态量,以保证在绘制(draw)遍历中所有的变换都是正确的。
许多新的OSG开发师会错误地以为LightSource的子节点会自动设置发光。事实上并非如此。OSG根据场景图形中当前的渲染状态来实现光照,而非根据LightSource节点的层次关系。用户必须允许GL_LIGHTING和至少一个光源(例如,GL_LIGHT0),才能实现场景图形的照明。
用户可以把LightSource看作是包含了一个单一Light对象的叶节点。但是,用户也可以向LightSource节点添加子节点,因为LightSource继承自Group类。一般来说,用户程序可以将用于渲染灯光的自然形体的几何体关联为LightSource的子节点。
要添加Light对象到场景中,首先要创建一个LightSource 节点,将Light添加到LightSource中,并将LightSource关联到场景图形中。灯光的位置可以由LightSource节点在场景图形中的位置决定。OSG根据当前LightSource节点的变 换状态来改变灯光的位置。OSG程序开发师往往将LightSource关联为MatrixTransform的子节点,以控制灯光的位置。
状态属性类osg::Material封装了OpenGL的glMaterial()和 glColorMaterial()指令的函数功能。要在用户应用程序中设置材质属性,首先要创建一个Material对象,设置颜色和其它参数,然后关联到场景图形的StateSet中。
Material 类允许用户设置全局、散射、镜面反射和放射材质的颜色,以及镜面和高光指数参数。Material类定义了枚 举量FRONT,BACK和FRONT_AND_BACK,以便用户程序为几何体的正面和背面的设置材质属性。
与 OpenGL 类似,此处镜面指数的值必须在1.0到128.0至间,除非用户指定了拓宽这一范围的方法。
在进行很多OpenGL的操作时,直接设置材质属性可能会过于耗费资源。而一种名为颜色跟踪材质(color material)的特性,可以允许用户程序通过改变当前颜色的方法,自动改变某一特定的材质属性。在许多情形下,这一操作比直接修改材质属性的效率要更高,还能加强光照场景和无光照场景的联系,并满足应用程序对材质的需要。
要允许颜色跟踪材质的特性,需要调用Material:: setColorMode()方法。Material类为之定义了枚举量 AMBIENT,DIFFUSE,SPECULAR,EMISSION,AMBIENT_AND_DIFFUSE以及OFF。缺省情况下,颜色跟踪模式被设置为OFF, 颜色跟踪材质被禁止。如果用户程序设置颜色跟踪模式为其它的值,那么OSG将为特定的材质属性开启颜色跟踪材质特性,此时主颜色的改变将会改变相应的材质属性。下面的代码段将允许颜色跟踪材质,此时几何体正面的环境材质和散 射材质颜色将自动跟踪当前颜色的改变而改变。
osg::StateSet* state = node->getOrCreateStateSet();
osg::ref_ptr<osg::Material> mat = new osg::Material;
mat->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE );
state->setAttribute(mat.get());
注意,根据颜色跟踪模式的取值不同,Material类会自动允许或禁止GL_COLOR_MATERIAL。因此用户程序不需要调用 setAttributeAndModes()来允许或禁止相关的模式值。