一、光照简介
OSG 全面支持
OpenGL
的光照特性,包括材质属性(
material property
),光照属性(
light property
)和光照模型(
lighting model
)。与
OpenGL
相似,
OSG
中
的光源也是不可见的,而非渲染一个灯泡或其他自然形状。同样,光源会创建着
色效果,但是并不创建阴影——
osgShadow
可以用来创建阴影。
如果要在用户程序中使用光照,需要遵循下面的步骤:
1 指定几何体法线。
2
允许光照并设置光照状态。
3 指定光源属性并关联到场景图形。
4指定表面材质属性。
本节针对上面的每一步进行讲解。
二、法线
只有几何体数据中设有单位长度法线时,才可以实现正确的光照。
“叶节点(
Geode
)和几何信息”已经对法线数组的设置和绑定到
Geometry
对象
作了介绍。
在大多数
3D API
中,法线数据必须单位化以得到正确的结果。注意缩放变
换的动作会改变法线的长度。如果你的
Geometry
对象中已经包含了单位长度的
1
法线数组,但是光照的计算结果过于明亮或过于暗淡,那么这一现象可能是缩放
变换造成的。最有效的解决方案是在
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 );
下面的部分将叙述如何控制独立的光源属性,例如它的位置和颜色,以及
OpenGL
颜色跟踪材质的特性(包括便面材质颜色的设置)。
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 );
要添加 Light
对象到场景中,首先要创建一个
LightSource
节点,将
Light
添
加
LightSource
中,并将
LightSource
关联到场景图形中。灯光的位置可以由
LightSource
节点在场景图形中的位置决定。
OSG
根据当前
LightSource
节点的变
换状态来改变灯光的位置。
五、案例
#include <osgViewer/Viewer>
#include <osg/Node>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Group>
#include <osg/Camera>
#include <osg/Light>
#include <osg/LightSource>
#include <osg/BoundingSphere>
#include <osg/BoundingBox>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgGA/StateSetManipulator>
#include <osgUtil/Optimizer>
//向场景中添加光源
osg::ref_ptr<osg::Group> createLight(osg::ref_ptr<osg::Node> node)
{
osg::ref_ptr<osg::Group> lightRoot = new osg::Group();
lightRoot->addChild(node);
//开启光照
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet();
stateset = lightRoot->getOrCreateStateSet();
stateset->setMode(GL_LIGHTING, osg::StateAttribute::ON);
stateset->setMode(GL_LIGHT0, osg::StateAttribute::ON);
//计算包围盒
osg::BoundingSphere bs;
node->computeBound();
bs = node->getBound();
//创建一个Light对象
osg::ref_ptr<osg::Light> light = new osg::Light();
light->setLightNum(0);
//设置方向
light->setDirection(osg::Vec3(0.0f, 1.0f, 0.0f));
//设置位置
light->setPosition(osg::Vec4(bs.center().x(), bs.center().y(), bs.center().z() + bs.radius(), 1.0f));
//设置环境光的颜色
light->setAmbient(osg::Vec4(1.0f, .0f, .0f, 1.0f));
//设置散射光颜色
light->setDiffuse(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f));
//设置恒衰减指数
light->setConstantAttenuation(1.0f);
//设置线形衰减指数
light->setLinearAttenuation(0.0f);
//设置二次方衰减指数
light->setQuadraticAttenuation(0.0f);
//创建光源
osg::ref_ptr<osg::LightSource> lightSource = new osg::LightSource();
lightSource->setLight(light);
lightRoot->addChild(lightSource);
return lightRoot;
}
int main()
{
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
osg::ref_ptr<osg::Group> root = new osg::Group();
//读取模型
osg::ref_ptr<osg::Node> node = osgDB::readNodeFile("cow.osg");
//向场景中添加光源
root->addChild(createLight(node));
//root->addChild(node.get());
//优化场景数据
osgUtil::Optimizer optimizer;
optimizer.optimize(root);
//设置场景数据
viewer->setSceneData(root);
//事件响应类,对渲染状态进行控制,按w在体/线/点模式间切换,
// 按l在照明与非照明模式下切换,按b在是否开启背面剔除模式下切换,按t在是否开启纹理的情况下切换
viewer->addEventHandler(new osgGA::StateSetManipulator(viewer->getCamera()->getOrCreateStateSet()));
//初始化并创建窗口
viewer->realize();
//开始渲染
viewer->run();
return 0;
}
也许大家会有一个疑问,为什么光照须有法线?
解释:光照必须实在一个模型或者物体的有发现的基础上区形成的,也就是说如果没有法线的画,其光照也是不会有的。
后续继续更新..........