void OceanSurfaceNode::rebuildShaders() { // need the terrain engine so we can get at the compositor. TerrainEngineNode* engine = osgEarth::findTopMostNodeOfType<TerrainEngineNode>( this ); if ( !engine ) { OE_DEBUG << LC << "No terrain engine found in the map node; abort" << std::endl; return; } // access the compositor because we are going to be sampling map layers. TextureCompositor* comp = engine->getTextureCompositor(); if ( !comp ) { OE_INFO << LC << "No texture compositor found in the terrain engine; abort" << std::endl; return; } // reserve a texture unit for the surface texture (if we haven't already) if ( !_oceanSurfaceTextureApplied && _oceanSurfaceTextureUnit < 0 && _oceanSurfaceTexture.valid() ) { if ( comp->reserveTextureImageUnit( _oceanSurfaceTextureUnit ) ) { getOrCreateStateSet()->setTextureAttributeAndModes( _oceanSurfaceTextureUnit, _oceanSurfaceTexture.get(), osg::StateAttribute::ON); _oceanSurfaceTextureApplied = true; } else { OE_WARN << LC << "Sorry, failed to allocate a texture image unit for the surface texture." << std::endl; } } // create a VP to store our custom shader components. osgEarth::VirtualProgram* vp = new osgEarth::VirtualProgram(); getOrCreateStateSet()->setAttributeAndModes( vp, osg::StateAttribute::ON ); // if the ocean is disabled, just return without injecting any shaders. if ( !_enabled ) return; // build the sampler function if necessary osg::ref_ptr<const ImageLayer> safeMaskLayer = _maskLayer.get(); osg::Shader* maskSampler = 0L; if ( safeMaskLayer.valid() ) { maskSampler = comp->createSamplerFunction( safeMaskLayer->getUID(), MASK_SAMPLER_FUNC, osg::Shader::VERTEX ); if ( maskSampler ) vp->setShader( MASK_SAMPLER_FUNC, maskSampler ); } // make the helper functions. { std::stringstream buf; buf << "vec3 xyz_to_lat_lon_height(in vec3 xyz) \n" << "{ \n" << " float X = xyz.x;\n" << " float Y = xyz.y;\n" << " float Z = xyz.z;\n" << " float _radiusEquator = 6378137.0;\n" << " float _radiusPolar = 6356752.3142;\n" << " float flattening = (_radiusEquator-_radiusPolar)/_radiusEquator;\n" << " float _eccentricitySquared = 2.0*flattening - flattening*flattening;\n" << " float p = sqrt(X*X + Y*Y);\n" << " float theta = atan(Z*_radiusEquator , (p*_radiusPolar));\n" << " float eDashSquared = (_radiusEquator*_radiusEquator - _radiusPolar*_radiusPolar)/(_radiusPolar*_radiusPolar);\n" << " float sin_theta = sin(theta);\n" << " float cos_theta = cos(theta);\n" << " float latitude = atan( (Z + eDashSquared*_radiusPolar*sin_theta*sin_theta*sin_theta), (p - _eccentricitySquared*_radiusEquator*cos_theta*cos_theta*cos_theta) );\n" << " float longitude = atan(Y,X);\n" << " float sin_latitude = sin(latitude);\n" << " float N = _radiusEquator / sqrt( 1.0 - _eccentricitySquared*sin_latitude*sin_latitude);\n" << " float height = p/cos(latitude) - N;\n" << " return vec3(longitude, latitude, height);\n" << "} \n" << "\n"; std::string str = buf.str(); vp->setShader( "xyz_to_lat_lon_height", new osg::Shader(osg::Shader::VERTEX, str) ); } // next make the vertex shader function that will morph the ocean verts and prepare // the texture coordinates for the surface effects. { std::stringstream buf; buf << std::fixed; buf << "uniform float osg_SimulationTime; \n" << "uniform mat4 osg_ViewMatrixInverse;\n" << "uniform mat4 osg_ViewMatrix;\n" << "uniform float osgearth_OceanHeight;\n" << "uniform float osgearth_OceanPeriod;\n" << "uniform float osgearth_OceanAnimationPeriod;\n" << "varying float osgearth_OceanAlpha;\n" << "varying float osgearth_CameraRange; \n" << "vec3 xyz_to_lat_lon_height(in vec3 xyz); \n"; if ( _oceanSurfaceTextureApplied ) { buf << "varying vec3 osgearth_oceanSurfaceTexCoord; \n"; } if ( maskSampler ) { buf << "vec4 " << MASK_SAMPLER_FUNC << "(); \n"; } buf << "void osgearth_ocean_morphSurface() \n" << "{ \n" << " mat4 modelMatrix = osg_ViewMatrixInverse * gl_ModelViewMatrix; \n" << " vec4 vert = modelMatrix * gl_Vertex; \n" << " vec3 vert3 = vec3(vert.x, vert.y, vert.z); \n" << " vec3 latlon = xyz_to_lat_lon_height(vert3); \n" << " osgearth_OceanAlpha = 1.0; \n"; if ( maskSampler ) { buf << " osgearth_OceanAlpha = 1.0 - (" << MASK_SAMPLER_FUNC << "()).a; \n"; } if ( _invertMask ) buf << " osgearth_OceanAlpha = 1.0 - osgearth_OceanAlpha; \n"; buf << " if ( osgearth_CameraRange <= " << _maxRange << " ) \n" << " { \n" << " float s = mix(1.0, 0.0, osgearth_CameraRange / " << _maxRange << "); \n" //Invert so it's between 0 and 1 << " osgearth_OceanAlpha *= s; \n" << " } \n" << " else \n" << " { \n" << " osgearth_OceanAlpha = 0.0; \n" << " } \n" << " if (osgearth_OceanAlpha > 0.0) \n" << " { \n" << " float PI_2 = 3.14158 * 2.0; \n" << " float period = PI_2/osgearth_OceanPeriod; \n" << " float half_period = period / 2.0; \n" << " vec3 n = normalize(vert3);\n" << " float theta = (mod(latlon.x, period) / period) * PI_2; \n" << " float phi = (mod(latlon.y, half_period) / half_period) * PI_2; \n" << " float phase1 = osg_SimulationTime * 2.0; \n" << " float phase2 = osg_SimulationTime * 4.0; \n" << " float waveHeight = (osgearth_OceanAlpha) * osgearth_OceanHeight; \n" << " float scale1 = sin(theta + phase1) * waveHeight; \n" << " float scale2 = cos(phi + phase2) * waveHeight; \n" << " float scale3 = sin(theta + phase2) * cos(phi + phase1) * waveHeight * 1.6; \n" << " float scale = (scale1 + scale2 + scale3)/3.0; \n"; // flatten verts to MSL: if ( _adjustToMSL ) { buf << " vec3 offset = n * -latlon.z; \n" << " vert += vec4( offset.xyz, 0 ); \n"; } // apply the save scale: buf << " n = n * scale; \n" << " vert += vec4(n.x, n.y,n.z,0); \n" << " vert = osg_ViewMatrix * vert; \n" << " gl_Position = gl_ProjectionMatrix * vert; \n" << " }\n"; // set up the coords for the surface texture: if ( _oceanSurfaceTextureApplied ) { buf << " osgearth_oceanSurfaceTexCoord.x = latlon.x / " << _oceanSurfaceImageSizeRadians << "; \n" << " osgearth_oceanSurfaceTexCoord.y = latlon.y / " << _oceanSurfaceImageSizeRadians << "; \n" << " osgearth_oceanSurfaceTexCoord.z = fract(osg_SimulationTime/osgearth_OceanAnimationPeriod); \n"; } buf << "}\n"; // add as a custom user function in the shader composition: std::string vertSource = buf.str(); vp->setFunction( "osgearth_ocean_morphSurface", vertSource, osgEarth::ShaderComp::LOCATION_VERTEX_PRE_TEXTURING ); } // now we need a fragment function that will apply the ocean surface texture. if ( _oceanSurfaceTextureApplied ) { getOrCreateStateSet()->getOrCreateUniform( "osgearth_oceanSurfaceTex", osg::Uniform::SAMPLER_3D )->set( _oceanSurfaceTextureUnit ); std::stringstream buf; buf << "uniform sampler3D osgearth_oceanSurfaceTex; \n" << "varying vec3 osgearth_oceanSurfaceTexCoord; \n" << "varying float osgearth_OceanAlpha; \n" << "void osgearth_ocean_applySurfaceTex( inout vec4 color ) \n" << "{ \n" << " vec4 texel = texture3D(osgearth_oceanSurfaceTex, osgearth_oceanSurfaceTexCoord); \n" << " color = vec4( mix( color.rgb, texel.rgb, texel.a * osgearth_OceanAlpha ), color.a); \n" << "} \n"; std::string str = buf.str(); vp->setFunction( "osgearth_ocean_applySurfaceTex", str, osgEarth::ShaderComp::LOCATION_FRAGMENT_PRE_LIGHTING ); } }
bool ShadowUtils::setUpShadows(osgShadow::ShadowedScene* sscene, osg::Group* root) { osg::StateSet* ssStateSet = sscene->getOrCreateStateSet(); MapNode* mapNode = MapNode::findMapNode(root); TerrainEngineNode* engine = mapNode->getTerrainEngine(); if (!engine) return false; TextureCompositor* compositor = engine->getTextureCompositor(); int su = -1; if (!compositor->reserveTextureImageUnit(su)) return false; OE_INFO << LC << "Reserved texture unit " << su << " for shadowing" << std::endl; osgShadow::ViewDependentShadowMap* vdsm = dynamic_cast< osgShadow::ViewDependentShadowMap*>(sscene->getShadowTechnique()); int su1 = -1; if (vdsm && sscene->getShadowSettings()->getNumShadowMapsPerLight() == 2) { if (!compositor->reserveTextureImageUnit(su1) || su1 != su + 1) { OE_FATAL << LC << "couldn't get contiguous shadows for split vdsm\n"; sscene->getShadowSettings()->setNumShadowMapsPerLight(1); if (su1 != -1) compositor->releaseTextureImageUnit(su1); su1 = -1; } else { OE_INFO << LC << "Reserved texture unit " << su1 << " for shadowing" << std::endl; } } // create a virtual program to attach to the shadowed scene. VirtualProgram* vp = new VirtualProgram(); vp->setName( "shadow:terrain" ); //vp->installDefaultColoringAndLightingShaders(); ssStateSet->setAttributeAndModes( vp, 1 ); std::stringstream buf; buf << "#version " << GLSL_VERSION_STR << "\n"; #ifdef OSG_GLES2_AVAILABLE buf << "precision mediump float;\n"; #endif buf << "varying vec4 oe_shadow_ambient;\n"; buf << "varying vec4 oe_shadow_TexCoord0;\n"; if ( su1 >= 0 ) buf << "varying vec4 oe_shadow_TexCoord1;\n"; buf << "void oe_shadow_setupShadowCoords(inout vec4 VertexVIEW)\n"; buf << "{\n"; buf << " vec4 position4 = VertexVIEW;\n"; buf << " oe_shadow_TexCoord0.s = dot( position4, gl_EyePlaneS[" << su <<"]);\n"; buf << " oe_shadow_TexCoord0.t = dot( position4, gl_EyePlaneT[" << su <<"]);\n"; buf << " oe_shadow_TexCoord0.p = dot( position4, gl_EyePlaneR[" << su <<"]);\n"; buf << " oe_shadow_TexCoord0.q = dot( position4, gl_EyePlaneQ[" << su <<"]);\n"; if (su1 >= 0) { buf << " oe_shadow_TexCoord1.s = dot( position4, gl_EyePlaneS[" << su1 <<"]);\n"; buf << " oe_shadow_TexCoord1.t = dot( position4, gl_EyePlaneT[" << su1 <<"]);\n"; buf << " oe_shadow_TexCoord1.p = dot( position4, gl_EyePlaneR[" << su1 <<"]);\n"; buf << " oe_shadow_TexCoord1.q = dot( position4, gl_EyePlaneQ[" << su1 <<"]);\n"; } // the ambient lighting will control the intensity of the shadow. buf << " oe_shadow_ambient = gl_FrontLightProduct[0].ambient; \n" << "}\n"; std::string setupShadowCoords; setupShadowCoords = buf.str(); vp->setFunction( "oe_shadow_setupShadowCoords", setupShadowCoords, ShaderComp::LOCATION_VERTEX_VIEW, -1.0 ); std::stringstream buf2; buf2 << "#version " << GLSL_VERSION_STR << "\n" #ifdef OSG_GLES2_AVAILABLE "precision mediump float;\n" #endif "uniform sampler2DShadow shadowTexture;\n" "varying vec4 oe_shadow_TexCoord0;\n"; if (su1 >= 0) { // bound by vdsm buf2 << "uniform sampler2DShadow shadowTexture1;\n"; buf2 << "varying vec4 oe_shadow_TexCoord1;\n"; } buf2 << "varying vec4 oe_shadow_ambient;\n" "void oe_shadow_applyLighting( inout vec4 color )\n" "{\n" " float alpha = color.a;\n" " float shadowFac = shadow2DProj( shadowTexture, oe_shadow_TexCoord0).r;\n"; if (su1 > 0) { buf2 << " shadowFac *= shadow2DProj( shadowTexture1, oe_shadow_TexCoord1).r;\n"; } // calculate the shadowed color and mix if with the lit color based on the // ambient lighting. The 0.5 is a multiplier that darkens the shadow in // proportion to ambient light. It should probably be a uniform. buf2 << " vec4 colorInFullShadow = color * oe_shadow_ambient; \n" " color = mix(colorInFullShadow, color, shadowFac); \n" " color.a = alpha;\n" "}\n"; std::string fragApplyLighting; fragApplyLighting = buf2.str(); vp->setFunction( "oe_shadow_applyLighting", fragApplyLighting, osgEarth::ShaderComp::LOCATION_FRAGMENT_LIGHTING ); setShadowUnit(sscene, su); // VDSM uses a different sampler name, shadowTexture0. ssStateSet ->getOrCreateUniform("shadowTexture", osg::Uniform::SAMPLER_2D_SHADOW) ->set(su); return true; }