1. 程式人生 > >osg利用矩陣投影在平面上產生陰影

osg利用矩陣投影在平面上產生陰影

效果:

  

實現:

PlaneShadowMatrix.h

#include <osg/Matrix>
#include <osg/Geode>
class PlaneShadowMatrix {
public:
    /************************************************************************/
    /* 功能:獲取投影矩陣
       引數:vPoints[3] = 平面上不在一直線上的三個點,vLightPos = 光照的位置,destmat = 用來儲存矩陣。
       作者:一夢
       時間:2018-11-14 11:37:39
       修訂:無。
       修訂時間:無。
    
*/ /************************************************************************/ void getShadowMatrix(osg::Vec3 vPoints[3], osg::Vec4 vLightPos, osg::Matrix &destMat); };

PlaneShadowMatrix.cpp

#include "PlaneShadowMatrix.h"
void getPlaneEquation(osg::Vec3 &vPoint1, osg::Vec3 &vPoint2, osg::Vec3 &vPoint3, osg::Vec4 &vPlane);

void getNormalVector(const osg::Vec3 &vP1, const osg::Vec3 &vP2, const osg::Vec3 &vP3, osg::Vec4 &vNormal); void subtractVectors(const osg::Vec3 &vFirst, const osg::Vec3 &vSecond, osg::Vec3 &vResult); void vectorCrossProduct(const osg::Vec3 &vU, const osg::Vec3 &vV, osg::Vec4 &vResult);
void normalizeVector(osg::Vec4 &vNormal); float getVectorLength(const osg::Vec4 &vVector); float getVectorLengthSqrd(const osg::Vec4 &vVector); void scaleVector(osg::Vec4 &vVector, const GLfloat fScale); void PlaneShadowMatrix::getShadowMatrix(osg::Vec3 vPoints[3], osg::Vec4 vLightPos, osg::Matrix &destMat){ osg::Vec4 vPlaneEquation; float dot; getPlaneEquation(vPoints[0], vPoints[1], vPoints[2], vPlaneEquation); // Dot product of plane and light position dot =vPlaneEquation[0]*vLightPos[0] + vPlaneEquation[1]*vLightPos[1] + vPlaneEquation[2]*vLightPos[2] + vPlaneEquation[3]*vLightPos[3]; // Now do the projection // First column destMat(0,0) = dot - vLightPos[0] * vPlaneEquation[0]; destMat(1,0) = 0.0f - vLightPos[0] * vPlaneEquation[1]; destMat(2,0) = 0.0f - vLightPos[0] * vPlaneEquation[2]; destMat(3,0) = 0.0f - vLightPos[0] * vPlaneEquation[3]; // Second column destMat(0,1) = 0.0f - vLightPos[1] * vPlaneEquation[0]; destMat(1,1) = dot - vLightPos[1] * vPlaneEquation[1]; destMat(2,1) = 0.0f - vLightPos[1] * vPlaneEquation[2]; destMat(3,1) = 0.0f - vLightPos[1] * vPlaneEquation[3]; // Third Column destMat(0,2) = 0.0f - vLightPos[2] * vPlaneEquation[0]; destMat(1,2) = 0.0f - vLightPos[2] * vPlaneEquation[1]; destMat(2,2) = dot - vLightPos[2] * vPlaneEquation[2]; destMat(3,2) = 0.0f - vLightPos[2] * vPlaneEquation[3]; // Fourth Column destMat(0,3) = 0.0f - vLightPos[3] * vPlaneEquation[0]; destMat(1,3) = 0.0f - vLightPos[3] * vPlaneEquation[1]; destMat(2,3) = 0.0f - vLightPos[3] * vPlaneEquation[2]; destMat(3,3) = dot - vLightPos[3] * vPlaneEquation[3]; return ; } void getPlaneEquation(osg::Vec3 &vPoint1, osg::Vec3 &vPoint2, osg::Vec3 &vPoint3, osg::Vec4 &vPlane){ // Get normal vector from three points. The normal vector is the first three coefficients // to the plane equation... getNormalVector(vPoint1, vPoint2, vPoint3, vPlane); // Final coefficient found by back substitution vPlane[3] = -(vPlane[0] * vPoint3[0] + vPlane[1] * vPoint3[1] + vPlane[2] * vPoint3[2]); } void getNormalVector(const osg::Vec3 &vP1, const osg::Vec3 &vP2, const osg::Vec3 &vP3, osg::Vec4 &vNormal){ osg::Vec3 vV1, vV2; subtractVectors(vP2, vP1, vV1); subtractVectors(vP3, vP1, vV2); vectorCrossProduct(vV1, vV2, vNormal); normalizeVector(vNormal); } void subtractVectors(const osg::Vec3 &vFirst, const osg::Vec3 &vSecond, osg::Vec3 &vResult){ vResult[0] = vFirst[0] - vSecond[0]; vResult[1] = vFirst[1] - vSecond[1]; vResult[2] = vFirst[2] - vSecond[2]; } void vectorCrossProduct(const osg::Vec3 &vU, const osg::Vec3 &vV, osg::Vec4 &vResult){ vResult[0] = vU[1]*vV[2] - vV[1]*vU[2]; vResult[1] = -vU[0]*vV[2] + vV[0]*vU[2]; vResult[2] = vU[0]*vV[1] - vV[0]*vU[1]; } void normalizeVector(osg::Vec4 &vNormal){ float fLength = 1.0f / getVectorLength(vNormal); scaleVector(vNormal, fLength); } float getVectorLength(const osg::Vec4 &vVector){ return (GLfloat)sqrt(getVectorLengthSqrd(vVector)); } float getVectorLengthSqrd(const osg::Vec4 &vVector){ return (vVector[0]*vVector[0]) + (vVector[1]*vVector[1]) + (vVector[2]*vVector[2]); } void scaleVector(osg::Vec4 &vVector, const GLfloat fScale){ vVector[0] *= fScale; vVector[1] *= fScale; vVector[2] *= fScale; }

main.cpp

#include <osg/Texture2D>
#include <osg/Geometry>
#include <osg/Geode>
#include <osgDB/ReadFile>
#include <osgUtil/SmoothingVisitor>
#include <osgViewer/Viewer>
#include <osg/PositionAttitudeTransform>
#include <osg/MatrixTransform>
#include <osgGA/TrackballManipulator>
#include <osg/Material>

#include "PlaneShadowMatrix.h"

osg::Matrix shadowMarix;

osg::Vec4 lightPos(30.0f,-10.0f,30.0f,1.0f);

osg::Vec3 myarray[3];

//物體回撥函式,讓其不斷的旋轉
class RotateCallback : public osg::NodeCallback
{
public:
    RotateCallback() : _rotateZ(0.0) {}

    virtual void operator()( osg::Node* node, osg::NodeVisitor* nv )
    {
        osg::PositionAttitudeTransform* pat = dynamic_cast<osg::PositionAttitudeTransform*>( node );
        if ( pat )
        {
            osg::Quat quat( osg::DegreesToRadians(_rotateZ), osg::Z_AXIS );
            pat->setAttitude( quat );
            _rotateZ += 1.0;
        }

        
        traverse( node, nv );//訪問器的下一個節點
    }

protected:
    double _rotateZ;
};
//陰影回撥函式,讓其投影
class ShadowCallback : public osg::NodeCallback
{
public:
        ShadowCallback() : rotateZ(0.0) {}
    virtual void operator()( osg::Node* node, osg::NodeVisitor* nv )
    {
        osg::MatrixTransform * mt = dynamic_cast<osg::MatrixTransform*>( node );
        if ( mt )
        {
            mt->setMatrix(shadowMarix);
        }
        traverse( node, nv );//訪問器的下一個節點
    }
protected:
    double rotateZ;
};
//建立燈光
void createLight(osg::ref_ptr<osg::Group> lightRoot)
{
    //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);

    //建立一個Light物件
    osg::ref_ptr<osg::Light> light = new osg::Light();
    light->setLightNum(0);
    //設定方向
    light->setDirection(osg::Vec3(1.0f,1.0f,-1.0f));
    //設定位置
    light->setPosition(lightPos);
    //設定環境光的顏色
    light->setAmbient(osg::Vec4(0.01f,0.01f,0.01f,1.0f));
    //設定散射光的顏色
    light->setDiffuse(osg::Vec4(1.0f,1.0f,1.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.get());

    lightRoot->addChild(lightSource.get());

    return ;
}
//建立牆壁
osg::Drawable* createHouseWall()
{
    // House vertices
    osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
    //前面
    vertices->push_back( osg::Vec3( 0.0, 0.0, 4.0) );  // 0
    vertices->push_back( osg::Vec3( 0.0, 0.0, 0.0) );  // 1
    vertices->push_back( osg::Vec3( 4.0, 0.0, 4.0) );  // 2
    vertices->push_back( osg::Vec3( 4.0, 0.0, 0.0) );  // 3
    //右面
    vertices->push_back( osg::Vec3( 4.0, 4.0, 4.0) );  // 4
    vertices->push_back( osg::Vec3( 4.0, 4.0, 0.0) );  // 5
    //後面
    vertices->push_back( osg::Vec3( 0.0, 4.0, 4.0) );  // 6
    vertices->push_back( osg::Vec3( 0.0, 4.0, 0.0) );  // 7
    //左面
    vertices->push_back( osg::Vec3( 0.0, 0.0, 4.0) );  // 8
    vertices->push_back( osg::Vec3( 0.0, 0.0, 0.0) );  // 9
    
    // House normals
    osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array( 10 );
    //左前
    (*normals)[0].set(-1.0,-1.0, 0.0 );
    (*normals)[1].set(-1.0,-1.0, 0.0 );
    //右前
    (*normals)[2].set( 1.0,-1.0, 0.0 );
    (*normals)[3].set( 1.0,-1.0, 0.0 );
    //右後
    (*normals)[4].set( 1.0, 1.0, 0.0 );
    (*normals)[5].set( 1.0, 1.0, 0.0 );
    //左後
    (*normals)[6].set(-1.0, 1.0, 0.0 );
    (*normals)[7].set(-1.0, 1.0, 0.0 );
    //左前
    (*normals)[8].set(-1.0,-1.0, 0.0 );
    (*normals)[9].set(-1.0,-1.0, 0.0 );
    
    // House texture coordinates
    osg::ref_ptr<osg::Vec2Array> texcoords = new osg::Vec2Array( 10 );
    //前面的左0.3
    (*texcoords)[0].set( 0.0, 1.0 );
    (*texcoords)[1].set( 0.0, 0.0 );
    (*texcoords)[2].set( 1.0, 1.0 );
    (*texcoords)[3].set( 1.0, 0.0 );

    //右面0.2
       (*texcoords)[4].set( 0.0, 1.0 );
    (*texcoords)[5].set( 0.0, 0.0 );

    //後面0.3
    (*texcoords)[6].set( 1.0, 1.0 );
    (*texcoords)[7].set( 1.0, 0.0 );
    //左邊0.2
    (*texcoords)[8].set( 0.0, 1.0 );
    (*texcoords)[9].set( 0.0, 0.0 );

    // House texture coordinates
    /*osg::ref_ptr<osg::Vec2Array> texcoords2 = new osg::Vec2Array( 10 );


      //右面0.2

    (*texcoords2)[4].set( 0.0, 1.0 );
    (*texcoords2)[5].set( 0.0, 0.0 );
    

    //後面0.3
    (*texcoords2)[6].set( 1.0, 1.0 );
    (*texcoords2)[7].set( 1.0, 0.0 );
    //左邊0.2
    (*texcoords2)[8].set( 0.0, 1.0 );
    (*texcoords2)[9].set( 0.0, 0.0 );*/

    
    // Create wall geometry
    osg::ref_ptr<osg::Geometry> houseWall = new osg::Geometry;
    houseWall->setVertexArray( vertices.get() );
    houseWall->setTexCoordArray( 0, texcoords.get() );
    // houseWall->setTexCoordArray( 1, texcoords2.get() );
    houseWall->setNormalArray( normals.get() );
    houseWall->setNormalBinding( osg::Geometry::BIND_PER_VERTEX );
    houseWall->addPrimitiveSet( new osg::DrawArrays(osg::DrawArrays::QUAD_STRIP, 0, 10) );
    
    houseWall->getOrCreateStateSet()->setTextureAttributeAndModes( 0,new osg::Texture2D(osgDB::readImageFile("C:\\55.jpg")) );

    // houseWall->getOrCreateStateSet()->setTextureAttributeAndModes( 1,new osg::Texture2D(osgDB::readImageFile("C:\\55.jpg")) );

    return houseWall.release();
} 
//建立大地
osg::Geometry* createGround(){

    osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
    vertices->push_back(osg::Vec3(50,-50,-10));
    vertices->push_back(osg::Vec3(50,50,-5));
    vertices->push_back(osg::Vec3(-50,50,-5));
    vertices->push_back(osg::Vec3(-50,-50,-10));

    myarray[0] = osg::Vec3(50,-50,-9);
    myarray[1] = osg::Vec3(50,50,-4);
    myarray[2] = osg::Vec3(-50,50,-4);

    osg::ref_ptr<osg::Vec3Array> colours = new osg::Vec3Array;
    colours->push_back(osg::Vec3(255,255,0));
    colours->push_back(osg::Vec3(0,255,0));
    colours->push_back(osg::Vec3(0,255,0));
    colours->push_back(osg::Vec3(0,255,0));


    osg::Geometry *ground = new osg::Geometry;
    ground->setVertexArray(vertices);
    ground->addPrimitiveSet(new osg::DrawArrays(osg::DrawArrays::QUADS,0,4));
    ground->setColorArray(colours,osg::Array::BIND_PER_PRIMITIVE_SET);
    return ground;

}

int main( int argc, char** argv )
{
    
    osg::Group *root = new osg::Group;

    //物體
    osg::ref_ptr<osg::Geode> geode = new osg::Geode;
    geode->addDrawable( createHouseWall() );

    //影子
    osg::ref_ptr<osg::Geode> shadowGeode = new osg::Geode;
    shadowGeode->addDrawable( createHouseWall() );

    //旋轉物體
    osg::ref_ptr<osg::PositionAttitudeTransform> pat = new osg::PositionAttitudeTransform;
    pat->addChild( geode );
    pat->setUpdateCallback( new RotateCallback );

    //旋轉影子
    osg::ref_ptr<osg::PositionAttitudeTransform> shaDowPat = new osg::PositionAttitudeTransform;
    shaDowPat->addChild( shadowGeode );
    shaDowPat->setUpdateCallback( new RotateCallback );

    //投影
    osg::ref_ptr<osg::MatrixTransform> mt = new osg::MatrixTransform;
    mt->addChild( shaDowPat );
    mt->setUpdateCallback( new ShadowCallback );
    //mt->getOrCreateStateSet()->setMode(GL_DEPTH_TEST,osg::StateAttribute::OFF);
    mt->getOrCreateStateSet()->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
    osg::Material *mm = new osg::Material;
    mm->setAmbient(osg::Material::Face::FRONT,osg::Vec4(0.0,0.0,0.0,1.0));
    mm->setDiffuse(osg::Material::Face::FRONT,osg::Vec4(0.0,0.0,0.0,1.0));
    mm->setSpecular(osg::Material::Face::FRONT,osg::Vec4(0.0,0.0,0.0,1.0));
    mt->getOrCreateStateSet()->setAttribute(mm);


    root->addChild(mt);


    root->addChild(pat);
    

    osg::ref_ptr<osg::Group> lightRoot = new osg::Group;
    createLight(lightRoot);
    root->addChild(lightRoot.get());
    root->addChild(createGround());

    osgViewer::Viewer viewer;
    viewer.setUpViewInWindow(20,20,400,400);
    viewer.setSceneData(root);
    viewer.setCameraManipulator(new osgGA::TrackballManipulator);

    PlaneShadowMatrix psm;
    psm.getShadowMatrix(myarray,lightPos,shadowMarix);
    //makeShadowMatrix(myarray,lightPos,shadowMarix);
/*
    while (!viewer.done()){
        viewer.frame();
    }
    return 1; */
    return viewer.run();

    
}

總結:得到的矩陣與當前矩陣相乘,就是投影。