osgEarth视口到达地球的南北极,鼠标就不能上下旋转视口了

系列文章目录

前言

在使用osgEarth做开发的时候大家估计都遇到过:在使用 osgEarth::EarthManipulator 时,可能会遇到在极地(南极和北极)无法进行上下旋转的问题,用户体验特别不好。有人说是因为万向锁的问题,osgEarth默认情况下对旋转做了限制。我也无从考证是不是这个原因造成的。
万向锁(Gimbal lock): 一旦选择±90°作为pitch角,就会导致第一次旋转和第三次旋转等价,整个旋转表示系统被限制在只能绕竖直轴旋转,丢失了一个表示维度。
维基百科的解释:环架锁定(英语:Gimbal lock),也称为万向节锁定,是使用动态欧拉角表示三维物体透过平衡环架旋转时会出现的问题。
欧拉角有两种:

静态:即绕世界坐标系三个轴的旋转,由于物体旋转过程中坐标轴保持静止,所以称为静态。
动态:即绕物体坐标系三个轴的旋转,由于物体旋转过程中坐标轴随着物体做相同的转动,所以称为动态。
使用动态欧拉角会出现万向锁现象;静态欧拉角不存在万向锁的问题。

在动态欧拉角的一次旋转中,需要按照固定的顺序分别绕x、y、z三个轴旋转一次,假设顺序为x-y-z。

在一次旋转中,当按x轴旋转时,y、z轴不动;当按y轴旋转时,为保持x轴在物体坐标系的对应位置,x轴会随物体旋转,z轴不动;同理,当按z轴旋转时,x、y轴随物体旋转。

因此,当绕y轴旋转角度为90°时,此次旋转中x轴与z轴重合(见右图“万向锁”),导致此次旋转无法按原顺序旋转至某些方向,这就是万向锁问题。

事实上,一旦选择±90°作为第二次旋转的角度,就会导致第一次旋转和第三次旋转等价,整个旋转表示系统被限制在只能绕竖直轴旋转,丢失了一个表示维度。这种角度为±90°的第二次旋转使得第一次和第三次旋转的旋转轴相同的现象,称作万向锁。
就像下图一下,osgEarth视口到达地球的南北极,鼠标就不能上下旋转视口了,好像被硬生生卡住了,感觉非常难受。

在这里插入图片描述

一、问题原因

从osgEarth代码层面上讲,这是因为默认情况下,EarthManipulator 在极地附近会限制相机的旋转,以避免某些情况下的视角问题。就是下面viewer->setCameraManipulator(manipulator);这行代码造成的,但是去掉这行代码,默认的视口又看不到地球了。

osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
viewer->setCameraManipulator(manipulator);

二、问题解决

加入下面2行代码

manipulator->getSettings()->setThrowingEnabled(false);
manipulator->getSettings()->setLockAzimuthWhilePanning(false);

三、完整代码

#include <Windows.h>
#include <iostream>
#include <string>

#include <osgViewer/Viewer>
#include <osgDB/ReadFile>
#include <osgEarth/MapNode>
#include <osgEarthDrivers/cache_filesystem/FileSystemCache>
#include <osgEarth/ImageLayer>
#include <osgEarth/EarthManipulator>
#include <osgEarth/OGRFeatureSource>
#include <osgEarth/FeatureModelLayer>
#include <osgEarth/FeatureImageLayer>
#include <osgEarth/ECEF>
#include <osgEarth/GeoData>
#include <osgEarth/Viewpoint>
#include <osgEarth/TerrainOptions>
#include <osgGA/SphericalManipulator>

using namespace std;

void LoadShape()
{
	// Load the base map (globe)
	osg::ref_ptr<osg::Node> globe = osgDB::readNodeFile("../vs2022_64bit_3rdParty_osg365_oe32/runtime/test/earthFile/china-simple.earth");
	osg::ref_ptr<osgEarth::MapNode> mapNode = osgEarth::MapNode::get(globe);
	osg::ref_ptr<osgEarth::Map> map = mapNode->getMap();

	// Load the Shapefile
	osg::ref_ptr<osgEarth::OGRFeatureSource> features = new osgEarth::OGRFeatureSource;
	features->setURL("F:/osg/yangShiXing/019.Earth/builder/data/shpFile/world.shp");

	// Define the style for the features
	osgEarth::Style style;

	// Set visibility options
	osg::ref_ptr<osgEarth::RenderSymbol> render = style.getOrCreate<osgEarth::RenderSymbol>();
	render->depthTest() = false;

	// Set altitude options
	osg::ref_ptr<osgEarth::AltitudeSymbol> alt = style.getOrCreate<osgEarth::AltitudeSymbol>();
	alt->clamping() = alt->CLAMP_TO_TERRAIN;
	alt->technique() = alt->TECHNIQUE_DRAPE;

	// Set line color and width
	osg::Vec4 color(1.0f, 0.0f, 0.0f, 1.0f);
	osg::ref_ptr<osgEarth::LineSymbol> ls = style.getOrCreate<osgEarth::LineSymbol>();
	ls->stroke()->color() = color;
	ls->stroke()->width() = 2.0f;
	ls->tessellationSize()->set(10000.0, osgEarth::Units::KILOMETERS);

	// Add the feature source and style to a layer
	osg::ref_ptr<osgEarth::FeatureImageLayer> layer = new osgEarth::FeatureImageLayer;
	layer->setFeatureSource(features);

	osg::ref_ptr<osgEarth::StyleSheet> sheet = new osgEarth::StyleSheet;
	sheet->addStyle(style);
	layer->setStyleSheet(sheet);
	map->addLayer(layer);

	// Print the status of added layers
	osgEarth::LayerVector layers;
	map->getLayers(layers);
	for (osgEarth::LayerVector::const_iterator it = layers.begin(); it != layers.end(); it++)
	{
		std::cout << "Layer Status: " << (*it)->getStatus().toString() << std::endl;
	}

	// Set up the viewer
	osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
	viewer->setSceneData(mapNode);

	// Use EarthManipulator to control the camera
	osg::ref_ptr<osgEarth::EarthManipulator> manipulator = new osgEarth::EarthManipulator;
	// Adjust pitch constraints to allow free rotation at the poles
	manipulator->getSettings()->setMinMaxPitch(-90.0, 90.0);

	// Allow free rotation around the poles
	manipulator->getSettings()->setThrowingEnabled(false);
	manipulator->getSettings()->setLockAzimuthWhilePanning(false);
	//manipulator->getSettings()->setLockAzimuthWhileRotating(false);
	viewer->setCameraManipulator(new osgGA::SphericalManipulator);
	viewer->setCameraManipulator(manipulator);

	// Set the initial viewpoint to look at Chengdu
	// Longitude: 104.0668, Latitude: 30.5728
	osgEarth::Viewpoint vp("Chengdu", 104.0668, 30.5728, 1000000.0, 0.0, -45.0, 25000000.0);
	manipulator->setHomeViewpoint(vp);

	// Run the viewer
	viewer->setUpViewInWindow(100, 100, 800, 600);
	viewer->run();
}


int main()
{
	LoadShape();
	//osg::ref_ptr<osgEarth::OGRFeatureSource> features = new osgEarth::OGRFeatureSource;
	//features->setURL("F:/osg/yangShiXing/019.Earth/builder/data/shpFile/world.shp");
	//features->open();

	//osgEarth::Query query;
	//query.expression() = "POP_CNTRY = 67074";

	 创建进度回调(可以传递 nullptr 如果不需要进度回调)
	//osg::ref_ptr<osgEarth::ProgressCallback> progressCallback = nullptr;

	//osg::ref_ptr<osgEarth::FeatureCursor> cursor = features->createFeatureCursor(query, progressCallback);
	//const osgEarth::SpatialReference* intputSrs = features->getFeatureProfile()->getSRS();
	//const osgEarth::SpatialReference* outputSrs = osgEarth::SpatialReference::get("wgs84");
	//osg::ref_ptr<osg::Geode> gnode = new osg::Geode;
	//osg::Vec4 color(1.0f, 0.0f, 0.0f, 1.0f);


	//while (cursor->hasMore()){
	//	//osgEarth::Feature* feature = cursor->nextFeature();
	//	//std::string text = feature->getString("Cntry_name");
	//	//if (text.empty()){
	//	//	continue;
	//	//}
	//	//if (feature) {
	//	//	std::string name = feature->getString("NAME");
	//	//	std::string pop = feature->getString("POP_CNTRY");
	//	//	std::string area = feature->getString("AREA_CNTRY");
	//	//	//std::string geom = feature->getGeometry()->asText();
	//	//	std::cout << "Cntry_name:" << text << name << ", Population: " << pop << ", Area: " << area << ", Geometry: " <</* geom <<*/ std::endl;
	//	//}
	//	osg::ref_ptr<osgEarth::Feature> feature = cursor->nextFeature();
	//	//osg::ref_ptr<osgEarth::GeometryIterator> parts = feature->getGeometry()->intersects();
	//	osgEarth::GeometryIterator parts(feature->getGeometry(), false);
	//	while (parts.hasMore()){
	//		osgEarth::Geometry* part = parts.next();
	//		osg::ref_ptr<osg::Geometry> osgGeom = new osg::Geometry;
	//		osg::ref_ptr<osg::Vec3Array> allPoints = new osg::Vec3Array;
	//		//设置顶点
	//		int totalPoints = part->getTotalPointCount();
	//		//allPoints = part->toVec3Array();
	//		//osgEarth::ECEF::transformAndLocalize(part->asVector(), srs->getGeographicSRS(), allPoints);
	//		osg::Matrixd matrixTemp = osg::Matrixd::identity();
	//		osgEarth::ECEF::transformAndLocalize(part->asVector(), intputSrs, allPoints, outputSrs, matrixTemp);
	//		/*for (size_t i = 0; i < part->size(); i++) {
	//			osg::Vec3d ecef;
	//			srs->transform(part->at(i), ecef);
	//			allPoints->push_back(ecef);
	//		}*/
	//		osgGeom->setVertexArray(allPoints);
	//		//osgGeom->setPrimitiveSet(new osg::DrawArrays(GL_LINE_LOOP, 0, part->size()));
	//		osgGeom->addPrimitiveSet(new osg::DrawArrays(GL_LINE_LOOP, 0, part->size()));
	//		//设置颜色
	//		osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array(allPoints->size());
	//		for (size_t c = 0; c < colors->size(); c++){
	//			(*colors)[c] = color;
	//		}
	//		osgGeom->setColorArray(colors);
	//		osgGeom->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
	//		gnode->addDrawable(osgGeom);
	//	}
	//}

	//osgViewer::Viewer viewer;
	//viewer.setSceneData(gnode);
	//return viewer.run();

	return 0;
	
}

四、运行效果如下

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值