osgEarth 2.1.1
|
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 }