我要发帖 回复

高级会员

109

主题

1366

积分

0

专家分

OSG-VR-GIS

忠于职守杰出贡献

:

私信
发表时间 : 2007-9-22 15:52:08 | 浏览 : 6852    评论 : 3
osgshadow示例程序的简介及阴影效果实现(转)

本文的目的是通过对示例程序osgshadow的详细注解和分析,学习OSG阴影库osgShadow的基本使用方法,并进行程序的适当注解和知识总结。
详细的源代码请参阅OSG 2.0发行版的附带示例程序examples/osgshadow.cpp。本文只是针对其中的重点代码进行注解和分析,文中所述的程序代码并不完整,基本无法直接执行。
首先简要地介绍一下osgShadow库的几个基本组件:
osgShadow::ShadowedScene:向场景中添加阴影节点的组节点类。它继承自osg::Group。
osgShadow::ShadowTechnique:用于实现各种阴影技法的基类。它派生出三个子类:ShadowMap,ShadowTexture和ShadowVolume。
osgShadow::ShadowMap:实现阴影贴图(shadow map)的阴影技法类,其原理与示例程序osgdepthshadow中的实现相同。
osgShadow::ShadowTexture:实现阴影纹理(shadow texture)的阴影技法类,其原理与示例程序osgshadowtexture中的实现相同。
osgShadow::ShadowVolume:实现基于模板缓存的体积阴影(volume shadow)的阴影技法类。

在观察osgshadow的源代码之前,首先可以了解一下这个例子程序的用法。
osgshadow基本演示了osgShadow库中所有可实现的阴影种类,并提供了多种用于承载阴影的几何环境,在运行osgshadow程序时,可以使用下面的命令行参数:
-h,--help:显示帮助文档(但是并不完全)。
--SingleThreaded,--CullDrawThreadPerContext,--DrawThreadPerContext,
--CullThreadPerCameraDrawThreadPerContext:
启用不同的线程模型
--positionalLight,--directionalLight:设置为位置光源还是方向光源,使用位置光源时,光源在场景上方。
--noUpdate:设置此参数则光源不会转动。
--screen (num):num表示要在哪一个显示器上显示。
--sv (--two-sided | --two-pass),--st:
--sv设置使用osgShadow::ShadowVolume实现基于模板缓存的体积阴影,并有双面和双通道两个选择(未完全实现);
--st设置使用osgShadow::ShadowTexture,使用-2可以清楚地观察到它与osgShadow::ShadowMap的区别;
缺省下使用osgShadow::ShadowMap。
--base:是否再添加一个地面。
--coloured-light:是否采用有色光源,否则只采用白色系的光照。
-1,-2,-3:采用不同的几何环境,包括一个立方体,飞机与地形图,以及多个几何体的组合。
--with-base-texture,--no-base-texture:是否给-3中的模型赋予纹理,缺省为有纹理。

这里顺便介绍一下,关于渲染时各个线程模型的意义:
SingleThreaded - 单CPU,单显示器时使用;
DrawThreadPerContext - 多CPU,单显示器时使用,以增加帧延迟时间为代价,提高屏幕同步刷新率;
CullThreadPerCameraDrawThreadPerContext - 摄像机和显示器的总数不大于CPU数;
CullDrawThreadPerContext - 上述情况之外,可以保证较低的帧延迟时间。
在DrawThreadPerContext和CullThreadPerCameraDrawThreadPerContext模式下,渲染遍历renderingTraversals()可能在绘制线程完成之前就会返回,此时如果用户修改STATIC类型的节点,几何体和渲染状态数据,就可能发生危险,必须使用setDataVariance(DYNAMIC)来指定数据类型。
用户可以在程序中随时使用osgViewer::Viewer::setThreadingModel函数来设置任意一个线程模型,不必拘泥于上面所述的限制。

示例程序的全局部分,定义了两个全局变量:
const int ReceivesShadowTraversalMask = 0x1; //标识阴影接收对象(例如地面)的掩码
const int CastsShadowTraversalMask = 0x2;   //标识阴影投射对象(例如飞机)的掩码。掩码可以做位运算
在后面的程序中,可以使用osgShadow::ShadowedScene::setReceivesShadowTraversalMask和setCastsShadowTraversalMask函数来设置阴影的接收和投射者标志;同时对要进行阴影运算的节点使用osg::Node::setNodeMask函数来设置其掩码,用户也可以使用掩码的位运算来设置某个节点同时为投射者和接收者。

下面的代码将示例程序中的主要实现代码作了总结和缩写,从中可以看出实现阴影效果的基本元素:
// 定义视口和模型文件
osgViewer::Viewer viewer;
osg::ref_ptr<osg::Node> model = osgDB::readNodeFiles(arguments);
model->setNodeMask(CastsShadowTraversalMask | ReceivesShadowTraversalMask);
// 定义新的阴影节点类,并设置接收和投射者标志
osg::ref_ptr<osgShadow::ShadowedScene> shadowedScene = new osgShadow::ShadowedScene;
shadowedScene->setReceivesShadowTraversalMask(ReceivesShadowTraversalMask);
shadowedScene->setCastsShadowTraversalMask(CastsShadowTraversalMask);
// 定义一个阴影技法类,比如下面代码所述的ShadowMap类,并指定给阴影节点
osg::ref_ptr<osgShadow::ShadowMap> sm = new osgShadow::ShadowMap;
shadowedScene->setShadowTechnique(sm.get());
// 定义一个新的光源,并设置其参数
osg::ref_ptr<osg::LightSource> ls = new osg::LightSource;
ls->getLight()->setPosition(lightpos);
ls->getLight()->setAmbient(osg::Vec4(1.0,0.0,0.0,1.0));
ls->getLight()->setDiffuse(osg::Vec4(0.0,1.0,0.0,1.0));
// 将光源和模型作为阴影节点的子节点加入,并设置视口的场景树
shadowedScene->addChild(model.get());
shadowedScene->addChild(ls.get());
viewer.setSceneData(shadowedScene.get());

上述代码即可实现一个简单的阴影效果。但是,由于OSG 2.0版的osgShadow库尚未完善,因此可以从中发现诸多问题,尤其是ShadowVolume类,稍加观察即可发现其中的缺陷。不过相信这些问题一定会在后继的版本中加以解决。由于osgShadow库的使用十分简单,且阴影效果在三维环境仿真中必不可少,相信这个全新的OSG库很快就会得到完善和大量的应用。

最近VR访客


OSG中国官方网站:http://www.osgChina.org
OSG中国官方讨论区:http://bbs.osgChina.org

OSG专业群-OSG地形研究者 49668412
      OSG地形专业研究 45763709
obuil 评论于2007-9-22 22:41:01
又被项目耽误了 osg 阴影 支持一下
Alexsander 评论于2007-9-23 17:10:27
支持下
season2005 评论于2007-11-9 14:46:52
能讲讲三个阴影深浅用什么函数来调节?我的场景实现的阴影好黑呀!

手机版|VR开发网 统计 津ICP备18009691号
网安备12019202000257

GMT+8, 2021-10-26 01:19 AM

返回顶部