欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【OpenGL编程】摄像机漫游

程序员文章站 2022-03-05 14:49:33
...

#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实现的即为三维空间的漫游。
【OpenGL编程】摄像机漫游
##什么是欧拉角?(借鉴于LearnOpenGL)
欧拉角使可以表示三维空间中任何旋转的三个值,一共有三种欧拉角,俯仰角,偏航角,和滚转角。下面的图片展示了它们的含义:
【OpenGL编程】摄像机漫游

  • 俯仰角(仰视角):绕X轴旋转的角,在OpenGL/ES中通常表示往上看或者往下看的角,将摄像机抽象成人的头代表的也就是人的仰视角。
  • 偏航角(左右角):绕Y旋转的角,在OpenGL/ES中通常表示往左或者往右看的角,代表人的头部左右旋转。
  • 滚转角:绕Z轴旋转的角,在OpenGL/ES中通常表示可以360度旋转的角,无法代表人的头部,因为人的头部是无法实现旋转360度的,通常情况下不会使用使用滚转角,除非有特许需求。
    ##一个摄像机漫游例子
    在介绍漫游之前,我们还需要了解设置摄像机的方法,在OpenGL/ES中,为了方便和简化摄像机矩阵,我们会设置摄像机相关的3个因素:摄像机位置,目标位置(眼睛的观察点),和与人头部保持一个方向的向量(通常叫它UP向量),当然,这些位置向量都是在世界坐标系考虑的。通过这三个因素去生成摄像机矩阵(关于生成摄像机矩阵的相关内容,可以参考LearnOpenGL中的章节),下图比较直观的表示了摄像机的三个因素。
    【OpenGL编程】摄像机漫游
    ##摄像机三维旋转部分
    先给出一段限制俯仰角和偏航角的方法,在这里传入的两个参数分别是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的球面上运动的,我绘制的只是某一个状态,不过这也足够了。接着就是对位置的分解了,我相信这难不倒大家
【OpenGL编程】摄像机漫游
这就是动态计算摄像机位置的代码部分,其中涉及到角度转弧度的知识,我便不再介绍了。这里便是通过编程实现上面的简单算法了。

    //计算当前观察角度下摄像机的位置(基于俯仰角重新计算 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向量的部分了,这与计算摄像机位置的部分大同小异,我就不再进行分解了,几何图如下。
【OpenGL编程】摄像机漫游
计算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));

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

/*
 * @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介绍了实现摄像机的三维漫游,如果本节内容有错误和不合理之处,还请朋友们多多指出,我会虚心接受每一个建议。