osgearth的多个layer同时渲染,使用的技术是多pass渲染,即,将一个瓦片的mesh,应用多个不同的的layer进行渲染,一个layer就是一个渲染pass。
/**
* Everything necessary to render a single terrain tile.
REX renders the terrain in multiple passes, one pass for each visible layer.
*/
struct TileRenderModel
{
/** Samplers that are bound for every rendering pass (elevation, normal map, etc.) */
Samplers _sharedSamplers;
/** Samplers bound for each visible layer (color) */
RenderingPasses _passes;
/** Add a new rendering pass to the end of the list. */
RenderingPass& addPass()
{
_passes.resize(_passes.size()+1);
return _passes.back();
}
/** Look up a rendering pass by the corresponding layer ID */
const RenderingPass* getPass(UID uid) const
{
for (unsigned i = 0; i < _passes.size(); ++i) {
if (_passes[i].sourceUID() == uid)
return &_passes[i];
}
return 0L;
}
/** Look up a rendering pass by the corresponding layer ID */
RenderingPass* getPass(UID uid)
{
for (unsigned i = 0; i < _passes.size(); ++i) {
if (_passes[i].sourceUID() == uid)
return &_passes[i];
}
return 0L;
}
/** Deallocate GPU objects associated with this model */
void releaseGLObjects(osg::State* state) const
{
for (unsigned s = 0; s<_sharedSamplers.size(); ++s)
if (_sharedSamplers[s]._texture.valid() && _sharedSamplers[s]._matrix.isIdentity())
_sharedSamplers[s]._texture->releaseGLObjects(state);
for (unsigned p = 0; p<_passes.size(); ++p)
_passes[p].releaseGLObjects(state);
}
/** Resize GL buffers associated with this model */
void resizeGLObjectBuffers(unsigned size)
{
for (unsigned s = 0; s<_sharedSamplers.size(); ++s)
if (_sharedSamplers[s]._texture.valid() && _sharedSamplers[s]._matrix.isIdentity())
_sharedSamplers[s]._texture->resizeGLObjectBuffers(size);
for (unsigned p = 0; p<_passes.size(); ++p)
_passes[p].resizeGLObjectBuffers(size);
}
};
TileNode::merge函数,在upatetraverse中调用,更新多个layer的渲染资源的绑定,如图片,渲染状态等
void
TileNode::merge(const TerrainTileModel* model, const RenderBindings& bindings)
{
bool newElevationData = false;
const SamplerBinding& color = bindings[SamplerBinding::COLOR];
if (color.isActive())
{
for(TerrainTileColorLayerModelVector::const_iterator i = model->colorLayers().begin();
i != model->colorLayers().end();
++i)
{
TerrainTileImageLayerModel* model = dynamic_cast<TerrainTileImageLayerModel*>(i->get());
if (model)
{
if (model->getTexture())
{
RenderingPass* pass = _renderModel.getPass(model->getImageLayer()->getUID());
if (!pass)
{
pass = &_renderModel.addPass();
pass->setLayer(model->getLayer());
// This is a new pass that just showed up at this LOD
// Since it just arrived at this LOD, make the parent the same as the color.
if (bindings[SamplerBinding::COLOR_PARENT].isActive())
{
pass->samplers()[SamplerBinding::COLOR_PARENT]._texture = model->getTexture();
pass->samplers()[SamplerBinding::COLOR_PARENT]._matrix = *model->getMatrix();
}
}
pass->samplers()[SamplerBinding::COLOR]._texture = model->getTexture();
pass->samplers()[SamplerBinding::COLOR]._matrix = *model->getMatrix();
// Handle an RTT image layer:
if (model->getImageLayer() && model->getImageLayer()->useCreateTexture())
{
// Check the texture's userdata for a Node. If there is one there,
// render it to the texture using the Tile Rasterizer service.
// TODO: consider hanging on to this texture and not applying it to
// the live tile until the RTT is complete. (Prevents unsightly flashing)
GeoNode* rttNode = dynamic_cast<GeoNode*>(model->getTexture()->getUserData());
if (rttNode && _context->getTileRasterizer())
{
_context->getTileRasterizer()->push(rttNode->_node.get(), model->getTexture(), rttNode->_extent);
}
}
// check to see if this data requires an image update traversal.
if (_imageUpdatesActive == false)
{
for(unsigned i=0; i<model->getTexture()->getNumImages(); ++i)
{
if (model->getTexture()->getImage(i)->requiresUpdateCall())
{
ADJUST_UPDATE_TRAV_COUNT(this, +1);
_imageUpdatesActive = true;
break;
}
}
}
}
}
else // non-image color layer (like splatting, e.g.)
{
TerrainTileColorLayerModel* model = i->get();
if (model && model->getLayer())
{
RenderingPass* pass = _renderModel.getPass(model->getLayer()->getUID());
if (!pass)
{
pass = &_renderModel.addPass();
pass->setLayer(model->getLayer());
}
}
}
}
}
// Elevation:
const SamplerBinding& elevation = bindings[SamplerBinding::ELEVATION];
if (elevation.isActive() && model->elevationModel().valid() && model->elevationModel()->getTexture())
{
osg::Texture* tex = model->elevationModel()->getTexture();
// always keep the elevation image around because we use it for bounding box computation:
tex->setUnRefImageDataAfterApply(false);
_renderModel._sharedSamplers[SamplerBinding::ELEVATION]._texture = tex;
_renderModel._sharedSamplers[SamplerBinding::ELEVATION]._matrix.makeIdentity();
setElevationRaster(tex->getImage(0), osg::Matrixf::identity());
newElevationData = true;
}
// Normals:
const SamplerBinding& normals = bindings[SamplerBinding::NORMAL];
if (normals.isActive() && model->normalModel().valid() && model->normalModel()->getTexture())
{
osg::Texture* tex = model->normalModel()->getTexture();
// keep the normal map around because we might update it later in "ping"
tex->setUnRefImageDataAfterApply(false);
_renderModel._sharedSamplers[SamplerBinding::NORMAL]._texture = tex;
_renderModel._sharedSamplers[SamplerBinding::NORMAL]._matrix.makeIdentity();
updateNormalMap();
}
// Other Shared Layers:
for (unsigned i = 0; i < model->sharedLayers().size(); ++i)
{
TerrainTileImageLayerModel* layerModel = model->sharedLayers()[i].get();
if (layerModel->getTexture())
{
// locate the shared binding corresponding to this layer:
UID uid = layerModel->getImageLayer()->getUID();
unsigned bindingIndex = INT_MAX;
for(unsigned i=SamplerBinding::SHARED; i<bindings.size() && bindingIndex==INT_MAX; ++i) {
if (bindings[i].isActive() && bindings[i].sourceUID().isSetTo(uid)) {
bindingIndex = i;
}
}
if (bindingIndex < INT_MAX)
{
osg::Texture* tex = layerModel->getTexture();
_renderModel._sharedSamplers[bindingIndex]._texture = tex;
_renderModel._sharedSamplers[bindingIndex]._matrix.makeIdentity();
}
}
}
// Patch Layers
for (unsigned i = 0; i < model->patchLayers().size(); ++i)
{
TerrainTilePatchLayerModel* layerModel = model->patchLayers()[i].get();
}
if (_childrenReady)
{
for (int i = 0; i < 4; ++i)
{
TileNode* child = getSubTile(i);
if (child)
child->refreshInheritedData(this, bindings);
}
}
if (newElevationData)
{
_context->getEngine()->getTerrain()->notifyTileAdded(getKey(), this);
}
}