glsl镜面水倒影的实现[转]

http://blog.sina.com.cn/s/blog_78ea87380101ejbf.html

使用两相机,一个master相机, 主要负责场景的渲染, 另一个rtt相机, 和master相机建立为镜面投影相机,用于在和master相机的纵向镜像投影,从而获取master投影场景的逆场景, 渲染到纹理,进行镜面贴图,实现水面的倒影效果。

 
效果如图:

实现代码(需要一张天空背景图, 一张water法线图):
 
vertex shader:

varying vec3 lightdir;

varying vec3 eyedir;

varying vec3 normal;

varying vec3 halfvec;

varying vec4 ambient, diffuse, specular;

 

attribute vec3 tangent;

 

uniform float _time;

uniform vec3  lightDir;

 

//传入RTT相机模型投影矩阵,使用主要为不让纹理跟随主相机的变动而异常变动,

//可以使用gl_ProjectionMatrix * vVertex试试相应的效果,实验时需要把fragshader中的coord.y=1.0-coord.y打开;

uniform mat4 uRTTViewMatrix;

varying vec4 vRTTVertex;

void main()

{

         vec4 vVertex = gl_ModelViewMatrix * gl_Vertex;

         vRTTVertex = uRTTViewMatrix * gl_Vertex;

 

         vec3 tLightDir = lightDir; 

         lightdir = normalize(tLightDir - vVertex.xyz);

        

         vec3 tEyeDir = -vVertex.xyz;

         eyedir = normalize(tEyeDir);

 

         vec3 tNormal = normalize(gl_NormalMatrix * gl_Normal);

         normal = normalize(tNormal);

 

////

         ambient = vec4(1.0,1.0,1.0,1.0);

         diffuse = vec4(1.0,1.0,1.0,1.0);

         specular = vec4(1.0,1.0,1.0,1.0);

 

         float LdotN = max(0.0, dot(lightdir, normal));

         if(LdotN > 0.0)

         {

                   diffuse = LdotN ;

 

                   vec3 H = normalize(lightdir + eyedir);

                   float spec = max(0.0, dot(H, normal));

                   specular =  pow(spec, 16.0);

         }

 

////

         //切线空间;

         vec3 _tangent = normalize(gl_NormalMatrix * tangent);

         vec3 _bNormal = normalize(cross(normal, _tangent));

 

         lightdir.x = dot(lightdir, _tangent);

         lightdir.y = dot(lightdir, _bNormal);

         lightdir.z = dot(lightdir, normal);

         lightdir = normalize(lightdir);

 

         eyedir.x = dot(eyedir, _tangent);

         eyedir.y = dot(eyedir, _bNormal);

         eyedir.z = dot(eyedir, normal);

         eyedir = normalize(eyedir);

 

         halfvec = normalize(lightdir + eyedir);

 

         gl_TexCoord[0] = gl_MultiTexCoord0;

         gl_TexCoord[1].s = gl_TexCoord[0].x + _time * 0.05;

         gl_TexCoord[1].t = gl_TexCoord[0].y + _time * 0.05;

 

         gl_Position = ftransform();

}

 

fragment shader:

 

uniform sampler2D _baseTex;

uniform sampler2D _normTex;

 

varying vec3 lightdir;

varying vec3 eyedir;

varying vec3 normal;

varying vec3 halfvec;

 

varying vec4 ambient, diffuse, specular;

varying vec4 vRTTVertex;

 

void main()

{

         vec3 L = normalize(lightdir);

         vec3 E = normalize(eyedir);

         vec3 N = normalize(normal);

         vec3 H = normalize(halfvec);

 

         vec3 _normC = texture2D(_normTex, gl_TexCoord[1].xy).xyz;

 

         //vRTTVertex范围[-0.5 ,0.5] ,转换为[0.0,1.0];

         vec2 coord = (vRTTVertex.xy/vRTTVertex.w)*0.5 + 0.5;

         //触使镜面纹理波动效果;

         vec2 _tInc = vec2(0.0,0.0);

         _tInc.y = clamp(_normC.y * 0.00925, 0.0, 1.0) * 0.5 ; //适当调节参数,使上下摆动幅度;

         vec4 _color = texture2D(_baseTex, coord + _tInc);

        

         _normC = texture2D(_normTex, gl_TexCoord[1].xy + _normC * 0.05); 

         //将[0,1]范围转到[-1,1];

         _normC = normalize((_normC - vec3(0.5)) * 2.0);

        

         float LdotN = max(dot(L, _normC), 0.0);

         float HdotN = max(dot(H, _normC), 0.0);

 

         if(HdotN > 0.0)

         {

                   HdotN = pow(HdotN, 64.0);

         }

  

         gl_FragColor = vec4(ambient.xyz * _color.xyz + diffuse.xyz * LdotN * _color.xyz 

                                               + specular.xyz * HdotN, _color.a);

 

 

}

 
main程序:
 

//创建Quad;

osg::ref_ptr<osg::Geode> createQuad(int _w, int _h)

{

         osg::ref_ptr<osg::Geode> geode = new osg::Geode;

         osg::ref_ptr<osg::Geometry> geom = new osg::Geometry;

         geode->addDrawable(geom);

 

         osg::ref_ptr<osg::Vec3Array> vArr = new osg::Vec3Array;

         vArr->push_back(osg::Vec3(-_w/2.0,-_h/2.0,0.0));

         vArr->push_back(osg::Vec3(-_w/2.0,_h/2.0,0.0));

         vArr->push_back(osg::Vec3(_w/2.0,_h/2.0,0.0));

         vArr->push_back(osg::Vec3(_w/2.0,-_h/2.0,0.0));

 

         geom->setVertexArray(vArr);

 

         osg::ref_ptr<osg::Vec2Array> texArr = new osg::Vec2Array;

         texArr->push_back(osg::Vec2(0.0,0.0));

         texArr->push_back(osg::Vec2(0.0,1.0));

         texArr->push_back(osg::Vec2(1.0,1.0));

         texArr->push_back(osg::Vec2(1.0,0.0));

 

         geom->setTexCoordArray(0,texArr);

 

         geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,4));

 

         osg::ref_ptr<osg::Vec3Array> nArr = new osg::Vec3Array; 

         nArr->push_back(osg::Vec3d(0.0,0.0,-1.0));

         geom->setNormalArray(nArr);

         geom->setNormalBinding(osg::Geometry::BIND_OVERALL); 

 

         return geode;

}

 

 

osg::ref_ptr<osg::Camera> createRTTCamera(int x, int y, int w, int h)

{

         osg::ref_ptr<osg::Camera> _camera = new osg::Camera;

         _camera->setAllowEventFocus(false);

         _camera->setViewport(x, y, w, h);

         _camera->setClearColor(osg::Vec4(0.2,0.3,0.58,1.0));

         _camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

         _camera->setRenderOrder(osg::Camera::PRE_RENDER);

         _camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT);

         _camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);

         _camera->setProjectionMatrixAsPerspective(30.0, double(w)/double(h),0.1, 9999.89);

         _camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);

         return _camera;

}

 

osg::ref_ptr<osg::Camera> createCamera(int x, int y, int w, int h)

{

         osg::ref_ptr<osg::Camera> _camera = new osg::Camera;

         _camera->setAllowEventFocus(false);

         _camera->setViewport(x, y, w, h);

         _camera->setClearColor(osg::Vec4(0.1,0.6,0.48,1.0));

         _camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

         _camera->setRenderOrder(osg::Camera::POST_RENDER);

         _camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);

         _camera->setProjectionMatrixAsPerspective(30.0, double(w)/double(h),0.1, 9999.89);

         _camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);

         return _camera;

}

 

int main(int argc, char *argv[])

{

         osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;

         osg::ref_ptr<osg::Group> _root = new osg::Group;

 

         osg::ref_ptr<osg::Node> _water  = createQuad(10000,10000); //osgDB::readNodeFile("water2.3DS");

         osg::ref_ptr<osg::Node> _cessna = osgDB::readNodeFile("cessna.osg");

         osg::ref_ptr<osg::Node> _ceep = osgDB::readNodeFile("ceep.ive");

         osg::ref_ptr<osg::Node> _skyBox = HalfSphere::createHalfSphere(osgDB::readImageFile("sky-HXY2.jpg"), 5000.0);

 

         //水池;

         osg::ref_ptr<osg::MatrixTransform> _m0 = new osg::MatrixTransform;

         _m0->addChild(_water);

 

         //天空;

         osg::ref_ptr<osg::MatrixTransform> _m1 = new osg::MatrixTransform;

         _m1->addChild(_skyBox);

 

         //cessna172;

         osg::ref_ptr<osg::MatrixTransform> _m2 = new osg::MatrixTransform;

         _m2->addChild(_cessna);

         _m2->setMatrix(osg::Matrix::translate(osg::Vec3d(10.0, 10.0, 100.0)));

 

         //ceep;

         osg::ref_ptr<osg::MatrixTransform> _m3 = new osg::MatrixTransform;

         _m3->addChild(_ceep);

         _m3->setMatrix(osg::Matrix::translate(osg::Vec3d(-100.0,0.0,0)));

 

         //rtt相机;

         osg::ref_ptr<osg::Camera> _rttCam = createRTTCamera(0,0,1920,1080);

         _rttCam->addChild(_m1);

         _rttCam->addChild(_m2);

         _rttCam->addChild(_m3);

 

 

         osg::ref_ptr<osg::Camera> _mstCam = createCamera(0,0,1920,1080);

         _mstCam->addChild(_m0);

         _mstCam->addChild(_m1);

         _mstCam->addChild(_m2);

         _mstCam->addChild(_m3);

 

         _root->addChild(_rttCam);

         _root->addChild(_mstCam);

 

         //纹理绑定;

         osg::ref_ptr<osg::Texture2D> _tex = new osg::Texture2D;

         _tex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);

         _tex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);

 

         _tex->setTextureSize(1920,1080);

         _tex->setInternalFormat(GL_RGB);

         _rttCam->attach(osg::Camera::COLOR_BUFFER, _tex);

         _m0->getOrCreateStateSet()->setTextureAttributeAndModes(0, _tex, 1);

 

         osg::ref_ptr<osg::Texture2D> _tex1 = new osg::Texture2D;

         _tex1->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);

         _tex1->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);

         _tex1->setImage(osgDB::readImageFile("water.bmp"));

         _m0->getOrCreateStateSet()->setTextureAttributeAndModes(1, _tex1, 1);

         _m0->getOrCreateStateSet()->setMode(GL_LIGHTING , 0);

 

         //添加shader;

         osg::ref_ptr<osg::Program> program = new osg::Program;

         osg::ref_ptr<osg::Shader> vertShader = new osg::Shader(osg::Shader::VERTEX);

         if(!vertShader->loadShaderSourceFromFile("water.vert"))

         {

                   return -1;

         }

         osg::ref_ptr<osg::Shader> fragShader = new osg::Shader(osg::Shader::FRAGMENT);

         if(!fragShader->loadShaderSourceFromFile("water.frag"))

         {

                   return -1;

         }

         program->addShader(vertShader);

         program->addShader(fragShader);

 

         _m0->getOrCreateStateSet()->setAttribute(program,1);

         _m0->getOrCreateStateSet()->addUniform(new osg::Uniform("_baseTex", 0));

         _m0->getOrCreateStateSet()->addUniform(new osg::Uniform("_normTex", 1));

         _m0->getOrCreateStateSet()->addUniform(new osg::Uniform("_time", float(0.0)));//lightDir

         _m0->getOrCreateStateSet()->addUniform(new osg::Uniform("lightDir", osg::Vec3f(100.0, 100.0, 100.0)));

         _m0->getOrCreateStateSet()->addUniform(new osg::Uniform("uRTTViewMatrix", osg::Matrix::identity()));

 

         viewer->setSceneData(_root);

         viewer->setCameraManipulator(new osgGA::TrackballManipulator);

         viewer->getCamera()->setClearColor(osg::Vec4(0.8,0.8,0.7,1.0));

         while(!viewer->done())

         {

 

                   float _t = viewer->getCamera()->getView()->getFrameStamp()->getSimulationTime(); 

                   _m0->getOrCreateStateSet()->getUniform("_time")->set(_t);

                   _m0->getOrCreateStateSet()->getUniform("lightDir")->set(viewer->getCamera()->getViewMatrix().getTrans());

 

                   _mstCam->setViewMatrix(viewer->getCamera()->getViewMatrix());

                   //rtt相机正投倒影;

                   //_rttCam->setViewMatrix(viewer->getCamera()->getViewMatrix());

                   //_rttCam->setProjectionMatrix(viewer->getCamera()->getProjectionMatrix());

 

                   //rtt相机镜像投影;

                   osg::Vec3 _eye, _center, _up;

                   _mstCam->getViewMatrixAsLookAt(_eye, _center, _up);

 

                   //求取投影相机相应的eye,center,up位置及方向;

                   osg::Vec3 _refEye = osg::Vec3(_eye.x(), _eye.y(), -_eye.z());

                   osg::Vec3 _refUp  = osg::Vec3(_up.x(), _up.y(), -_up.z());

                   osg::Vec3 _refCenter = osg::Vec3(_center.x(), _center.y(), -_center.z());

 

                   //使用_refUp有倒影,不解?;

                   _rttCam->setViewMatrixAsLookAt(_refEye, _refCenter, osg::Vec3d(0.0, 0.0, 1.0));

 

                   //设置rtt相机的投影矩阵;

                   _m0->getOrCreateStateSet()->getUniform("uRTTViewMatrix")->set(

                            _rttCam->getViewMatrix()*_rttCam->getProjectionMatrix());

 

                   viewer->frame();

         }

 

         return 0;

}

 

//创建天空盒, 半球型;

osg::ref_ptr<osg::Node> HalfSphere::createHalfSphere(osg::Image *img, float radius)

{

         osg::ref_ptr<osg::Geode> geode = new osg::Geode;

         osg::ref_ptr<osg::Geometry> geom = new osg::Geometry;

 

         //压入顶点;

         osg::ref_ptr<osg::Vec3Array> veArr = new osg::Vec3Array;

         std::vector<std::vector<osg::Vec3>> veVertext;

 

         //纹理坐标;

         osg::ref_ptr<osg::Vec2Array> texArr = new osg::Vec2Array;

         std::vector<std::vector<osg::Vec2>> texVertex;

 

         for(int i=0; i<=90; i+=10)

         {

                   std::vector<osg::Vec3> veTmp;

                   std::vector<osg::Vec2> _texTmp;

                   for(int j=0; j<=360; j += 10)

                   {

                            double x = radius * cos(osg::DegreesToRadians((float)i)) * cos(osg::DegreesToRadians((float)j));

                            double y = radius * cos(osg::DegreesToRadians((float)i)) * sin(osg::DegreesToRadians((float)j));

                            double z = radius * sin(osg::DegreesToRadians((float)i));

                            veTmp.push_back(osg::Vec3(x,y,z));

                            _texTmp.push_back(osg::Vec2((float)j/370.0+0.01,(float)i/100.0+0.01));

                   }

                   veVertext.push_back(veTmp);

                   texVertex.push_back(_texTmp);

         }

 

         //重新组织点;

         //法线数组;

         osg::ref_ptr<osg::Vec3Array> norArr = new osg::Vec3Array;

 

         std::vector<std::vector<osg::Vec3>>::iterator it = veVertext.begin();

         for(; it != veVertext.end(); )

         {

                   std::vector<osg::Vec3> _tmp = *it;

                   it++;

                   if(it == veVertext.end())

                            break;

 

                   int count = (*it).size();

                   for(int i=0; i<count; i++)

                   {

                            veArr->push_back(_tmp.at(i));

                            veArr->push_back(it->at(i));

                            norArr->push_back(osg::Vec3(0,0,0) - _tmp.at(i));

                            norArr->push_back(osg::Vec3(0,0,0) - it->at(i));

                   }

         }

         geom->setVertexArray(veArr);

 

 

         std::vector<std::vector<osg::Vec2>>::iterator itt = texVertex.begin();

         for(; itt != texVertex.end(); )

         {

                   std::vector<osg::Vec2> _tmp = *itt;

                   itt++;

                   if(itt == texVertex.end())

                            break;

 

                   int count = (*itt).size();

                   for(int i=0; i<count; i++)

                   {

                            texArr->push_back(_tmp.at(i));

                            texArr->push_back(itt->at(i));

                   }

         }

 

         geom->setTexCoordArray(0,texArr);

 

         //法线;

         geom->setNormalArray(norArr);

         geom->setNormalBinding(osg::Geometry::AttributeBinding::BIND_PER_VERTEX);

 

         geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLE_STRIP,0,veArr->size()));

 

         geode->addDrawable(geom);

 

         //纹理;

         osg::ref_ptr<osg::Texture2D> tex = new osg::Texture2D;

         if(img->valid())

         {

                   tex->setImage(0,img);

         }

 

         tex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);//列向;

         tex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);//行向;

 

         geom->getOrCreateStateSet()->setTextureAttributeAndModes(0,tex,osg::StateAttribute::ON);

 

posted on 2015-05-06 10:17  3D入魔  阅读(1632)  评论(0编辑  收藏  举报