OSG笔记:设置DO_NOT_COMPUTE_NEAR_FAR,手动计算远近平面

问题

实现正交投影的操作杆时,缩放平移后,会产生图形部分消息的情况。没研究透osg自动计算算法,暂时认为自动计算远近平面对透视投影可能没啥问题,但正交投影则有。

思路(记录下,没有严格推敲,不保证正确性)

left, right, top, bottom使用模型包围球的半径即可。远近平面的距离思路如下图所示,z轴一半的长度需取最大可能的长度(即视点沿着视线到包围盒另一侧的距离),这样才能保证怎么旋转模型,模型都在以眼睛为坐标原点的观察范围内。
在这里插入图片描述

代码

#include <iostream>
#include <osgViewer/Viewer>
#include <osg/Group>
#include <osg/ShapeDrawable>
#include <osgGA/GUIEventHandler>
#include <osg/Viewport>
#include <osg/NodeCallback>
#include <osgGA/StandardManipulator>
#include <osgGA/EventVisitor>

using namespace std;


class MyGUIEventHandler : public osgGA::GUIEventHandler
{
public:
	virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa,
		osg::Object* pObject, osg::NodeVisitor* pNodeVisitor)
	{
		osgViewer::Viewer* pViewer = dynamic_cast<osgViewer::Viewer*>(aa.asView());
		if (!pViewer || !pViewer->getSceneData() )
		{
			return false;
		}
		osg::ref_ptr<osg::Camera> pCamera = pViewer->getCamera();
		if (!pCamera)
		{
			return false;
		}
		osg::Viewport* pViewport = pCamera->getViewport();
		if (!pViewport)
		{
			return false;
		}
		osgGA::StandardManipulator* pManipulator = dynamic_cast<osgGA::StandardManipulator*>(
			pViewer->getCameraManipulator());
		if (!pManipulator)
		{
			return false;
		}

		double aspecRadio = double(pViewport->width()) / double(pViewport->height());

		osg::BoundingSphere modelBound = pViewer->getSceneData()->getBound();
		double dModelHalfSize = modelBound.radius();
		static double s_dScale = 1.0;

		switch (ea.getEventType())
		{
		case osgGA::GUIEventAdapter::EventType::KEYDOWN:
		{
			bool bZoom = false;
			if (int(osgGA::GUIEventAdapter::KeySymbol::KEY_R) == ea.getKey())
			{
				s_dScale = 1.0; //恢复
				bZoom = true;
			}
			else if (int(osgGA::GUIEventAdapter::KeySymbol::KEY_D) == ea.getKey())
			{
				s_dScale *= 1.2; //缩小
				bZoom = true;
			}
			else if (int(osgGA::GUIEventAdapter::KeySymbol::KEY_U) == ea.getKey())
			{
				s_dScale *= 0.8;//放大
				bZoom = true;
			}
			else
			{
				bZoom = false; //修正远近平面
			}



			//计算眼睛到视点距离使用操作杆的观察矩阵,使用相机的结果不对
//			osg::Vec3d eye0;
//			osg::Vec3d center0;
//			osg::Vec3d up0;
//			pCamera->getViewMatrixAsLookAt(eye0, center0, up0);
//			double dDistance0 = (eye0 - center0).length();

			double left = 0.0;
			double right = 0.0;
			double bottom = 0.0;
			double top = 0.0;
			double zNear = 0.0;
			double zFar = 0.0;
			if (pViewer->getCamera()->getProjectionMatrixAsOrtho(left, right, bottom, top, zNear, zFar))
			{//正交投影

				osg::Vec3d eye1;
				osg::Vec3d center1;
				osg::Vec3d up1;
				pManipulator->getTransformation(eye1, center1, up1);
				double dDistance1 = (eye1 - center1).length();
				double dDistance2 = (center1 - modelBound.center()).length();

				std::cout << "dDistance1:" << dDistance1
					<< ", dDistance2:" << dDistance2 << std::endl;


				if (bZoom)//正交投影的缩放
				{
					double dHalfRange = dModelHalfSize * s_dScale;
					if (aspecRadio > 1.0)
					{
						pViewer->getCamera()->setProjectionMatrixAsOrtho(
							-dHalfRange*aspecRadio, dHalfRange*aspecRadio,
							-dHalfRange, dHalfRange, zNear * s_dScale, zFar * s_dScale);
					}
					else
					{
						pViewer->getCamera()->setProjectionMatrixAsOrtho(
							-dHalfRange, dHalfRange,
							-dHalfRange*aspecRadio, dHalfRange*aspecRadio,
							zNear * s_dScale, zFar * s_dScale);
					}
				}
				else //修正远近平面
				{
					double dHalfRangeZ = dDistance1 + dDistance2 + dModelHalfSize*0.7;
					pViewer->getCamera()->setProjectionMatrixAsOrtho(
						left, right, bottom, top, -dHalfRangeZ, dHalfRangeZ);
				}
			}

			break;
		}

		}
		return osgGA::GUIEventHandler::handle(ea, aa, pObject, pNodeVisitor);
	}


};



int main(int argc, char** argv)
{
	osgViewer::Viewer* pViewer = new osgViewer::Viewer();

	osg::ref_ptr<osg::Group> pRoot = new osg::Group();
	osg::ref_ptr<osg::Geode> pGeode = new osg::Geode();
	pRoot->addChild(pGeode);


	osg::ref_ptr<osg::Box> pBox = new osg::Box(osg::Vec3(0, 0, 0), 100.0);
	pGeode->addDrawable(new osg::ShapeDrawable(pBox));

	const osg::BoundingSphere& boundingSphere = pRoot->getBound();


	pViewer->addEventHandler(new MyGUIEventHandler());

	pViewer->setSceneData(pRoot);
	pViewer->getCamera()->setClearColor(osg::Vec4(0, 0, 0, 1.0));
	pViewer->setUpViewInWindow(450, 220, 720, 450);
	pViewer->realize();

	//不自动计算远近平面
	pViewer->getCamera()->setComputeNearFarMode(
		osg::CullSettings::ComputeNearFarMode::DO_NOT_COMPUTE_NEAR_FAR);

	//设置个默认的投影范围
	const osg::Viewport* pViewport = pViewer->getCamera()->getViewport();
	if (pViewport)
	{
		double dHalfRange = 100;
		double aspecRadio = double(pViewport->width()) / double(pViewport->height());
		if (aspecRadio > 1.0)
		{
			pViewer->getCamera()->setProjectionMatrixAsOrtho(
				-dHalfRange*aspecRadio, dHalfRange*aspecRadio, 
				-dHalfRange, dHalfRange, -dHalfRange, dHalfRange);
		}
		else
		{
			pViewer->getCamera()->setProjectionMatrixAsOrtho(
				-dHalfRange, dHalfRange,
				-dHalfRange*aspecRadio, dHalfRange*aspecRadio, -dHalfRange, dHalfRange);
		}
	}
	//aspecRadio
	

	pViewer->run();

	return 0;
}



截图

  • 轴测视图下,正方体,未计算正确远近平面
    在这里插入图片描述

  • 手动计算了远近平面后
    在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值