osg示例解析之osgLight(1)

1. 简介

osgLight示例演示了osg如何使用光照。

2. 描述

首先看一下osgLight的运行效果图,如下图所示:
osgLight

3. 创建立方体

3.1 绘制一个面

首先是创建场景中的立方体,在创建中需要创建立方体的每一个面,实现在函数 createWall中,createWall的函数原型如下

osg::Geometry* createWall(const osg::Vec3& v1,const osg::Vec3& v2,const osg::Vec3& v3,osg::StateSet* stateset)

调用它的代码如下所示:

    // create front side.
    geode->addDrawable(createWall(bb.corner(0),
                                  bb.corner(4),
                                  bb.corner(1),
                                  wall));

可以知道它是通过某个节点的包围盒来创建的,那么包围盒的corner分别是哪些点呢?也就是说这里的corner(0)到corner(7)分别指的是包围盒的哪些。查看源码可以得知:

//BoundingBox源码
inline const vec_type corner(unsigned int pos) const
{
 return vec_type(pos&1?_max.x():_min.x(),pos&2?_max.y():_min.y(),pos&4?_max.z():_min.z());
}

也就是说corner(0)到corner(7)遵循x、y、z从小到大排列,同时x优先y优先z的顺序,具体来说corner(0-7)如下图所示:

Corner顺序

通过上图以及createWall被调用的情况(前面是 0、4、1),我们可以知道该函数 createWall中的前三个参数 v1、v2、v3分别指的是一个四边形的 左下点、左上点和右下点。(如果这点不清除那么代码阅读起来会有很大的障碍)通过阅读createWall的代码可以知道,该函数主要是用来绘制立方体的一个面,这个面被细分成100x100个四边形小方格来绘制。

3.2 绘制立方体

通过6个面的绘制组装成一个立方体,立方体绘制的代码如下:

    osg::StateSet* rootStateSet = new osg::StateSet;
    root->setStateSet(rootStateSet);

    osg::StateSet* wall = new osg::StateSet;
    wall->setMode(GL_CULL_FACE,osg::StateAttribute::ON);

    osg::StateSet* floor = new osg::StateSet;
    floor->setMode(GL_CULL_FACE,osg::StateAttribute::ON);

    osg::StateSet* roof = new osg::StateSet;
    roof->setMode(GL_CULL_FACE,osg::StateAttribute::ON);

    osg::Geode* geode = new osg::Geode;

    // create front side.
    geode->addDrawable(createWall(bb.corner(0),
                                  bb.corner(4),
                                  bb.corner(1),
                                  wall));

    // right side
    geode->addDrawable(createWall(bb.corner(1),
                                  bb.corner(5),
                                  bb.corner(3),
                                  wall));

    // left side
    geode->addDrawable(createWall(bb.corner(2),
                                  bb.corner(6),
                                  bb.corner(0),
                                  wall));
    // back side
    geode->addDrawable(createWall(bb.corner(3),
                                  bb.corner(7),
                                  bb.corner(2),
                                  wall));

    // floor
    geode->addDrawable(createWall(bb.corner(0),
                                  bb.corner(1),
                                  bb.corner(2),
                                  floor));

    // roof
    geode->addDrawable(createWall(bb.corner(6),
                                  bb.corner(7),
                                  bb.corner(4),
                                  roof));

绘制六个面(设置了面的拣选,让背面被剔除掉),这些面绘制顺序是让背面朝向外,设置之后可以看到立方体内部。

3.3 绘制光源

场景中的光源绘制是在createLights函数中完成的,这个函数绘制了两个光源,一个光源固定在立方体的左上角(corner(4))的位置,另一个光源是一个移动的光源,移动的路径是立方体的8个角点组成的。

3.4 模型动画

osgLight中添加了一个模型,通过该模型计算出一个包围盒,模型中添加了动画效果,主要是通过ModelTransformCallback实现的。实现如下:

class ModelTransformCallback : public osg::NodeCallback
{
    public:

        ModelTransformCallback(const osg::BoundingSphere& bs)
        {
            _firstTime = 0.0;
            _period = 4.0f;
            _range = bs.radius()*0.5f;
        }

        virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
        {
            osg::PositionAttitudeTransform* pat = dynamic_cast<osg::PositionAttitudeTransform*>(node);
            const osg::FrameStamp* frameStamp = nv->getFrameStamp();
            if (pat && frameStamp)
            {
                if (_firstTime==0.0)
                {
                    _firstTime = frameStamp->getSimulationTime();
                }

                double phase = (frameStamp->getSimulationTime()-_firstTime)/_period;
                phase -= floor(phase);
                phase *= (2.0 * osg::PI);

                osg::Quat rotation;
                rotation.makeRotate(phase,1.0f,1.0f,1.0f);

                pat->setAttitude(rotation);

                pat->setPosition(osg::Vec3(0.0f,0.0f,sin(phase))*_range);
            }

            // must traverse the Node's subgraph
            traverse(node,nv);
        }

        double _firstTime;
        double _period;
        double _range;
};

主要是修改位置姿态节点osg::PositionAttitudeTransform类,该类与osg::MatrixTransform类似,主要区别在于osg::PositionAttitudeTransform比较直观,通过设置缩放、平移量和旋转实现。它的矩阵 Matrix = SRT(顺序是缩放、旋转、平移),在NodeCallback中一直去修改平移和旋转实现动画的效果。

osg::MatrixTransform和osg::PositionAttitudeTransform的几点区别:
1. osg::MatrixTransform按照我们给的顺序组合各种变换,比如我们先定义平移、在定义旋转和缩放,那么MatrixTransform的矩阵是
M = TRS,也就是所它会严格按照我们指定的顺序执行操作。指定的顺序不同时产生的结果差异巨大。
2. osg::PositionAttitudeTransform同时几个接口来获得平移、旋转和缩放矩阵,分别是:

1.setPosition指定平移量
2.setAttitude指定旋转量
3.setScale指定缩放量

不管我们设置的顺序如何,它的结果矩阵M=SRT(先缩放再旋转最后平移)总是按固定的顺序进行。除此之外osg::PositionAttitudeTransform还可以设置旋转和缩放的轴心位置(一般来说旋转和缩放是以原点为轴心进行的),但是PositionAttitudeTransform提供了让我们以其他位置为轴心进行旋转和缩放。

  1. 如果我们想用MatrixTranform实现与PositionAttitudeTransform同样的效果,那么就必须设置三个矩阵相乘的顺序是SRT。如果PositionAttitudeTransform设置了轴心位置,那么在作平移和旋转变换的时候还需要注意应该先平移到轴心位置,再旋转(和缩放),再平移回去。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值