osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarth/TextureCompositorMulti.cpp

Go to the documentation of this file.
00001 /* -*-c++-*- */
00002 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
00003  * Copyright 2008-2010 Pelican Mapping
00004  * http://osgearth.org
00005  *
00006  * osgEarth is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU Lesser General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public License
00017  * along with this program.  If not, see <http://www.gnu.org/licenses/>
00018  */
00019 #include <osgEarth/TextureCompositorMulti>
00020 #include <osgEarth/ImageUtils>
00021 #include <osgEarth/Registry>
00022 #include <osgEarth/ShaderComposition>
00023 #include <osgEarth/ShaderUtils>
00024 #include <osg/Texture2D>
00025 #include <osg/TexEnv>
00026 #include <osg/TexEnvCombine>
00027 #include <vector>
00028 
00029 using namespace osgEarth;
00030 
00031 #define LC "[TextureCompositorMulti] "
00032 
00033 //------------------------------------------------------------------------
00034 
00035 namespace
00036 {
00037     static osg::Shader*
00038     s_createTextureVertexShader( const TextureLayout& layout, bool blending )
00039     {
00040         std::stringstream buf;
00041        
00042         const TextureLayout::TextureSlotVector& slots = layout.getTextureSlots();
00043 
00044         if ( blending )
00045         {
00046             buf << "uniform mat4 osgearth_TexBlendMatrix[" << slots.size() << "];\n";
00047         }
00048 
00049         buf << "void osgearth_vert_setupTexturing() \n"
00050             << "{ \n";
00051 
00052         // Set up the texture coordinates for each active slot (primary and secondary).
00053         // Primary slots are the actual image layer's texture image unit. A Secondary
00054         // slot is what an image layer uses for LOD blending, and is set on a per-layer basis.
00055         for( int slot = 0; slot < (int)slots.size(); ++slot )
00056         {
00057             if ( slots[slot] >= 0 )
00058             {
00059                 UID uid = slots[slot];
00060                 int primarySlot = layout.getSlot(uid, 0);
00061 
00062                 if ( slot == primarySlot )
00063                 {
00064                     // normal unit:
00065                     buf << "    gl_TexCoord["<< slot <<"] = gl_MultiTexCoord" << slot << ";\n";
00066                 }
00067                 else
00068                 {
00069                     // secondary (blending) unit:
00070                     buf << "    gl_TexCoord["<< slot <<"] = osgearth_TexBlendMatrix["<< primarySlot << "] * gl_MultiTexCoord" << primarySlot << ";\n";
00071                 }
00072             }
00073         }
00074             
00075         buf << "} \n";
00076 
00077         std::string str = buf.str();
00078         return new osg::Shader( osg::Shader::VERTEX, str );
00079     }
00080 
00081     static osg::Shader*
00082     s_createTextureFragShaderFunction( const TextureLayout& layout, int maxSlots, bool blending, float fadeInDuration )
00083     {
00084         const TextureLayout::RenderOrderVector& order = layout.getRenderOrder();
00085 
00086         std::stringstream buf;
00087 
00088         buf << "#version 120 \n";
00089 
00090         if ( blending )
00091         {
00092             buf << "#extension GL_ARB_shader_texture_lod : enable \n"
00093                 << "uniform float osgearth_SlotStamp[" << maxSlots << "]; \n"
00094                 << "uniform float osg_FrameTime; \n"
00095                 << "uniform float osgearth_LODRangeFactor; \n";
00096         }
00097 
00098         buf << "uniform float osgearth_ImageLayerOpacity[" << maxSlots << "]; \n"
00099             //The enabled array is a fixed size.  Make sure this corresponds to the size definition in TerrainEngineNode.cpp
00100             << "uniform bool  osgearth_ImageLayerEnabled[" << 16 << "]; \n"  
00101             << "uniform float osgearth_ImageLayerRange[" << 2 * maxSlots << "]; \n"
00102             << "uniform float osgearth_ImageLayerAttenuation; \n"
00103             << "uniform float osgearth_CameraElevation; \n"
00104             << "varying float osgearth_CameraRange; \n";
00105 
00106         const TextureLayout::TextureSlotVector& slots = layout.getTextureSlots();
00107 
00108         for( int i = 0; i < maxSlots && i < (int)slots.size(); ++i )
00109         {
00110             if ( slots[i] >= 0 )
00111             {
00112                 buf << "uniform sampler2D tex" << i << ";\n";
00113             }
00114         }
00115 
00116         buf << "void osgearth_frag_applyTexturing( inout vec4 color ) \n"
00117             << "{ \n"
00118             << "    vec3 color3 = color.rgb; \n"
00119             << "    vec4 texel; \n"
00120             << "    float maxOpacity = 0.0; \n"
00121             << "    float dmin, dmax, atten_min, atten_max, age; \n";           
00122 
00123         for( unsigned int i=0; i < order.size(); ++i )
00124         {
00125             int slot = order[i];
00126             int q = 2 * i;
00127 
00128             // if this UID has a secondyar slot, LOD blending ON.
00129             int secondarySlot = layout.getSlot( slots[slot], 1, maxSlots );
00130 
00131             buf << "    if (osgearth_ImageLayerEnabled["<< i << "]) { \n"
00132                 << "        dmin = osgearth_CameraElevation - osgearth_ImageLayerRange["<< q << "]; \n"
00133                 << "        dmax = osgearth_CameraElevation - osgearth_ImageLayerRange["<< q+1 <<"]; \n"
00134 
00135                 << "        if (dmin >= 0 && dmax <= 0.0) { \n"
00136                 << "            atten_max = -clamp( dmax, -osgearth_ImageLayerAttenuation, 0 ) / osgearth_ImageLayerAttenuation; \n"
00137                 << "            atten_min =  clamp( dmin, 0, osgearth_ImageLayerAttenuation ) / osgearth_ImageLayerAttenuation; \n";
00138 
00139             if ( secondarySlot >= 0 ) // LOD blending enabled for this layer
00140             {
00141                 float invFadeInDuration = 1.0f/fadeInDuration;
00142 
00143                 buf << "            age = "<< invFadeInDuration << " * min( "<< fadeInDuration << ", osg_FrameTime - osgearth_SlotStamp[" << slot << "] ); \n"
00144                     << "            age = clamp(age, 0.0, 1.0); \n"
00145                     << "            vec4 texel0 = texture2D(tex" << slot << ", gl_TexCoord["<< slot << "].st);\n"
00146                     << "            vec4 texel1 = texture2D(tex" << secondarySlot << ", gl_TexCoord["<< secondarySlot << "].st);\n"
00147                     << "            float mixval = age * osgearth_LODRangeFactor;\n"
00148 
00149                     // pre-multiply alpha before mixing:
00150                     << "            texel0.rgb *= texel0.a; \n"
00151                     << "            texel1.rgb *= texel1.a; \n"
00152                     << "            texel = mix(texel1, texel0, mixval); \n"
00153 
00154                     // revert to non-pre-multiplies alpha (assumes openGL state uses non-pre-mult alpha)
00155                     << "            if (texel.a > 0.0) { \n"
00156                     << "                texel.rgb /= texel.a; \n"
00157                     << "            } \n";
00158             }
00159             else
00160             {
00161                 buf << "            texel = texture2D(tex" << slot << ", gl_TexCoord["<< slot <<"].st); \n";
00162             }
00163             
00164             buf << "            float opacity =  texel.a * osgearth_ImageLayerOpacity[" << i << "];\n"
00165                 << "            color3 = mix(color3, texel.rgb, opacity * atten_max * atten_min); \n"
00166                 << "            if (opacity > maxOpacity) {\n"
00167                 << "              maxOpacity = opacity;\n"
00168                 << "            }\n"                
00169                 << "        } \n"
00170                 << "    } \n";
00171         }
00172         
00173             buf << "    color = vec4(color3, maxOpacity);\n"
00174                 << "} \n";
00175 
00176 
00177 
00178         std::string str = buf.str();
00179         //OE_INFO << std::endl << str;
00180         return new osg::Shader( osg::Shader::FRAGMENT, str );
00181     }
00182 }
00183 
00184 //------------------------------------------------------------------------
00185 
00186 namespace
00187 {
00188     static std::string makeSamplerName(int slot)
00189     {
00190         std::stringstream buf;
00191         buf << "tex" << slot;
00192         return buf.str();
00193     }
00194     
00195     static osg::Texture2D*
00196     s_getTexture( osg::StateSet* stateSet, UID layerUID, const TextureLayout& layout, osg::StateSet* parentStateSet)
00197     {
00198         int slot = layout.getSlot( layerUID, 0 );
00199         if ( slot < 0 )
00200             return 0L;
00201 
00202         osg::Texture2D* tex = static_cast<osg::Texture2D*>(
00203             stateSet->getTextureAttribute( slot, osg::StateAttribute::TEXTURE ) );
00204 
00205         if ( !tex )
00206         {
00207             tex = new osg::Texture2D();
00208 
00209             // configure the mipmapping
00210 
00211             tex->setMaxAnisotropy( 16.0f );
00212 
00213             tex->setResizeNonPowerOfTwoHint(false);
00214             tex->setFilter( osg::Texture::MAG_FILTER, osg::Texture::LINEAR );
00215             tex->setFilter( osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR );
00216 
00217             // configure the wrapping
00218             tex->setWrap( osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE );
00219             tex->setWrap( osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE );
00220 
00221             stateSet->setTextureAttributeAndModes( slot, tex, osg::StateAttribute::ON );
00222             
00223             // install the slot attribute
00224             std::string name = makeSamplerName(slot);
00225             stateSet->getOrCreateUniform( name.c_str(), osg::Uniform::SAMPLER_2D )->set( slot );
00226         }
00227 
00228         // see if we need an LOD blending secondary texture:
00229         int secondarySlot = layout.getSlot( layerUID, 1 );
00230         if ( secondarySlot >= 0 )
00231         {
00232             osg::Texture2D* parentTex = 0;
00233 
00234             //int parentSlot = slot + layout.getRenderOrder().size();
00235             std::string parentSampler = makeSamplerName( secondarySlot );
00236             if (parentStateSet)
00237             {
00238                 parentTex = static_cast<osg::Texture2D*>(
00239                     parentStateSet->getTextureAttribute( slot, osg::StateAttribute::TEXTURE ) );
00240 
00241                 if (parentTex)
00242                 {
00243                     stateSet->setTextureAttributeAndModes(secondarySlot, parentTex, osg::StateAttribute::ON );
00244                     stateSet->getOrCreateUniform(parentSampler.c_str(),
00245                                                  osg::Uniform::SAMPLER_2D )->set( secondarySlot );
00246                 }
00247             }
00248 
00249             if ( !parentTex )
00250             {
00251                 // Bind the main texture as the secondary texture and
00252                 // set the scaling factors appropriately.
00253                 stateSet->getOrCreateUniform(
00254                     parentSampler.c_str(), osg::Uniform::SAMPLER_2D)->set(slot);
00255             }
00256 
00257         }
00258         return tex;
00259     }
00260 }
00261 
00262 //------------------------------------------------------------------------
00263 
00264 TextureCompositorMultiTexture::TextureCompositorMultiTexture( bool useGPU, const TerrainOptions& options ) :
00265 _lodTransitionTime( *options.lodTransitionTime() ),
00266 _enableMipmapping( *options.enableMipmapping() ),
00267 _useGPU( useGPU )
00268 {
00269     _enableMipmappingOnUpdatedTextures = Registry::instance()->getCapabilities().supportsMipmappedTextureUpdates();
00270 }
00271 
00272 void
00273 TextureCompositorMultiTexture::applyLayerUpdate(osg::StateSet*       stateSet,
00274                                                 UID                  layerUID,
00275                                                 const GeoImage&      preparedImage,
00276                                                 const TileKey&       tileKey,
00277                                                 const TextureLayout& layout,
00278                                                 osg::StateSet*       parentStateSet) const
00279 {
00280     osg::Texture2D* tex = s_getTexture( stateSet, layerUID, layout, parentStateSet);
00281     if ( tex )
00282     {
00283         osg::Image* image = preparedImage.getImage();
00284         image->dirty(); // required for ensure the texture recognizes the image as new data
00285         tex->setImage( image );
00286 
00287         // set up proper mipmapping filters:
00288         if (_enableMipmapping &&
00289             _enableMipmappingOnUpdatedTextures && 
00290             ImageUtils::isPowerOfTwo( image ) && 
00291             !(!image->isMipmap() && ImageUtils::isCompressed(image)) )
00292         {
00293             if ( tex->getFilter(osg::Texture::MIN_FILTER) != osg::Texture::LINEAR_MIPMAP_LINEAR )
00294                 tex->setFilter( osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR );
00295         }
00296         else if ( tex->getFilter(osg::Texture::MIN_FILTER) != osg::Texture::LINEAR )
00297         {
00298             tex->setFilter( osg::Texture::MIN_FILTER, osg::Texture::LINEAR );
00299         }
00300 
00301         bool lodBlending = layout.getSlot(layerUID, 1) >= 0;
00302 
00303         if (_enableMipmapping &&
00304             _enableMipmappingOnUpdatedTextures && 
00305             lodBlending )
00306         {
00307             int slot = layout.getSlot(layerUID, 0);
00308 
00309             // update the timestamp on the image layer to support blending.
00310             float now = (float)osg::Timer::instance()->delta_s( osg::Timer::instance()->getStartTick(), osg::Timer::instance()->tick() );
00311             ArrayUniform stampUniform( "osgearth_SlotStamp", osg::Uniform::FLOAT, stateSet, layout.getMaxUsedSlot() + 1 );
00312             stampUniform.setElement( slot, now );            
00313 
00314             // set the texture matrix to properly position the blend (parent) texture
00315             osg::Matrix mat;
00316             if ( parentStateSet != 0L )
00317             {
00318                 unsigned tileX, tileY;
00319                 tileKey.getTileXY(tileX, tileY);
00320 
00321                 mat(0,0) = 0.5f;
00322                 mat(1,1) = 0.5f;
00323                 mat(3,0) = (float)(tileX % 2) * 0.5f;
00324                 mat(3,1) = (float)(1 - tileY % 2) * 0.5f;
00325             }
00326 
00327             ArrayUniform texMatUniform( "osgearth_TexBlendMatrix", osg::Uniform::FLOAT_MAT4, stateSet, layout.getMaxUsedSlot() + 1 );
00328             texMatUniform.setElement( slot, mat );
00329         }
00330     }
00331 }
00332 
00333 void 
00334 TextureCompositorMultiTexture::updateMasterStateSet(osg::StateSet*       stateSet,
00335                                                     const TextureLayout& layout    ) const
00336 {
00337     int numSlots = layout.getMaxUsedSlot() + 1;
00338     int maxUnits = numSlots;
00339 
00340     if ( _useGPU )
00341     {
00342         // Validate against the max number of GPU texture units:
00343         if ( maxUnits > Registry::instance()->getCapabilities().getMaxGPUTextureUnits() )
00344         {
00345             maxUnits = Registry::instance()->getCapabilities().getMaxGPUTextureUnits();
00346 
00347             OE_WARN << LC
00348                 << "Warning! You have exceeded the number of texture units available on your GPU ("
00349                 << maxUnits << "). Consider using another compositing mode."
00350                 << std::endl;
00351         }
00352 
00353         VirtualProgram* vp = static_cast<VirtualProgram*>( stateSet->getAttribute(osg::StateAttribute::PROGRAM) );
00354         if ( maxUnits > 0 )
00355         {
00356             // see if we have any blended layers:
00357             bool hasBlending = layout.containsSecondarySlots( maxUnits ); 
00358 
00359             vp->setShader( 
00360                 "osgearth_vert_setupTexturing", 
00361                 s_createTextureVertexShader(layout, hasBlending) );
00362 
00363             vp->setShader( 
00364                 "osgearth_frag_applyTexturing",
00365                 s_createTextureFragShaderFunction(layout, maxUnits, hasBlending, _lodTransitionTime ) );
00366         }
00367         else
00368         {
00369             vp->removeShader( "osgearth_frag_applyTexturing", osg::Shader::FRAGMENT );
00370             vp->removeShader( "osgearth_vert_setupTexturing", osg::Shader::VERTEX );
00371         }
00372     }
00373 
00374     else
00375     {
00376         // Validate against the maximum number of textures available in FFP mode.
00377         if ( maxUnits > Registry::instance()->getCapabilities().getMaxFFPTextureUnits() )
00378         {
00379             maxUnits = Registry::instance()->getCapabilities().getMaxFFPTextureUnits();
00380             OE_WARN << LC << 
00381                 "Warning! You have exceeded the number of texture units available in fixed-function pipeline "
00382                 "mode on your graphics hardware (" << maxUnits << "). Consider using another "
00383                 "compositing mode." << std::endl;
00384         }
00385 
00386         // FFP multitexturing requires that we set up a series of TexCombine attributes:
00387         if (maxUnits == 1)
00388         {
00389             osg::TexEnv* texenv = new osg::TexEnv(osg::TexEnv::MODULATE);
00390             stateSet->setTextureAttributeAndModes(0, texenv, osg::StateAttribute::ON);
00391         }
00392         else if (maxUnits >= 2)
00393         {
00394             //Blend together the colors and accumulate the alpha values of textures 0 and 1 on unit 0
00395             {
00396                 osg::TexEnvCombine* texenv = new osg::TexEnvCombine;
00397                 texenv->setCombine_RGB(osg::TexEnvCombine::INTERPOLATE);
00398                 texenv->setCombine_Alpha(osg::TexEnvCombine::ADD);
00399 
00400                 texenv->setSource0_RGB(osg::TexEnvCombine::TEXTURE0+1);
00401                 texenv->setOperand0_RGB(osg::TexEnvCombine::SRC_COLOR);
00402                 texenv->setSource0_Alpha(osg::TexEnvCombine::TEXTURE0+1);
00403                 texenv->setOperand0_Alpha(osg::TexEnvCombine::SRC_ALPHA);
00404 
00405                 texenv->setSource1_RGB(osg::TexEnvCombine::TEXTURE0+0);
00406                 texenv->setOperand1_RGB(osg::TexEnvCombine::SRC_COLOR);
00407                 texenv->setSource1_Alpha(osg::TexEnvCombine::TEXTURE0+0);
00408                 texenv->setOperand1_Alpha(osg::TexEnvCombine::SRC_ALPHA);
00409 
00410                 texenv->setSource2_RGB(osg::TexEnvCombine::TEXTURE0+1);
00411                 texenv->setOperand2_RGB(osg::TexEnvCombine::SRC_ALPHA);
00412 
00413                 stateSet->setTextureAttributeAndModes(0, texenv, osg::StateAttribute::ON);
00414             }
00415 
00416 
00417             //For textures 2 and beyond, blend them together with the previous
00418             //Add the alpha values of this unit and the previous unit
00419             for (int unit = 1; unit < maxUnits-1; ++unit)
00420             {
00421                 osg::TexEnvCombine* texenv = new osg::TexEnvCombine;
00422                 texenv->setCombine_RGB(osg::TexEnvCombine::INTERPOLATE);
00423                 texenv->setCombine_Alpha(osg::TexEnvCombine::ADD);
00424 
00425                 texenv->setSource0_RGB(osg::TexEnvCombine::TEXTURE0+unit+1);
00426                 texenv->setOperand0_RGB(osg::TexEnvCombine::SRC_COLOR);
00427                 texenv->setSource0_Alpha(osg::TexEnvCombine::TEXTURE0+unit+1);
00428                 texenv->setOperand0_Alpha(osg::TexEnvCombine::SRC_ALPHA);
00429 
00430                 texenv->setSource1_RGB(osg::TexEnvCombine::PREVIOUS);
00431                 texenv->setOperand1_RGB(osg::TexEnvCombine::SRC_COLOR);
00432                 texenv->setSource1_Alpha(osg::TexEnvCombine::PREVIOUS);
00433                 texenv->setOperand1_Alpha(osg::TexEnvCombine::SRC_ALPHA);
00434 
00435                 texenv->setSource2_RGB(osg::TexEnvCombine::TEXTURE0+unit+1);
00436                 texenv->setOperand2_RGB(osg::TexEnvCombine::SRC_ALPHA);
00437 
00438                 stateSet->setTextureAttributeAndModes(unit, texenv, osg::StateAttribute::ON);
00439             }
00440 
00441             //Modulate the colors to get proper lighting on the last unit
00442             //Keep the alpha results from the previous stage
00443             {
00444                 osg::TexEnvCombine* texenv = new osg::TexEnvCombine;
00445                 texenv->setCombine_RGB(osg::TexEnvCombine::MODULATE);
00446                 texenv->setCombine_Alpha(osg::TexEnvCombine::REPLACE);
00447 
00448                 texenv->setSource0_RGB(osg::TexEnvCombine::PREVIOUS);
00449                 texenv->setOperand0_RGB(osg::TexEnvCombine::SRC_COLOR);
00450                 texenv->setSource0_Alpha(osg::TexEnvCombine::PREVIOUS);
00451                 texenv->setOperand0_Alpha(osg::TexEnvCombine::SRC_ALPHA);
00452 
00453                 texenv->setSource1_RGB(osg::TexEnvCombine::PRIMARY_COLOR);
00454                 texenv->setOperand1_RGB(osg::TexEnvCombine::SRC_COLOR);
00455                 stateSet->setTextureAttributeAndModes(maxUnits-1, texenv, osg::StateAttribute::ON);
00456             }
00457         }
00458     }
00459 }
00460 
00461 osg::Shader*
00462 TextureCompositorMultiTexture::createSamplerFunction(UID layerUID,
00463                                                      const std::string& functionName,
00464                                                      osg::Shader::Type type,
00465                                                      const TextureLayout& layout ) const
00466 {
00467     osg::Shader* result = 0L;
00468 
00469     int slot = layout.getSlot( layerUID );
00470     if ( slot >= 0 )
00471     {
00472         std::stringstream buf;
00473 
00474         buf << "uniform sampler2D tex"<< slot << "; \n"
00475             << "vec4 " << functionName << "() \n"
00476             << "{ \n";
00477 
00478         if ( type == osg::Shader::VERTEX )
00479             buf << "    return texture2D(tex"<< slot << ", gl_MultiTexCoord"<< slot <<".st); \n";
00480         else
00481             buf << "    return texture2D(tex"<< slot << ", gl_TexCoord["<< slot << "].st); \n";
00482 
00483         buf << "} \n";
00484 
00485         std::string str = buf.str();
00486         result = new osg::Shader( type, str );
00487     }
00488     return result;
00489 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines