OSG学习:计算纹理坐标

在很多时候,直接指定纹理坐标是非常不方便的,如曲面纹理坐标,只有少数的曲面(如圆锥、圆柱等)可以在不产生扭曲的情况下映射到平面上,其他的曲面在映射到表面时都会产生一定程度的扭曲。一般而言,曲面表面的曲率越大,纹理所需要的扭曲度就越大。这时,直接指定纹理坐标可能是一件非常困难的事情了。
下面的示例,通过一个纹理坐标生成器(继承自osg::NodeVisitor访问器)遍历模型的所有顶点及法线,然后根据顶点、法线及一定的比例来确定纹理坐标。

#include <osgViewer/Viewer>

#include <osg/Node>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Group>
#include <osg/Camera>
#include <osg/MatrixTransform>
#include <osg/PositionAttitudeTransform>
#include <osg/TexGen>
#include <osg/TexEnv>
#include <osg/NodeVisitor>

#include <osgDB/ReadFile>
#include <osgDB/WriteFile>

#include <osgUtil/Optimizer>

#include <iostream>

//纹理坐标生成器,继承自NodeVisitor
class TexCoordGenerator: public osg::NodeVisitor 
{
public:
    //遍历所有的子节点
    TexCoordGenerator(): NodeVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN)
    {
        //
    }

    void apply(osg::Geode& geode)
    {
        //通过包围盒来确定合适的比例
        const osg::BoundingSphere &bsphere = geode.getBound();

        float scale = 10;

        if (bsphere.radius() != 0) 
        {
            scale = 5 / bsphere.radius();
        }

        //遍历所有几何体,并设置纹理坐标
        for (unsigned i=0; i<geode.getNumDrawables(); ++i) 
        {
            osg::Geometry* geo = dynamic_cast<osg::Geometry* >(geode.getDrawable(i));

            if (geo) 
            {
                osg::Vec2Array* tc = generate_coords(geo->getVertexArray(), geo->getNormalArray(), scale);

                geo->setTexCoordArray(0, tc);
            }
        }

        NodeVisitor::apply(geode);
    }

protected:

    //计算纹理坐标
    osg::Vec2Array* generate_coords(osg::Array* vx, osg::Array* nx, float scale)
    {
        osg::Vec2Array* v2a = dynamic_cast<osg::Vec2Array*>(vx);
        osg::Vec3Array* v3a = dynamic_cast<osg::Vec3Array*>(vx);
        osg::Vec4Array* v4a = dynamic_cast<osg::Vec4Array*>(vx);
        osg::Vec2Array* n2a = dynamic_cast<osg::Vec2Array*>(nx);
        osg::Vec3Array* n3a = dynamic_cast<osg::Vec3Array*>(nx);
        osg::Vec4Array* n4a = dynamic_cast<osg::Vec4Array*>(nx);

        osg::ref_ptr<osg::Vec2Array> tc = new osg::Vec2Array;
        for (unsigned i=0; i<vx->getNumElements(); ++i) {

            osg::Vec3 P;
            if (v2a) P.set((*v2a)[i].x(), (*v2a)[i].y(), 0);
            if (v3a) P.set((*v3a)[i].x(), (*v3a)[i].y(), (*v3a)[i].z());
            if (v4a) P.set((*v4a)[i].x(), (*v4a)[i].y(), (*v4a)[i].z());

            osg::Vec3 N(0, 0, 1);
            if (n2a) N.set((*n2a)[i].x(), (*n2a)[i].y(), 0);
            if (n3a) N.set((*n3a)[i].x(), (*n3a)[i].y(), (*n3a)[i].z());
            if (n4a) N.set((*n4a)[i].x(), (*n4a)[i].y(), (*n4a)[i].z());

            int axis = 0;
            if (N.y() > N.x() && N.y() > N.z()) axis = 1;
            if (-N.y() > N.x() && -N.y() > N.z()) axis = 1;
            if (N.z() > N.x() && N.z() > N.y()) axis = 2;
            if (-N.z() > N.x() && -N.z() > N.y()) axis = 2;

            osg::Vec2 uv;

            switch (axis) {
                case 0: uv.set(P.y(), P.z()); break;
                case 1: uv.set(P.x(), P.z()); break;
                case 2: uv.set(P.x(), P.y()); break;
                default: ;
            }

            tc->push_back(uv * scale);
        }
        return tc.release();
    }

};

//创建二维纹理状态对象
osg::ref_ptr<osg::StateSet> createTexture2DState(osg::ref_ptr<osg::Image> image)
{
    //创建状态集对象
    osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet();

    //创建二维纹理对象
    osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D();
    texture->setDataVariance(osg::Object::DYNAMIC);
    //设置贴图
    texture->setImage(image.get());

    texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR);
    texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
    texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
    texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);

    stateset->setTextureAttributeAndModes(0,texture.get(),osg::StateAttribute::ON);

    return stateset.get() ;
}

int main()
{
    osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();

    osg::ref_ptr<osg::Group> root = new osg::Group();

    //读取贴图文件
    osg::ref_ptr<osg::Image> image = osgDB::readImageFile("Images/primitives.gif");

    osg::ref_ptr<osg::Node> node = osgDB::readNodeFile("dumptruck.osg");

    //计算纹理坐标
    TexCoordGenerator tcg ;

    node->accept(tcg);

    //创建状态集对象
    osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet();
    stateset = createTexture2DState(image.get());

    //使用二维纹理
    node->setStateSet(stateset.get());

    root->addChild(node.get());

    //优化场景数据
    osgUtil::Optimizer optimizer ;
    optimizer.optimize(root.get()) ;

    viewer->setSceneData(root.get());

    viewer->realize();

    viewer->run();

    return 0 ;
}
posted @ 2017-07-24 15:20  huahai  阅读(1624)  评论(0编辑  收藏  举报