图形学第一人称摄像机算法

版权声明:未经同意,不能用于商业用途,版权归本博主所有 https://blog.csdn.net/qq_16123279/article/details/83090902

本算法是从CryEngine里面扒下来的,CryEngine里面使用的矩阵为列矩阵。而我扒下来是要用到OSG,OSG是行矩阵,所以所有算法涉及到取矩阵元素的行列位置做了颠倒。比如在CryEngine中取矩阵x行y列元素为M(x,y),对应到OSG中应该是M(y,x)。不知道什么是行列矩阵的请参考我的博客图形学中的行矩阵和列矩阵

欢迎加入QQ群:457378561 讨论图形相关知识


在行矩阵中第一人称摄像机操作算法

所谓摄像机就是在3D图形学中观察场景所需要的东西,对应观察矩阵viewMatrix(viewMatrix=lookat(…))。在图形管线中观察矩阵起到把模型空间变换到相机空间的作用。但是一般仅仅用lookat很难满足日常需求,所以就抽象了一个相机实体,这个实体有自己的矩阵叫CamreMatrix,通过CamreMatrix就可以操作相机(移动、旋转这个相机实体),然后再对CamreMatrix求其逆矩阵就是viewMatrix了。然后再将viewMatrix放到管线中就完成了对相机的操作。

鼠标操作相机的方向

/*
@	功能:从相机矩阵创建欧拉角(cameraMatrix=Inverse(ViewMatrix));
@	参数[m]:为相机矩阵;
@	返回值:Vec3.x()为Yaw,Vec3.y()为Pitch,Vec3.z()为Roll;
*/
template<typename Vec3,typename Matrix>
static Vec3		CreateAnglesYPR(const Matrix& m)
{
	float l = Vec3(m(1, 0), m(1, 1), 0.0f).length();
	if (l > 0.0001)
		return Vec3(atan2f(-m(1, 0) / l, m(1, 1) / l), atan2f(m(1, 2), l), atan2f(-m(0, 2) / l, m(2, 2) / l));
	else
		return Vec3(0, atan2f(m(1, 2), l), 0);
}

/*
@	功能:从欧拉角创建旋转四元数
@	参数[ypr]: 欧拉角
@	返回值:旋转四元数
*/
template<typename Quat, typename Vec3>
static  Quat		CreateOrientationYPR(const Vec3& ypr)
{
	auto sincos = [](float angle, float& s, float& c)
	{
		s = sinf(angle);
		c = cosf(angle);
	};

	float sz, cz;
	sincos(ypr.x(), sz, cz);            //!< Zaxis = YAW.
	float sx, cx;
	sincos(ypr.y(), sx, cx);            //!< Xaxis = PITCH.
	float sy, cy;
	sincos(ypr.z(), sy, cy);            //!< Yaxis = ROLL.
	Matrix c;
	c(0, 0) = cy * cz - sy * sz * sx;
	c(1, 0) = -sz * cx;
	c(2, 0) = sy * cz + cy * sz * sx;
	c(0, 1) = cy * sz + sy * sx * cz;
	c(1, 1) = cz * cx;
	c(2, 1) = sy * sz - cy * sx * cz;
	c(0, 2) = -sy * cx;
	c(1, 2) = sx;
	c(2, 2) = cy * cx;
	return c.getRotate();
}

键盘操作相机的位移

/*
@	功能: 将一个局部偏移向量转化成当前相机所朝向方向的偏移
@   参数[mat]: 相机矩阵
@	参数[v]:局部偏移向量
@   返回值:相机所朝向方向的偏移
*/
template<typename Vec4, typename Matrix,typename Vec3>
static Vec4		TransformVector(const Matrix& mat, const Vec3& v)
{
	Vec4 x = GetMatrixColumn<Vec4>(mat, 0);
	Vec4 y = GetMatrixColumn<Vec4>(mat, 1);
	Vec4 z = GetMatrixColumn<Vec4>(mat, 2);

	Vec4 tmp0 = Vec4(x.x(), y.x(), x.y(), y.y());
	Vec4 tmp1 = Vec4(x.z(), y.z(), x.w(), y.w());
	Vec4 tmp2 = Vec4(z.x(), 0, z.y(), 0);
	Vec4 tmp3 = Vec4(z.z(), 0, z.w(), 0);

	auto cc = [](const Vec4& a, const Vec4& b)->Vec4
	{
		Vec4::value_type x = a.x()*b.x();
		Vec4::value_type y = a.y()*b.y();
		Vec4::value_type z = a.z()*b.z();
		Vec4::value_type w = a.w()*b.w();

		Vec4 res = Vec4(x, y, z, w);
		return res;
	};

	Vec4 r = cc(Vec4(v.x(), v.x(), v.x(), 0.0), Vec4(tmp0.x(), tmp0.y(), tmp2.x(), tmp2.y()))
				+ cc(Vec4(v.y(), v.y(), v.y(), 0.0), Vec4(tmp2.z(), tmp2.w(), tmp0.z(), tmp0.w()))
				+ cc(Vec4(v.z(), v.z(), v.z(), 0.0), Vec4(tmp1.x(), tmp1.y(), tmp3.x(), tmp3.y()));

	return r;
}

/*
@	功能:从一个矩阵中取出指定列向量,索引从0开始
@	参数[mat]: 目标矩阵
@   参数[column]: 矩阵的第几行
@	返回值: 取出来的列向量
*/
template<typename Vec4, typename Matrix>
static Vec4		GetMatrixColumn(const Matrix& mat, size_t column)
{
	Vec4 columnVector;
	columnVector.x() = mat(0, column);
	columnVector.y() = mat(1, column);
	columnVector.z() = mat(2, column);
	columnVector.w() = mat(3, column);

	return columnVector;
}

/*
@	功能: 在矩阵平移参数加上一个偏移
@	参数[m]: 需要偏移的矩阵
@   参数[t]: 偏移向量 
*/
template<typename Vec3, typename Matrix>
static void AddTranslation(Matrix& m, const Vec3& t)
{
	m(3, 0) += t.x();
	m(3, 1) += t.y();
	m(3, 2) += t.z();
}

调用方法

//获取相机自身矩阵
Matrix WorldTM = view->getCameraManipulator()->getMatrix();
//CAMERA DIR
if (IsMouseMoving())
{
	//m_currentEA为当前帧的信息记录对象
	//m_preEA为前一帧的信息记录对象
	float dx = m_currentEA->getXnormalized() - m_preEA->getXnormalized();
	float dy = m_currentEA->getYnormalized() - m_preEA->getYnormalized();

	//dx鼠标X位移差值
	//dy鼠标Y位移差值
	if (dx != 0. || dy != 0.)
	{
		//从相机矩阵中获取欧拉角yaw、pitch、roll
		osg::Vec3 ypr = Math::Math3DAlgorithm::CreateAnglesYPR<osg::Vec3>(WorldTM);
		ypr.x() -= dx;
		ypr.y() += dy;
		ypr.z() = 0;
		//将欧拉角转换为四元数并设置成相机矩阵的旋转分量	
		WorldTM.setRotate(Math::Math3DAlgorithm::CreateOrientationYPR<osg::Quat>(ypr));
	}
}

//Camera pos
case osgGA::GUIEventAdapter::KEYDOWN:
{
	if (m_currentEA&&m_preEA)
	{
		double fFrameTime = m_currentEA->getTime() - m_preEA->getTime();
		//camera pos
		osg::Vec3f velocity = osg::Vec3f(0, 0, 0);

		if (osgGA::GUIEventAdapter::KEY_W == ea.getKey())//W
		{
			velocity.z() = velocity.z() - m_uMoveSpeed*(fFrameTime);
		}
		if (osgGA::GUIEventAdapter::KEY_S == ea.getKey())//S
		{
			velocity.z() = velocity.z() + m_uMoveSpeed*(fFrameTime);
		}
		if (osgGA::GUIEventAdapter::KEY_A == ea.getKey())//A
		{
			velocity.x() = velocity.x() - m_uMoveSpeed*(fFrameTime);
		}
		if (osgGA::GUIEventAdapter::KEY_D == ea.getKey())//D
		{
			velocity.x() = velocity.x() + m_uMoveSpeed*(fFrameTime);
		}

		//osg::Vec3 pos = WorldTM.getTrans();
		//WorldTM.setTrans(pos + velocity);
		osg::Vec4 offset = Math::Math3DAlgorithm::TransformVector<osg::Vec4>(WorldTM, velocity);
		Math::Math3DAlgorithm::AddTranslation(WorldTM, offset);
	}
	break;
}

bool IsMouseMoving()
{
	if (m_currentEA.get() == NULL || m_preEA.get() == NULL) return false;

	static const float velocity = 0.1f;

	float dx = m_currentEA->getXnormalized() - m_preEA->getXnormalized();
	float dy = m_currentEA->getYnormalized() - m_preEA->getYnormalized();
	float len = sqrtf(dx*dx + dy*dy);
	float dt = m_currentEA->getTime() - m_preEA->getTime();

	return (len > dt*velocity);
}

//设置相机矩阵
view->getCameraManipulator()->setByMatrix(WorldTM);

猜你喜欢

转载自blog.csdn.net/qq_16123279/article/details/83090902
今日推荐