效果:
实现:
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(); }
总结:得到的矩阵与当前矩阵相乘,就是投影。