void OSGTerrainEngineNode::updateTextureCombining() { if ( _texCompositor.valid() ) { int numImageLayers = _update_mapf->imageLayers().size(); osg::StateSet* terrainStateSet = _terrain->getOrCreateStateSet(); if ( _texCompositor->usesShaderComposition() ) { // Creates or updates the shader components that are generated by the texture compositor. // These components reside in the CustomTerrain's stateset, and override the components // installed in the VP on the engine-node's stateset in installShaders(). VirtualProgram* vp = dynamic_cast<VirtualProgram*>( terrainStateSet->getAttribute(osg::StateAttribute::PROGRAM) ); if ( !vp ) { // create and add it the first time around.. vp = new VirtualProgram(); terrainStateSet->setAttributeAndModes( vp, osg::StateAttribute::ON ); } // first, update the default shader components based on the new layer count: const ShaderFactory* sf = Registry::instance()->getShaderFactory(); vp->setShader( "osgearth_vert_setupTexturing", sf->createDefaultTextureVertexShader( numImageLayers ) ); // not this one, because the compositor always generates a new one. //vp->setShader( "osgearth_frag_applyTexturing", lib.createDefaultTextureFragmentShader( numImageLayers ) ); } // next, inform the compositor that it needs to update based on a new layer count: _texCompositor->updateMasterStateSet( terrainStateSet ); //, numImageLayers ); } }
void TextureCompositorTexArray::updateMasterStateSet( osg::StateSet* stateSet, const TextureLayout& layout ) const { VirtualProgram* vp = static_cast<VirtualProgram*>( stateSet->getAttribute(osg::StateAttribute::PROGRAM) ); vp->setShader( "osgearth_frag_applyTexturing", s_createTextureFragShaderFunction(layout, true, _lodTransitionTime ) ); }
void HSLColorFilter::install( osg::StateSet* stateSet ) const { // safe: won't add twice. stateSet->addUniform( _hsl.get() ); VirtualProgram* vp = dynamic_cast<VirtualProgram*>(stateSet->getAttribute(VirtualProgram::SA_TYPE)); if ( vp ) { // safe: won't add the same pointer twice. vp->setShader( "osgEarthUtil::HSLColorFilter_common", s_commonShader.get() ); // build the local shader (unique per instance). We'll use a template with search // and replace for this one. std::string entryPoint = Stringify() << FUNCTION_PREFIX << _instanceId; std::string code = s_localShaderSource; replaceIn( code, "__UNIFORM_NAME__", _hsl->getName() ); replaceIn( code, "__ENTRY_POINT__", entryPoint ); osg::Shader* main = new osg::Shader(osg::Shader::FRAGMENT, code); //main->setName(entryPoint); vp->setShader(entryPoint, main); } }
void OSGTerrainEngineNode::updateTextureCombining() { if ( _texCompositor.valid() ) { int numImageLayers = _update_mapf->imageLayers().size(); osg::StateSet* terrainStateSet = _terrain->getOrCreateStateSet(); if ( _texCompositor->usesShaderComposition() ) { // Creates or updates the shader components that are generated by the texture compositor. // These components reside in the CustomTerrain's stateset, and override the components // installed in the VP on the engine-node's stateset in installShaders(). VirtualProgram* vp = new VirtualProgram() ; vp->setName( "engine_osgterrain:TerrainNode" ); vp->installDefaultColoringShaders(numImageLayers); terrainStateSet->setAttributeAndModes( vp, osg::StateAttribute::ON ); // first, update the default shader components based on the new layer count: const ShaderFactory* sf = Registry::instance()->getShaderFactory(); // second, install the per-layer color filter functions. for( int i=0; i<numImageLayers; ++i ) { std::string layerFilterFunc = Stringify() << "osgearth_runColorFilters_" << i; const ColorFilterChain& chain = _update_mapf->getImageLayerAt(i)->getColorFilters(); // install the wrapper function that calls all the filters in turn: vp->setShader( layerFilterFunc, sf->createColorFilterChainFragmentShader(layerFilterFunc, chain) ); // install each of the filter entry points: for( ColorFilterChain::const_iterator j = chain.begin(); j != chain.end(); ++j ) { const ColorFilter* filter = j->get(); filter->install( terrainStateSet ); } } } // next, inform the compositor that it needs to update based on a new layer count: _texCompositor->updateMasterStateSet( terrainStateSet ); //, numImageLayers ); } }
// This runs when the color filter is first installed. void install(osg::StateSet* stateSet) const { // When we added the shared layer, osgEarth reserves a special // image unit for it. Retrieve that now sine we need it in order // to use its sampler. int unit = _maskLayer->shareImageUnit().get(); // Make a vertex shader that will access the texture coordinates // for our shared layer. std::string vs = Stringify() << "varying vec4 mask_layer_texc; \n" << "void my_filter_vertex(inout vec4 VertexMODEL) \n" << "{ \n" << " mask_layer_texc = gl_MultiTexCoord" << unit << "; \n" <<"} \n"; // Make the fragment shader that will modulate the visible layer. // This is is simple -- it just maxes out the red component wherever // the shared "mask" layer exceed a certain alpha value. std::string fs = "uniform sampler2D mask_layer_tex; \n" "varying vec4 mask_layer_texc; \n" "void my_color_filter(inout vec4 color) \n" "{ \n" " vec4 mask_texel = texture2D(mask_layer_tex, mask_layer_texc.st); \n" " if ( mask_texel.a >= 0.5 ) \n" " { \n" " color.r = 1.0; \n" " } \n" "} \n"; // getOrCreate ensures we don't overwrite an existing VP. VirtualProgram* vp = VirtualProgram::getOrCreate( stateSet ); // install the vertex function vp->setFunction( "my_filter_vertex", vs, ShaderComp::LOCATION_VERTEX_MODEL ); // install the color filter entry point. This is just a regular shader function, // not a pipeline injection, so just use setShader. vp->setShader( "my_color_filter", new osg::Shader(osg::Shader::FRAGMENT, fs) ); // finally, bind our sampler uniform to the allocated image unit. stateSet->getOrCreateUniform("mask_layer_tex", osg::Uniform::SAMPLER_2D)->set( unit ); }
void GroundCoverLayer::buildStateSets() { // assert we have the necessary TIUs: if (_groundCoverTexBinding.valid() == false) { OE_DEBUG << LC << "buildStateSets deferred.. bindings not reserved\n"; return; } if (!_zonesConfigured) { OE_DEBUG << LC << "buildStateSets deferred.. zones not yet configured\n"; return; } osg::ref_ptr<LandCoverDictionary> landCoverDict; if (_landCoverDict.lock(landCoverDict) == false) { OE_DEBUG << LC << "buildStateSets deferred.. land cover dictionary not available\n"; return; } osg::ref_ptr<LandCoverLayer> landCoverLayer; if (_landCoverLayer.lock(landCoverLayer) == false) { OE_DEBUG << LC << "buildStateSets deferred.. land cover layer not available\n"; return; } NoiseTextureFactory noise; osg::ref_ptr<osg::Texture> noiseTexture = noise.create(256u, 4u); GroundCoverShaders shaders; // Layer-wide stateset: osg::StateSet* stateset = getOrCreateStateSet(); // bind the noise sampler. stateset->setTextureAttribute(_noiseBinding.unit(), noiseTexture.get()); stateset->addUniform(new osg::Uniform(NOISE_SAMPLER, _noiseBinding.unit())); if (_maskLayer.valid()) { stateset->setDefine("OE_GROUNDCOVER_MASK_SAMPLER", _maskLayer->shareTexUniformName().get()); stateset->setDefine("OE_GROUNDCOVER_MASK_MATRIX", _maskLayer->shareTexMatUniformName().get()); } // disable backface culling to support shadow/depth cameras, // for which the geometry shader renders cross hatches instead of billboards. stateset->setMode(GL_CULL_FACE, osg::StateAttribute::PROTECTED); // enable alpha-to-coverage multisampling for vegetation. stateset->setMode(GL_SAMPLE_ALPHA_TO_COVERAGE_ARB, 1); // uniform that communicates the availability of multisampling. if (osg::DisplaySettings::instance()->getMultiSamples()) { stateset->setDefine("OE_GROUNDCOVER_HAS_MULTISAMPLES"); } stateset->setAttributeAndModes( new osg::BlendFunc(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO), osg::StateAttribute::OVERRIDE); for (Zones::iterator z = _zones.begin(); z != _zones.end(); ++z) { Zone* zone = z->get(); GroundCover* groundCover = zone->getGroundCover(); if (groundCover) { if (!groundCover->getBiomes().empty() || groundCover->getTotalNumBillboards() > 0) { osg::StateSet* zoneStateSet = groundCover->getOrCreateStateSet(); // Install the land cover shaders on the state set VirtualProgram* vp = VirtualProgram::getOrCreate(zoneStateSet); vp->setName("Ground cover (" + groundCover->getName() + ")"); shaders.loadAll(vp, getReadOptions()); // Generate the coverage acceptor shader osg::Shader* covTest = groundCover->createPredicateShader(_landCoverDict.get(), _landCoverLayer.get()); covTest->setName(covTest->getName() + "_GEOMETRY"); covTest->setType(osg::Shader::GEOMETRY); vp->setShader(covTest); osg::Shader* covTest2 = groundCover->createPredicateShader(_landCoverDict.get(), _landCoverLayer.get()); covTest->setName(covTest->getName() + "_TESSCONTROL"); covTest2->setType(osg::Shader::TESSCONTROL); vp->setShader(covTest2); osg::ref_ptr<osg::Shader> layerShader = groundCover->createShader(); layerShader->setType(osg::Shader::GEOMETRY); vp->setShader(layerShader.get()); OE_INFO << LC << "Established zone \"" << zone->getName() << "\" at LOD " << getLOD() << "\n"; osg::Texture* tex = groundCover->createTexture(); zoneStateSet->setTextureAttribute(_groundCoverTexBinding.unit(), tex); zoneStateSet->addUniform(new osg::Uniform(GCTEX_SAMPLER, _groundCoverTexBinding.unit())); OE_DEBUG << LC << "buildStateSets completed!\n"; } else { OE_WARN << LC << "ILLEGAL: ground cover layer with no biomes or no billboards defined\n"; } } else { // not an error. OE_DEBUG << LC << "zone contains no ground cover information\n"; } } }
void TextureCompositorMultiTexture::updateMasterStateSet(osg::StateSet* stateSet, const TextureLayout& layout ) const { int numSlots = layout.getMaxUsedSlot() + 1; int maxUnits = numSlots; if ( _useGPU ) { // Validate against the max number of GPU texture units: if ( maxUnits > Registry::instance()->getCapabilities().getMaxGPUTextureUnits() ) { maxUnits = Registry::instance()->getCapabilities().getMaxGPUTextureUnits(); OE_WARN << LC << "Warning! You have exceeded the number of texture units available on your GPU (" << maxUnits << "). Consider using another compositing mode." << std::endl; } VirtualProgram* vp = static_cast<VirtualProgram*>( stateSet->getAttribute(osg::StateAttribute::PROGRAM) ); if ( maxUnits > 0 ) { // see if we have any blended layers: bool hasBlending = layout.containsSecondarySlots( maxUnits ); vp->setShader( "osgearth_vert_setupTexturing", s_createTextureVertexShader(layout, hasBlending) ); vp->setShader( "osgearth_frag_applyTexturing", s_createTextureFragShaderFunction(layout, maxUnits, hasBlending, _lodTransitionTime ) ); } else { vp->removeShader( "osgearth_frag_applyTexturing" ); vp->removeShader( "osgearth_vert_setupTexturing" ); } } else { // Forcably disable shaders stateSet->setAttributeAndModes( new osg::Program(), osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED ); // Validate against the maximum number of textures available in FFP mode. if ( maxUnits > Registry::instance()->getCapabilities().getMaxFFPTextureUnits() ) { maxUnits = Registry::instance()->getCapabilities().getMaxFFPTextureUnits(); OE_WARN << LC << "Warning! You have exceeded the number of texture units available in fixed-function pipeline " "mode on your graphics hardware (" << maxUnits << "). Consider using another " "compositing mode." << std::endl; } // FFP multitexturing requires that we set up a series of TexCombine attributes: if (maxUnits == 1) { osg::TexEnv* texenv = new osg::TexEnv(osg::TexEnv::MODULATE); stateSet->setTextureAttributeAndModes(0, texenv, osg::StateAttribute::ON); } else if (maxUnits >= 2) { //Blend together the colors and accumulate the alpha values of textures 0 and 1 on unit 0 { osg::TexEnvCombine* texenv = new osg::TexEnvCombine; texenv->setCombine_RGB(osg::TexEnvCombine::INTERPOLATE); texenv->setCombine_Alpha(osg::TexEnvCombine::ADD); texenv->setSource0_RGB(osg::TexEnvCombine::TEXTURE0+1); texenv->setOperand0_RGB(osg::TexEnvCombine::SRC_COLOR); texenv->setSource0_Alpha(osg::TexEnvCombine::TEXTURE0+1); texenv->setOperand0_Alpha(osg::TexEnvCombine::SRC_ALPHA); texenv->setSource1_RGB(osg::TexEnvCombine::TEXTURE0+0); texenv->setOperand1_RGB(osg::TexEnvCombine::SRC_COLOR); texenv->setSource1_Alpha(osg::TexEnvCombine::TEXTURE0+0); texenv->setOperand1_Alpha(osg::TexEnvCombine::SRC_ALPHA); texenv->setSource2_RGB(osg::TexEnvCombine::TEXTURE0+1); texenv->setOperand2_RGB(osg::TexEnvCombine::SRC_ALPHA); stateSet->setTextureAttributeAndModes(0, texenv, osg::StateAttribute::ON); } //For textures 2 and beyond, blend them together with the previous //Add the alpha values of this unit and the previous unit for (int unit = 1; unit < maxUnits-1; ++unit) { osg::TexEnvCombine* texenv = new osg::TexEnvCombine; texenv->setCombine_RGB(osg::TexEnvCombine::INTERPOLATE); texenv->setCombine_Alpha(osg::TexEnvCombine::ADD); texenv->setSource0_RGB(osg::TexEnvCombine::TEXTURE0+unit+1); texenv->setOperand0_RGB(osg::TexEnvCombine::SRC_COLOR); texenv->setSource0_Alpha(osg::TexEnvCombine::TEXTURE0+unit+1); texenv->setOperand0_Alpha(osg::TexEnvCombine::SRC_ALPHA); texenv->setSource1_RGB(osg::TexEnvCombine::PREVIOUS); texenv->setOperand1_RGB(osg::TexEnvCombine::SRC_COLOR); texenv->setSource1_Alpha(osg::TexEnvCombine::PREVIOUS); texenv->setOperand1_Alpha(osg::TexEnvCombine::SRC_ALPHA); texenv->setSource2_RGB(osg::TexEnvCombine::TEXTURE0+unit+1); texenv->setOperand2_RGB(osg::TexEnvCombine::SRC_ALPHA); stateSet->setTextureAttributeAndModes(unit, texenv, osg::StateAttribute::ON); } //Modulate the colors to get proper lighting on the last unit //Keep the alpha results from the previous stage { osg::TexEnvCombine* texenv = new osg::TexEnvCombine; texenv->setCombine_RGB(osg::TexEnvCombine::MODULATE); texenv->setCombine_Alpha(osg::TexEnvCombine::REPLACE); texenv->setSource0_RGB(osg::TexEnvCombine::PREVIOUS); texenv->setOperand0_RGB(osg::TexEnvCombine::SRC_COLOR); texenv->setSource0_Alpha(osg::TexEnvCombine::PREVIOUS); texenv->setOperand0_Alpha(osg::TexEnvCombine::SRC_ALPHA); texenv->setSource1_RGB(osg::TexEnvCombine::PRIMARY_COLOR); texenv->setOperand1_RGB(osg::TexEnvCombine::SRC_COLOR); stateSet->setTextureAttributeAndModes(maxUnits-1, texenv, osg::StateAttribute::ON); } } } }
void SplatTerrainEffect::onInstall(TerrainEngineNode* engine) { if ( engine ) { // Check that we have coverage data (required for now - later masking data will be an option) if ( !_coverage.valid() || !_coverage->hasLayer() ) { OE_WARN << LC << "ILLEGAL: coverage data is required\n"; return; } if ( !_surface.valid() ) { OE_WARN << LC << "Note: no surface information available\n"; } // First, create a surface splatting texture array for each biome region. if ( _coverage.valid() && _surface.valid() ) { if ( createSplattingTextures(_coverage.get(), _surface.get(), _textureDefs) == false ) { OE_WARN << LC << "Failed to create any valid splatting textures\n"; return; } // First install a shared noise texture. if ( _gpuNoise == false ) { osg::StateSet* terrainStateSet = engine->getOrCreateStateSet(); if ( terrainStateSet->getUniform("oe_splat_noiseTex") == 0L ) { // reserve a texture unit: if (engine->getResources()->reserveTextureImageUnit(_noiseTexUnit, "Splat Noise")) { NoiseTextureFactory noise; terrainStateSet->setTextureAttribute( _noiseTexUnit, noise.create(256u, 4u) ); terrainStateSet->addUniform( new osg::Uniform("oe_splat_noiseTex", _noiseTexUnit) ); } } } // Set up surface splatting: if ( engine->getResources()->reserveTextureImageUnit(_splatTexUnit, "Splat Coverage Data") ) { osg::StateSet* stateset; if ( _surface->getBiomeRegions().size() == 1 ) stateset = engine->getSurfaceStateSet(); else stateset = new osg::StateSet(); // surface splatting texture array: _splatTexUniform = stateset->getOrCreateUniform( SPLAT_SAMPLER, osg::Uniform::SAMPLER_2D_ARRAY ); _splatTexUniform->set( _splatTexUnit ); stateset->setTextureAttribute( _splatTexUnit, _textureDefs[0]._texture.get() ); // coverage code sampler: osg::ref_ptr<ImageLayer> coverageLayer; _coverage->lockLayer( coverageLayer ); _coverageTexUniform = stateset->getOrCreateUniform( COVERAGE_SAMPLER, osg::Uniform::SAMPLER_2D ); _coverageTexUniform->set( coverageLayer->shareImageUnit().get() ); // control uniforms (TODO: simplify and deprecate unneeded uniforms) stateset->addUniform( _scaleOffsetUniform.get() ); stateset->addUniform( _warpUniform.get() ); stateset->addUniform( _blurUniform.get() ); stateset->addUniform( _noiseScaleUniform.get() ); stateset->addUniform( _useBilinearUniform.get() ); stateset->addUniform(new osg::Uniform("oe_splat_detailRange", 1000000.0f)); SplattingShaders splatting; splatting.define( "SPLAT_EDIT", _editMode ); splatting.define( "SPLAT_GPU_NOISE", _gpuNoise ); splatting.define( "OE_USE_NORMAL_MAP", engine->normalTexturesRequired() ); splatting.replace( "$COVERAGE_TEXTURE_MATRIX", coverageLayer->shareTexMatUniformName().get() ); VirtualProgram* vp = VirtualProgram::getOrCreate(stateset); splatting.load( vp, splatting.VertModel ); splatting.load( vp, splatting.VertView ); splatting.load( vp, splatting.Frag ); splatting.load( vp, splatting.Util ); // GPU noise is expensive, so only use it to tweak noise function values that you // can later bake into the noise texture generator. if ( _gpuNoise ) { //osgEarth::replaceIn( fragmentShader, "#undef SPLAT_GPU_NOISE", "#define SPLAT_GPU_NOISE" ); // Use --uniform on the command line to tweak these values: stateset->addUniform(new osg::Uniform("oe_splat_freq", 32.0f)); stateset->addUniform(new osg::Uniform("oe_splat_pers", 0.8f)); stateset->addUniform(new osg::Uniform("oe_splat_lac", 2.2f)); stateset->addUniform(new osg::Uniform("oe_splat_octaves", 8.0f)); // support shaders std::string noiseShaderSource = ShaderLoader::load( splatting.Noise, splatting ); osg::Shader* noiseShader = new osg::Shader(osg::Shader::FRAGMENT, noiseShaderSource); vp->setShader( "oe_splat_noiseshaders", noiseShader ); } // TODO: disabled biome selection temporarily because the callback impl applies the splatting shader // to the land cover bin as well as the surface bin, which we do not want -- find another way if ( _surface->getBiomeRegions().size() == 1 ) { // install his biome's texture set: stateset->setTextureAttribute(_splatTexUnit, _textureDefs[0]._texture.get()); // install this biome's sampling function. Use cloneOrCreate since each // stateset needs a different shader set in its VP. VirtualProgram* vp = VirtualProgram::cloneOrCreate( stateset ); osg::Shader* shader = new osg::Shader(osg::Shader::FRAGMENT, _textureDefs[0]._samplingFunction); vp->setShader( "oe_splat_getRenderInfo", shader ); } else { OE_WARN << LC << "Multi-biome setup needs re-implementing (reminder)\n"; // install the cull callback that will select the appropriate // state based on the position of the camera. _biomeRegionSelector = new BiomeRegionSelector( _surface->getBiomeRegions(), _textureDefs, stateset, _splatTexUnit ); engine->addCullCallback( _biomeRegionSelector.get() ); } } } } }
// Generates the main shader code for rendering the terrain. void MPTerrainEngineNode::updateShaders() { if ( _batchUpdateInProgress ) { _shaderUpdateRequired = true; } else { osg::StateSet* terrainStateSet = _terrain->getOrCreateStateSet(); VirtualProgram* vp = new VirtualProgram(); vp->setName( "engine_mp:TerrainNode" ); terrainStateSet->setAttributeAndModes( vp, osg::StateAttribute::ON ); // Vertex shader template: std::string vs = "#version " GLSL_VERSION_STR "\n" GLSL_DEFAULT_PRECISION_FLOAT "\n" "varying vec4 osg_FrontColor; \n" "varying vec4 osg_FrontSecondaryColor; \n" "varying vec4 oe_layer_tc;\n" "void osgearth_vert_setupColoring() \n" "{ \n" " osg_FrontColor = gl_Color; \n" " osg_FrontSecondaryColor = vec4(0.0);\n" " oe_layer_tc = __GL_MULTITEXCOORD__;\n" "}\n"; // Fragment shader template: std::string fs = "#version " GLSL_VERSION_STR "\n" GLSL_DEFAULT_PRECISION_FLOAT "\n" "varying vec4 oe_layer_tc; \n" "uniform sampler2D oe_layer_tex; \n" "uniform int oe_layer_uid; \n" "uniform int oe_layer_order; \n" "uniform float oe_layer_opacity; \n" "__COLOR_FILTER_HEAD__" "void osgearth_frag_applyColoring( inout vec4 color ) \n" "{ \n" " vec4 texel = texture2D(oe_layer_tex, oe_layer_tc.st);\n" " float alpha = texel.a * oe_layer_opacity; \n" " if (oe_layer_order == 0) \n" " color = vec4(color.rgb * (1.0 - alpha) + (texel.rgb * alpha), 1.0); \n" " else \n" " color = vec4(texel.rgb, color.a * alpha); \n" " __COLOR_FILTER_BODY__" "} \n"; // install the gl_MultiTexCoord* variable that uses the proper texture // image unit: replaceIn( vs, "__GL_MULTITEXCOORD__", Stringify() << "gl_MultiTexCoord" << _textureImageUnit ); // assemble color filter code snippets. { std::stringstream cf_head; std::stringstream cf_body; // second, install the per-layer color filter functions. bool ifStarted = false; const char* I = " "; int numImageLayers = _update_mapf->imageLayers().size(); for( int i=0; i<numImageLayers; ++i ) { ImageLayer* layer = _update_mapf->getImageLayerAt(i); if ( layer->getEnabled() ) { const ColorFilterChain& chain = layer->getColorFilters(); if ( chain.size() > 0 ) { if ( ifStarted ) cf_body << I << "else if "; else cf_body << I << "if "; cf_body << "(oe_layer_uid == " << layer->getUID() << ") {\n"; for( ColorFilterChain::const_iterator j = chain.begin(); j != chain.end(); ++j ) { const ColorFilter* filter = j->get(); cf_head << "void " << filter->getEntryPointFunctionName() << "(in int slot, inout vec4 color);\n"; cf_body << I << I << filter->getEntryPointFunctionName() << "(" << _textureImageUnit << ", color);\n"; filter->install( terrainStateSet ); } cf_body << I << "}\n"; ifStarted = true; } } } std::string cf_head_str, cf_body_str; cf_head_str = cf_head.str(); cf_body_str = cf_body.str(); replaceIn( fs, "__COLOR_FILTER_HEAD__", cf_head_str ); replaceIn( fs, "__COLOR_FILTER_BODY__", cf_body_str ); } vp->setShader( "osgearth_vert_setupColoring", new osg::Shader( osg::Shader::VERTEX, vs ), osg::StateAttribute::ON | osg::StateAttribute::PROTECTED ); vp->setShader( "osgearth_frag_applyColoring", new osg::Shader( osg::Shader::FRAGMENT, fs ), osg::StateAttribute::ON | osg::StateAttribute::PROTECTED ); // required for multipass tile rendering to work terrainStateSet->setAttributeAndModes( new osg::Depth(osg::Depth::LEQUAL, 0, 1, true) ); // blend multipass image layers terrainStateSet->setAttributeAndModes( new osg::BlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA), 1); // binding for the terrain texture terrainStateSet->getOrCreateUniform( "oe_layer_tex", osg::Uniform::SAMPLER_2D )->set( _textureImageUnit ); // uniform that controls per-layer opacity terrainStateSet->getOrCreateUniform( "oe_layer_opacity", osg::Uniform::FLOAT )->set( 1.0f ); // uniform that conveys the layer UID to the shaders; necessary // for per-layer branching (like color filters) terrainStateSet->getOrCreateUniform( "oe_layer_uid", osg::Uniform::INT )->set( 0 ); // uniform that conveys the render order, since the shaders // need to know which is the first layer in order to blend properly terrainStateSet->getOrCreateUniform( "oe_layer_order", osg::Uniform::INT )->set( 0 ); _shaderUpdateRequired = false; } }
void DetailTexture::onInstall(TerrainEngineNode* engine) { if ( engine ) { if ( !_texture.valid() ) { _texture = new osg::Texture2DArray(); _texture->setTextureSize(1024, 1024, _textures.size()); _texture->setWrap( osg::Texture::WRAP_S, osg::Texture::REPEAT ); _texture->setWrap( osg::Texture::WRAP_T, osg::Texture::REPEAT ); _texture->setFilter( osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR ); _texture->setFilter( osg::Texture::MAG_FILTER, osg::Texture::LINEAR ); _texture->setResizeNonPowerOfTwoHint( false ); for(unsigned i=0; i<_textures.size(); ++i) { const TextureSource& ts = _textures[i]; osg::ref_ptr<osg::Image> image = URI(ts._url).getImage(_dbOptions.get()); if ( image->s() != 1024 || image->t() != 1024 ) { osg::ref_ptr<osg::Image> imageResized; ImageUtils::resizeImage( image.get(), 1024, 1024, imageResized ); _texture->setImage( i, imageResized.get() ); } else { _texture->setImage( i, image.get() ); } } } osg::StateSet* stateset = engine->getOrCreateStateSet(); if ( engine->getTextureCompositor()->reserveTextureImageUnit(_unit) ) { _samplerUniform = stateset->getOrCreateUniform( "oe_detail_tex", osg::Uniform::SAMPLER_2D_ARRAY ); _samplerUniform->set( _unit ); stateset->setTextureAttribute( _unit, _texture.get(), osg::StateAttribute::ON ); // don't use "..andModes" } if ( _maskLayer.valid() ) { int unit = *_maskLayer->shareImageUnit(); _maskUniform = stateset->getOrCreateUniform("oe_detail_mask", osg::Uniform::SAMPLER_2D); _maskUniform->set(unit); OE_NOTICE << LC << "Installed layer " << _maskLayer->getName() << " as texture mask on unit " << unit << std::endl; } else { exit(-1); } stateset->addUniform( _startLODUniform.get() ); stateset->addUniform( _intensityUniform.get() ); stateset->addUniform( _scaleUniform.get() ); stateset->addUniform( _attenuationDistanceUniform.get() ); std::string fs = generateFragmentShader( _textures.size(), _octaves.value() ); VirtualProgram* vp = VirtualProgram::getOrCreate(stateset); vp->setFunction( "oe_detail_vertex", vs, ShaderComp::LOCATION_VERTEX_VIEW ); vp->setFunction( "oe_detail_fragment", fs, ShaderComp::LOCATION_FRAGMENT_COLORING ); vp->setShader( "simplexNoise", new osg::Shader(osg::Shader::FRAGMENT, snoise) ); } }
void SplatTerrainEffect::onInstall(TerrainEngineNode* engine) { if ( engine && _ok ) { if ( !_coverageLayer.valid() ) { OE_WARN << LC << "No coverage layer set\n"; return; } engine->requireElevationTextures(); // install the splat texture array: if ( engine->getResources()->reserveTextureImageUnit(_splatTexUnit) ) { osg::StateSet* stateset = new osg::StateSet(); // splat sampler _splatTexUniform = stateset->getOrCreateUniform( SPLAT_SAMPLER, osg::Uniform::SAMPLER_2D_ARRAY ); _splatTexUniform->set( _splatTexUnit ); stateset->setTextureAttribute( _splatTexUnit, _textureDefs[0]._texture.get() ); // coverage sampler _coverageTexUniform = stateset->getOrCreateUniform( COVERAGE_SAMPLER, osg::Uniform::SAMPLER_2D ); _coverageTexUniform->set( _coverageLayer->shareImageUnit().get() ); // control uniforms stateset->addUniform( _scaleOffsetUniform.get() ); stateset->addUniform( _warpUniform.get() ); stateset->addUniform( _blurUniform.get() ); stateset->addUniform( _noiseScaleUniform.get() ); stateset->addUniform( _useBilinearUniform.get() ); stateset->addUniform(new osg::Uniform("oe_splat_detailRange", 1000000.0f)); // Configure the vertex shader: std::string vertexShaderModel = ShaderLoader::load(_shaders.VertModel, _shaders); std::string vertexShaderView = ShaderLoader::load(_shaders.VertView, _shaders); osgEarth::replaceIn( vertexShaderView, "$COVERAGE_TEXMAT_UNIFORM", _coverageLayer->shareTexMatUniformName().get() ); // Configure the fragment shader: std::string fragmentShader = ShaderLoader::load(_shaders.Frag, _shaders); if ( _editMode ) { osgEarth::replaceIn( fragmentShader, "#undef SPLAT_EDIT", "#define SPLAT_EDIT" ); } // are normal maps available? if ( engine->normalTexturesRequired() ) { osgEarth::replaceIn( fragmentShader, "#undef OE_USE_NORMAL_MAP", "#define OE_USE_NORMAL_MAP" ); } // GPU noise is expensive, so only use it to tweak noise function values that you // can later bake into the noise texture generator. if ( _gpuNoise ) { osgEarth::replaceIn( fragmentShader, "#undef SPLAT_GPU_NOISE", "#define SPLAT_GPU_NOISE" ); // Use --uniform on the command line to tweak these values: stateset->addUniform(new osg::Uniform("oe_splat_freq", 32.0f)); stateset->addUniform(new osg::Uniform("oe_splat_pers", 0.8f)); stateset->addUniform(new osg::Uniform("oe_splat_lac", 2.2f)); stateset->addUniform(new osg::Uniform("oe_splat_octaves", 8.0f)); } else // use a noise texture (the default) { if (engine->getResources()->reserveTextureImageUnit(_noiseTexUnit)) { OE_INFO << LC << "Noise texture -> unit " << _noiseTexUnit << "\n"; _noiseTex = createNoiseTexture(); stateset->setTextureAttribute( _noiseTexUnit, _noiseTex.get() ); _noiseTexUniform = stateset->getOrCreateUniform( NOISE_SAMPLER, osg::Uniform::SAMPLER_2D ); _noiseTexUniform->set( _noiseTexUnit ); } } // shader components VirtualProgram* vp = VirtualProgram::getOrCreate(stateset); vp->setFunction( "oe_splat_vertex_model", vertexShaderModel, ShaderComp::LOCATION_VERTEX_MODEL ); vp->setFunction( "oe_splat_vertex_view", vertexShaderView, ShaderComp::LOCATION_VERTEX_VIEW ); vp->setFunction( "oe_splat_fragment", fragmentShader, ShaderComp::LOCATION_FRAGMENT_COLORING, _renderOrder ); if ( _gpuNoise ) { // support shaders std::string noiseShaderSource = ShaderLoader::load(_shaders.Noise, _shaders); osg::Shader* noiseShader = new osg::Shader(osg::Shader::FRAGMENT, noiseShaderSource); vp->setShader( "oe_splat_noiseshaders", noiseShader ); } // install the cull callback that will select the appropriate // state based on the position of the camera. _biomeSelector = new BiomeSelector( _biomes, _textureDefs, stateset, _splatTexUnit ); engine->addCullCallback( _biomeSelector.get() ); } } }
void LandCoverTerrainEffect::onInstall(TerrainEngineNode* engine) { // First verify that the land cover definition is legal if ( !_landCover.valid() ) return; if ( engine ) { // install a noise texture if we haven't already. osg::StateSet* terrainStateSet = engine->getOrCreateStateSet(); // check whether it's already installed by another extension: if ( terrainStateSet->getUniform("oe_splat_noiseTex") == 0L ) { // reserve a texture unit: if (engine->getResources()->reserveTextureImageUnit(_noiseTexUnit, "Splat Noise")) { NoiseTextureFactory noise; terrainStateSet->setTextureAttribute( _noiseTexUnit, noise.create(256u, 4u) ); terrainStateSet->addUniform( new osg::Uniform("oe_splat_noiseTex", _noiseTexUnit) ); } } for(LandCoverLayers::const_iterator i = _landCover->getLayers().begin(); i != _landCover->getLayers().end(); ++i) { const LandCoverLayer* layer = i->get(); if ( layer ) { if ( !layer->getBiomes().empty() ) { osg::StateSet* stateset = engine->addLandCoverGroup( layer->getName(), layer->getLOD() ); if ( stateset ) { // Install the land cover shaders on the state set VirtualProgram* vp = VirtualProgram::getOrCreate(stateset); LandCoverShaders shaders; shaders.loadAll( vp, _dbo.get() ); // Generate the coverage acceptor shader osg::Shader* covTest = layer->createPredicateShader( getCoverage() ); covTest->setType( osg::Shader::TESSCONTROL ); vp->setShader( covTest ); osg::ref_ptr<osg::Shader> layerShader = layer->createShader(); layerShader->setType( osg::Shader::GEOMETRY ); vp->setShader( layerShader ); OE_INFO << LC << "Adding land cover group: " << layer->getName() << " at LOD " << layer->getLOD() << "\n"; // Install the uniforms stateset->addUniform( new osg::Uniform("oe_landcover_windFactor", layer->getWind()) ); stateset->addUniform( new osg::Uniform("oe_landcover_noise", 1.0f) ); stateset->addUniform( new osg::Uniform("oe_landcover_ao", 0.5f) ); stateset->addUniform( new osg::Uniform("oe_landcover_exposure", 1.0f) ); stateset->addUniform( new osg::Uniform("oe_landcover_density", layer->getDensity()) ); stateset->addUniform( new osg::Uniform("oe_landcover_fill", layer->getFill()) ); stateset->addUniform( new osg::Uniform("oe_landcover_maxDistance", layer->getMaxDistance()) ); stateset->addUniform( new osg::Uniform("oe_landcover_brightness", layer->getBrightness()) ); stateset->addUniform( new osg::Uniform("oe_landcover_contrast", layer->getContrast()) ); stateset->addUniform( new osg::Uniform("oe_landcover_noise", 0.75f) ); int unit; if ( engine->getResources()->reserveTextureImageUnit(unit, "LandCover") ) { int s=-1, t=-1; osg::Texture2DArray* tex = new osg::Texture2DArray(); int arrayIndex = 0; for(int b=0; b<layer->getBiomes().size(); ++b) { const LandCoverBiome* biome = layer->getBiomes().at(b); for(int i=0; i<biome->getBillboards().size(); ++i, ++arrayIndex) { const LandCoverBillboard& bb = biome->getBillboards().at(i); osg::ref_ptr<osg::Image> im; if ( s < 0 ) { s = bb._image->s(); t = bb._image->t(); im = bb._image.get(); tex->setTextureSize(s, t, layer->getTotalNumBillboards()); } else { if ( bb._image->s() != s || bb._image->t() != t ) { ImageUtils::resizeImage( bb._image.get(), s, t, im ); } else { im = bb._image.get(); } } tex->setImage( arrayIndex, im.get() ); } } tex->setFilter(tex->MIN_FILTER, tex->NEAREST_MIPMAP_LINEAR); tex->setFilter(tex->MAG_FILTER, tex->LINEAR); tex->setWrap (tex->WRAP_S, tex->CLAMP_TO_EDGE); tex->setWrap (tex->WRAP_T, tex->CLAMP_TO_EDGE); tex->setUnRefImageDataAfterApply( true ); tex->setMaxAnisotropy( 4.0 ); tex->setResizeNonPowerOfTwoHint( false ); stateset->setTextureAttribute(unit, tex); stateset->addUniform(new osg::Uniform("oe_landcover_texArray", unit) ); } else { OE_WARN << LC << "Terrain engine failed to allocate a texture image unit for a land cover group\n"; } } else { OE_WARN << LC << "Terrain engine failed to return a stateset for a land cover group\n"; } } else { OE_WARN << LC << "ILLEGAL: land cover layer with no biomes defined\n"; } } else { OE_WARN << LC << "ILLEGAL: empty layer found in land cover layer list\n"; } } } }