osgEarth::ModelNode动态更新顶点数据及圆锥高度修改失效咨询
解决osgEarth中修改圆锥高度不生效的问题
问题根源
核心问题在于ModelNode会对传入的模型进行内部克隆/实例化,你在外部修改_cone和_coneDrawable的参数,并不会同步到ModelNode内部实际渲染的副本上。另外你设置了_coneDrawable->setUseDisplayList(false),此时调用dirtyDisplayList()本身就不会起作用。
修复方案
方案1:重构节点结构,直接控制渲染节点(推荐)
放弃通过ModelSymbol::setModel给ModelNode传入模型,改用GeoTransform处理地形贴合和位置管理,这样你直接操作的就是场景中实际渲染的节点,修改参数后能立即生效:
修改构造函数:
Cone::Cone(osgEarth::MapNode* mapNode) { if(!mapNode){ return; } _height = 700000.0; _cone = new osg::Cone(osg::Vec3(0, 0, 0), 300000.0, _height); _coneDrawable = new osg::ShapeDrawable(_cone); _coneDrawable->setColor(osg::Vec4d(0.0,1.0,0.0,0.5)); osg::ref_ptr<osg::Geode> geode = new osg::Geode(); geode->addDrawable(_coneDrawable); geode->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); osgEarth::ShaderGenerator shg; geode->accept(shg); _mt = new osg::MatrixTransform; _mt->setMatrix( osg::Matrix::rotate(0.0, osg::X_AXIS, 0.0, osg::Y_AXIS, 0.0, osg::Z_AXIS) *osg::Matrix::translate(osg::Vec3d(0, 0, -_height*3/4))); _mt->addChild(geode); // 改用GeoTransform处理地形贴合 osgEarth::GeoTransform* geoXform = new osgEarth::GeoTransform(); osgEarth::Style style; style.getOrCreate<osgEarth::AltitudeSymbol>()->clamping() = osgEarth::AltitudeSymbol::CLAMP_TO_TERRAIN; style.getOrCreate<osgEarth::AltitudeSymbol>()->binding() = osgEarth::AltitudeSymbol::BINDING_CENTROID; geoXform->setStyle(style); geoXform->addChild(_mt); // 设置初始位置 osgEarth::GeoPoint position( mapNode->getMapSRS(), -122.0, 37.0, 1000.0, osgEarth::ALTMODE_RELATIVE); geoXform->setPosition(position); geoXform->setLocalRotation(osg::Quat(osg::DegreesToRadians(90.0),osg::Vec3(0.0,0.0,0.0))); this->addChild(geoXform); // 保存GeoTransform指针,方便后续更新 _geoXform = geoXform; }
修改updateCone方法:
void Cone::updateCone(osgEarth::GeoPoint gp,double height,double r,osg::Quat quat){ _height = height; // 更新圆锥参数 _cone->setHeight(_height); _cone->setRadius(r); // 更新MatrixTransform的平移 _mt->setMatrix( osg::Matrix::rotate(quat) *osg::Matrix::translate(osg::Vec3d(0, 0, -_height*3/4))); // 通知Drawable更新 _coneDrawable->dirtyBound(); // 更新包围盒 _coneDrawable->dirtyVertexBufferObjects(); // 更新VBO数据 // 更新位置和旋转 _geoXform->setPosition(gp); _geoXform->setLocalRotation(quat); }
方案2:获取ModelNode内部的实际渲染节点(不推荐,耦合性高)
若坚持使用ModelNode,可通过遍历子节点找到内部的ShapeDrawable修改,但该方式依赖ModelNode内部实现,版本更新后可能失效:
void Cone::updateCone(osgEarth::GeoPoint gp,double height,double r,osg::Quat quat){ _height = height; // 遍历ModelNode子节点查找目标Drawable struct FindConeDrawable : public osg::NodeVisitor { osg::ShapeDrawable* result = nullptr; FindConeDrawable() : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {} void apply(osg::Geode& geode) override { for(auto drawable : geode.getDrawableList()){ if(auto sd = dynamic_cast<osg::ShapeDrawable*>(drawable.get())){ if(dynamic_cast<osg::Cone*>(sd->getShape())){ result = sd; break; } } } } }; FindConeDrawable finder; _modelNode->accept(finder); if(finder.result){ auto cone = dynamic_cast<osg::Cone*>(finder.result->getShape()); if(cone){ cone->setHeight(height); cone->setRadius(r); finder.result->dirtyBound(); finder.result->dirtyVertexBufferObjects(); } } _modelNode->setPosition(gp); _modelNode->setLocalRotation(quat); }
关键注意点
- 修改
osg::Shape参数后,必须调用ShapeDrawable的dirtyBound()更新包围盒,否则视锥体剔除可能出错;若使用VBO(默认开启),需调用dirtyVertexBufferObjects()更新顶点数据。 - 关闭DisplayList后,
dirtyDisplayList()不再生效,无需调用。
内容的提问来源于stack exchange,提问作者user30325735




