【OpenGL编程】摄像机漫游

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ModestBean/article/details/79130876

#OpenGL/ES摄像机漫游
##说在前面:
之前的学习当中只是实现了简单的XZ平面的摄像机旋转,对于三维空间的旋转还需要学习一些新的知识,主要涉及两方面的知识:1、欧拉角的概念2、三维空间的数学计算。本节的代码都可在我的github上进行下载。https://github.com/ModestBean/MySample/tree/master/CameraRoam如果本节内容有错误和不合理之处,还请朋友们多多指出,我会虚心接受每一个建议。
本节内容主要参考:
LearnOpenGL的摄像机一章中的欧拉角的概念。
https://learnopengl-cn.github.io/01 Getting started/09 Camera/#_1
##什么是摄像机漫游?
已下gif实现的即为三维空间的漫游。
这里写图片描述
##什么是欧拉角?(借鉴于LearnOpenGL)
欧拉角使可以表示三维空间中任何旋转的三个值,一共有三种欧拉角,俯仰角,偏航角,和滚转角。下面的图片展示了它们的含义:
这里写图片描述

  • 俯仰角(仰视角):绕X轴旋转的角,在OpenGL/ES中通常表示往上看或者往下看的角,将摄像机抽象成人的头代表的也就是人的仰视角。
  • 偏航角(左右角):绕Y旋转的角,在OpenGL/ES中通常表示往左或者往右看的角,代表人的头部左右旋转。
  • 滚转角:绕Z轴旋转的角,在OpenGL/ES中通常表示可以360度旋转的角,无法代表人的头部,因为人的头部是无法实现旋转360度的,通常情况下不会使用使用滚转角,除非有特许需求。
    ##一个摄像机漫游例子
    在介绍漫游之前,我们还需要了解设置摄像机的方法,在OpenGL/ES中,为了方便和简化摄像机矩阵,我们会设置摄像机相关的3个因素:摄像机位置,目标位置(眼睛的观察点),和与人头部保持一个方向的向量(通常叫它UP向量),当然,这些位置向量都是在世界坐标系考虑的。通过这三个因素去生成摄像机矩阵(关于生成摄像机矩阵的相关内容,可以参考LearnOpenGL中的章节),下图比较直观的表示了摄像机的三个因素。
    这里写图片描述
    ##摄像机三维旋转部分
    先给出一段限制俯仰角和偏航角的方法,在这里传入的两个参数分别是y方向的偏移距离(手指竖着划动的距离)和x方向的偏移距离(手指横着划动的距离)。在这里,我限制了俯仰角为090度。偏航角为0360度。
/*
 * @param yjSpan y方向的偏移距离
 * @param cxSpan x方向的偏移距离
 * */
void CameraUtil::calCamera(float yjSpan, float cxSpan) {
    //限制俯仰角度
    yj = yj + yjSpan;
    if(yj>=90){
        yj=90;
    }if(yj<=0){
        yj=0;
    }
    //限制偏航角角度
    degree = degree + cxSpan;
    if (degree >= 360) {
        degree = degree - 360;
    }
    else if (degree <= 0) {
        degree = degree + 360;
    }
    calCamera();//重新计算摄像机姿态的方法
}

我绘制了相关三维图像方便理解。首先是动态计算摄像机的位置cx,cy,cz,图像如下。为了方便理解,我将目标点放到了原点。摄像机是在以目标点为中心,半径为R的球面上运动的,我绘制的只是某一个状态,不过这也足够了。接着就是对位置的分解了,我相信这难不倒大家
这里写图片描述
这就是动态计算摄像机位置的代码部分,其中涉及到角度转弧度的知识,我便不再介绍了。这里便是通过编程实现上面的简单算法了。

    //计算当前观察角度下摄像机的位置(基于俯仰角重新计算 X轴为旋转轴)
     cy = float(sin(yj*3.1415926535898 / 180)*CAMERA_R + ty);
    float cxz = float(cos(yj*3.1415926535898 / 180)*CAMERA_R);
    //(基于偏航角重新计算摄像机位置 Y轴为旋转轴)
     cx = float(sin(degree*3.1415926535898 / 180)*cxz + tx);
     cz = float(cos(degree*3.1415926535898 / 180)*cxz + tz);

然后就是动态计算UP向量的部分了,这与计算摄像机位置的部分大同小异,我就不再进行分解了,几何图如下。
这里写图片描述
计算UP向量的代码。

//计算当前摄像机的UP向量
    float upY = float(cos(yj*3.1415926535898 / 180));
    float upXZ = float(sin(yj*3.1415926535898 / 180));
    float upX = float(-upXZ*sin(degree*3.1415926535898 / 180));
    float upZ = float(-upXZ*cos(degree*3.1415926535898 / 180));

##摄像机三维移动部分
通过以上我绘制的几何图相信已经很好理解整个过程了,下面是摄像机前后所有移动的相关几何图。
这里写图片描述
通过上面的几何分解就可得到移动的距离了,非常简单、我直接给出摄像机前后左右的移动代码。

/*
 * @param goBack 前进或者后退的距离
 * @param leftRight 左右移动的距离
 * */
void CameraUtil::cameraGo(float goBack, float leftRight){
	//计算X方向位移
    float xStep = float(-goBack*sin(degree*3.1415926535898 / 180) - leftRight*sin((degree + 90)*3.1415926535898 / 180)); 
	//计算Z方向位移
    float zStep = float(-goBack*cos(degree*3.1415926535898 / 180) - leftRight*cos((degree + 90)*3.1415926535898 / 180)); 
    tx = tx + xStep;
    tz = tz + zStep;
    calCamera();
}

最后是应用的代码啦,屏幕上X,Y方向的偏移量的大小与俯仰角和偏航角的大小是成正比的,这里这是计算了X,Y两个放向的偏移量,并传给了摄像机类。如下

				int x=AMotionEvent_getRawX(event,0);
				int y=AMotionEvent_getRawY(event,0);
				int32_t id = AMotionEvent_getAction(event);
				switch(id){
					case AMOTION_EVENT_ACTION_DOWN://按下事件
						xPre=x;
						yPre=y;
						isClick = true;
					break;
					case AMOTION_EVENT_ACTION_MOVE://滑动事件
						xDis=x-xPre;
						yDis=y-yPre;
						if (myabs(xDis)>5 || myabs(yDis)>5){//判断触控点位移是否超过阈值
							isClick = false;
						}
						if (!isClick){
							CameraUtil::calCamera(yDis*180.0f / 600, xDis*180.0f / 600);
						}
						xPre=x;
						yPre=y;
					break;
					case AMOTION_EVENT_ACTION_UP://抬起事件
						#define MOVE_SPAN 10
						if (isClick){
							if (x < MyVulkanManager::screenWidth / 4){//左移
								CameraUtil::cameraGo(0, MOVE_SPAN);
							}
							else if (x > MyVulkanManager::screenWidth * 3 / 4){//右移
								CameraUtil::cameraGo(0, -MOVE_SPAN);
							}
							else if (y < MyVulkanManager::screenHeight / 2){//前移
								CameraUtil::cameraGo(MOVE_SPAN, 0);
							}
							else{
								CameraUtil::cameraGo(-MOVE_SPAN, 0);//后移
							}
						}
					break;
				}

##写在最后:
本小节主要基于OpenGL ES介绍了实现摄像机的三维漫游,如果本节内容有错误和不合理之处,还请朋友们多多指出,我会虚心接受每一个建议。

猜你喜欢

转载自blog.csdn.net/ModestBean/article/details/79130876