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() ); } } } } }
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 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"; } } } }