快速学习OSG(6)——立方图纹理(天空盒)

一、立方图

立方图纹理是一种特殊的技术,它是-一个由6幅二维图像构成的、以原点为中心的纹理立方体。对于每个片段而言,纹理坐标(S,T,R)都被 当作-个方向向量来看待,每个纹理单元表示从原点所看到的纹理立方体的东西。立方图纹理应用非常广泛,可以利用它来实现环境贴图、反射和光照等效果。

立方图纹理的功能与其他的纹理操作是相互独立的,不要认为它是一种特殊的纹理。因此,立方图纹理可以使用其他标准的纹理特性,如多重纹理、纹理边框等。但需要注意的是,立方图纹理渲染时需要很大的内存,是通常二维纹理的6倍左右。在为其指定纹理数据和创建纹理对象时,应该把它作为一个整体来处理,而不是作为单个的面来指定。立方图纹理技术实现需要的主要类是osg::TextureCubeMap,它继承自osg:Texture类,封装了OpenGL中的立方图纹理函数的功能。

二、类

从继承关系中可以看出,osg:TextureCubeMap 同样继承自osg:StateAttribute 因此,可以使用设

置渲染属性的方式来启用立方图纹理,代码如下:

//设置立方图纹理
osg:TextureCubeMap* skymap = osg:TextureCubeMapO;
stateset->setTextureAttributeAndModes(0,skymap.osg:StateAttribute::ON osg:tatcAtribute:OVERRIDE);

创建天空盒的方法有很多,可以直接绘制一个,当然效果是非常不好的。实际中,经常使用的天空盒包括两种,即方体天空盒和球体天空盒。更多的是球体天空盒的应用,看起来更真实,更能描述真实的天空效果。创建球体天空盒的主要步骤如下:

(1)创建立方图纹理对象,读取立方图纹理贴图。

(2)设置自动生成纹理坐标。

(3)设置纹理矩阵。

(4)设置立方图纹理。

(5)设置矩阵变换节点,以实现合适的矩阵变换。

(6)关闭光照并关闭背面剔除,设置渲染顺序,加入到叶节点中绘制。

在上面的步骤中有几点需要注意的地方:团t读取立方图纹理贴图时,需要特别注意的是,纹理贴图要与立方体的各个面(+x, -x, +y,-Y, +Z, -Z) 一一对应,否则,生成的天空盒会非常难看。

POSITIVE X-0, //Left
X正方向
NEGATIVE X=1,
//Right
X负方向
POSITIVE Y-2,
//Front
Y正方向
NEGATIVE Y=3,
//Back
Y负方向
POSITIVE Z=4, //Up
Z正方向
NEGATIVE Z-5
//Down
Z负方向

一定要设置关闭背面剔除及光照,并设置正确的渲染顺序。为了实现更真实的效果,通常天空盒只会根据视点做适当的变换,这就需要继承变换类实现-一个新的变换类,下面示例中会有详细说明,可仔细体会。

三、案例

#include<osg/Vec3>
#include<osg/Vec4>
#include<osg/Quat>
#include<osg/Matrix>
#include<osg/ShapeDrawable>
#include<osg/Geode>
#include<osg/Geometry>
#include<osg/Transform>
#include<osg/Material>
#include<osg/NodeCallback>
#include<osg/Depth>
#include<osg/CullFace>
#include<osg/TexMat>
#include<osg/TexGen>
#include<osg/TexEnv>
#include<osg/TextureCubeMap>
#include<osgDB/WriteFile>
#include<osgDB/ReadFile>
#include<osgUtil/Optimizer>
#include<osgUtil/CullVisitor>
#include <osgViewer/Viewer>
//读取立方图
osg::ref_ptr<osg::TextureCubeMap> readCubeMap()
{
    osg::ref_ptr<osg::TextureCubeMap> cubemap = new osg::TextureCubeMap;

    osg::ref_ptr<osg::Image> imagePosX = osgDB::readImageFile("Images/1.jpg");
    osg::ref_ptr<osg::Image> imageNegX = osgDB::readImageFile("Images/2.jpg");
    osg::ref_ptr<osg::Image> imagePosY = osgDB::readImageFile("Images/3.jpg");
    osg::ref_ptr<osg::Image> imageNegY = osgDB::readImageFile("Images/4.jpg");
    osg::ref_ptr<osg::Image> imagePosZ = osgDB::readImageFile("Images/5.jpg");
    osg::ref_ptr<osg::Image> imageNegZ = osgDB::readImageFile("Images/6.jpg");
    if (imagePosX.get() && imageNegX.get() && imagePosY.get() &&
        imageNegY.get() && imagePosZ.get() && imageNegZ.get())
    {
        //设置立方图的六个面的贴图
        cubemap->setImage(osg::TextureCubeMap::POSITIVE_X, imagePosX.get());
        cubemap->setImage(osg::TextureCubeMap::NEGATIVE_X, imageNegX.get());
        cubemap->setImage(osg::TextureCubeMap::POSITIVE_Y, imagePosY.get());
        cubemap->setImage(osg::TextureCubeMap::NEGATIVE_Y, imageNegY.get());
        cubemap->setImage(osg::TextureCubeMap::POSITIVE_Z, imagePosZ.get());
        cubemap->setImage(osg::TextureCubeMap::NEGATIVE_Z, imageNegZ.get());

        //设置纹理环绕
        cubemap->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
        cubemap->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
        cubemap->setWrap(osg::Texture::WRAP_R, osg::Texture::CLAMP_TO_EDGE);

        //设置滤波;线性和Mipmap
        cubemap->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR);
        cubemap->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
    }
    return cubemap.get();
}
//更新立方图纹理
struct TexMatCallback :public osg::NodeCallback
{
public:
    TexMatCallback(osg::TexMat& tm): _texMat(tm)
    {
        //int
    }
    virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
    {
        osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>(nv);
        if (cv)
        {
            //得到模型试图并且设置选转角度
            const osg::Matrix& MV = *(cv->getModelViewMatrix());
            const osg::Matrix R = osg::Matrix::rotate(osg::DegreesToRadians(112.0f), 0.0f, 0.0f, 1.0f)*
                osg::Matrix::rotate(osg::DegreesToRadians(90.0f), 1.0f, 0.0f, 0.0f);

                osg::Quat q = MV.getRotate();
                const osg::Matrix C = osg::Matrix::rotate(q.inverse());
                //设置纹理矩阵
                _texMat.setMatrix(C * R);
        }
        traverse(node, nv);
    }
    //纹理矩阵
    osg::TexMat& _texMat;
};

//一个变化类,使天空盒绕视点旋转
class MoveEarthSkyWithEyePointTransform :public osg::Transform
{
public:
    //局部矩阵计算成世界矩阵
    virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix, osg::NodeVisitor* nv)const
    {
        osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>(nv);
        if (cv)
        {
            osg::Vec3 eyePointLocal = cv->getEyeLocal();
            matrix.preMult(osg::Matrix::translate(eyePointLocal));
        }
        return true;
    }
    //世界矩阵计算为局部矩阵
    virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix, osg::NodeVisitor* nv)const
    {
        osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>(nv);
        if (cv)
        {
            osg::Vec3 eyePointLocal = cv->getEyeLocal();
            matrix.preMult(osg::Matrix::translate(-eyePointLocal));
        }
        return true;
    }
};
//创建天空盒
osg::ref_ptr<osg::Node> creatSkyBox()
{
    osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet();
    //设置纹理映射方式,指定为替代方法,即纹理中的颜色代替原来的颜色
    osg::ref_ptr<osg::TexEnv> te = new osg::TexEnv;
    te->setMode(osg::TexEnv::REPLACE);
    stateset->setTextureAttributeAndModes(0, te.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
    //自动生成纹理坐标
    /*NORMAL_MAP 标准模型-立方纹理
    REFLECTION_MAP 反射模型-球体纹理
    SPHERE_MAP  球体模型-球体纹理
    */
    osg::ref_ptr<osg::TexGen> tg = new osg::TexGen;
    tg->setMode(osg::TexGen::NORMAL_MAP);
    stateset->setTextureAttributeAndModes(0, te.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);

    //设置纹理矩阵
    osg::ref_ptr<osg::TexMat> tm = new osg::TexMat;
    stateset->setTextureAttribute(0, tm.get());

    //设置立方图的纹理
    osg::ref_ptr<osg::TextureCubeMap> skymap = readCubeMap();
    stateset->setTextureAttributeAndModes(0, skymap.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
    stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
    stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);

    //将深度设置为远平面
    osg::ref_ptr<osg::Depth> depth = new osg::Depth;
    depth->setFunction(osg::Depth::ALWAYS);
    depth->setRange(1.0, 1.0);//远平面
    stateset->setAttributeAndModes(depth, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
    //设置渲染顺序为-1,先渲染
    stateset->setRenderBinDetails(-1, "RenderBin");
    osg::ref_ptr<osg::Drawable> drawable = new osg::ShapeDrawable(new osg::Sphere(osg::Vec3(0.0f, 0.0f,0.0f), 1 ));

    //吧球体加入到叶子节点中
    osg::ref_ptr<osg::Geode> geode = new osg::Geode;
    geode->setCullingActive(false);
    geode->setStateSet(stateset.get());
    geode->addDrawable(drawable.get());

    //转置变化
    osg::ref_ptr<osg::Transform>transform = new MoveEarthSkyWithEyePointTransform();
    transform->setCullingActive(false);
    transform->addChild(geode.get());

    osg::ref_ptr<osg::ClearNode> clearnode = new osg::ClearNode;
    clearnode->setCullCallback(new TexMatCallback(*tm));
    clearnode->addChild(transform.get());

    return clearnode.get();
}

int main()
{
    //创建Viewer对象,场景浏览器
    osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
    //创建场景组节点
    osg::ref_ptr<osg::Group> root = new osg::Group();

    //加入天空盒
    root->addChild(creatSkyBox());


    //优化场景数据
    osgUtil::Optimizer optimizer;
    optimizer.optimize(root.get());
    //设置场景数据
    viewer->setSceneData(root.get());
    //初始化并创建窗口
    viewer->realize();
    //viewer->setUpViewInWindow(200, 200, 800, 800);
    viewer->run();
    return 0;

}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小气鬼944

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值