osg利用矩阵投影在平面上产生阴影

Posted airduce

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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();

    
}

总结:得到的矩阵与当前矩阵相乘,就是投影。

以上是关于osg利用矩阵投影在平面上产生阴影的主要内容,如果未能解决你的问题,请参考以下文章

3D光照阴影 平面阴影矩阵推导及代码实现

线性代数之——子空间投影

移动端实时阴影+自投影技术实现

谁知道photoshop中怎么使图片产生阴影效果

剔除适用于从视图投影矩阵中提取平面,但不适用于投影矩阵

如何将 3D 协方差矩阵投影到给定的图像平面(姿势)