欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 产业 > OpenGL实现摄像机(根据鼠标位置放大缩小视图)

OpenGL实现摄像机(根据鼠标位置放大缩小视图)

2025/5/15 1:50:57 来源:https://blog.csdn.net/qq_45526401/article/details/146244462  浏览:    关键词:OpenGL实现摄像机(根据鼠标位置放大缩小视图)

文章目录

  • 摄像机类
  • 鼠标左击
  • 鼠标右击
  • 滚轮滚动

 在一个场景中最重要的就是和用户的交互,所以实现一个摄像机对整个程序的影响是很大的,本文旨在实现比较实用的摄像机。主要包括的功能为:按下鼠标左键对整个场景进行移动,按下鼠标右键对整个场景进行旋转操作,根据鼠标位置滚动滚轮进行视图的放大缩小。

摄像机类

 在OpenGL中定义一个摄像机坐标系需要有三个元素,摄像机的位置,摄像机的视点,摄像机向上的向量,又这三个元素即可算出摄像机矩阵,但是在我们的摄像机中需要管理投影矩阵和模型矩阵,所以定义了以下变量。

        real3       _eye;real3       _up;real3       _right;real3       _target;real3       _dir;matrix4r    _matView;matrix4r    _matProj;matrix4r    _matWorld;real2       _viewSize;real3       _oldLength;

这是他的初始化

        CELLCamera(const real3& target = real3(0,0,0),const real3& eye = real3(0,100,100),const real3& right = real3(1,0,0)){_viewSize   =   real2(256,256);_matView    =   CELL::matrix4r(1);_matProj    =   CELL::matrix4r(1);_matWorld   =   CELL::matrix4r(1);_oldLength  =   10;_target     =   target;_eye        =   eye;_dir        =   normalize(_target - _eye);_right      =   right;_up         =   normalize(cross(_right,_dir));}~CELLCamera(){}

其中定义了一些重要的函数我来解释一下。
首先是将世界坐标转化为窗口坐标

  /***   世界坐标转化为窗口坐标*/bool    project( const real4& world, real4& screen ){screen  =   (_matProj * _matView * _matWorld) * world;if (screen.w == 0.0f){return false;}screen.x    /=  screen.w;screen.y    /=  screen.w;screen.z    /=  screen.w;// map to range 0 - 1screen.x    =   screen.x * 0.5f + 0.5f;screen.y    =   screen.y * 0.5f + 0.5f;screen.z    =   screen.z * 0.5f + 0.5f;// map to viewportscreen.x    =   screen.x * _viewSize.x;screen.y    =   _viewSize.y - (screen.y * _viewSize.y);return  true;}

 我们都知道一般我们每个模型都有对应自己的坐标,我们需要经过坐标变换后才能显示在对应的OpenGL的窗口中,这部分可以参考一下ai的解释
1.坐标变换流程概览
世界坐标到屏幕坐标的转换需要经过以下5个阶段的矩阵变换:
世界坐标 → 观察坐标 → 裁剪坐标 → 标准化设备坐标(NDC) → 屏幕坐标
每个阶段对应不同的矩阵操作和空间映射。
​2. 具体转换步骤
​(1) 模型视图变换(世界坐标 → 观察坐标)
​作用:将物体从世界坐标系转换到相机视角坐标系。
​矩阵操作:
​模型矩阵(Model Matrix)​:处理物体的平移、旋转、缩放(世界空间变换)。
​视图矩阵(View Matrix)​:通过gluLookAt或手动设置相机位置、观察点和上方向量,定义观察者的视角。

​(2) 投影变换(观察坐标 → 裁剪坐标)
​作用:将3D场景投影到2D视平面上,并确定可见范围。
​投影类型:
​透视投影​(glm::perspective):模拟人眼视角,近大远小,适合3D场景。
​正交投影​(glm::ortho):保持物体尺寸不变,适合2D UI或CAD工具。

​(3) 透视除法(裁剪坐标 → 标准化设备坐标)​
​作用:将齐次坐标转换为归一化设备坐标(NDC),范围[-1, 1]。
超出[-1,1]范围的坐标将被裁剪。
​(4) 视口变换(NDC → 屏幕坐标)​
​作用:将NDC坐标映射到实际屏幕像素位置。
​参数设置:
通过glViewport(x, y, width, height)定义视口区域。

        Ray createRayFromScreen(int x,int y){real4  minWorld;real4  maxWorld;real4  screen(real(x),real(y),0,1);real4  screen1(real(x),real(y),1,1);unProject(screen,minWorld);unProject(screen1,maxWorld);Ray     ray;ray.setOrigin(real3(minWorld.x,minWorld.y,minWorld.z));real3  dir(maxWorld.x - minWorld.x,maxWorld.y - minWorld.y, maxWorld.z - minWorld.z);ray.setDirection(normalize(dir));return  ray;}

 根据鼠标位置创建一条射线。

鼠标左击

 鼠标左击实现的是鼠标左键按下拖动的时候场景进行平移,主要的实现思想是记录下当前的鼠标位置和上一次鼠标的位置,将摄像机的位置和摄像机的视点位置改变就行。
 首先根据鼠标点击的位置求出与场景的交点,再根据这个交点和原位置得出一个差改变位置即可。
首先是鼠标按下事件

            if (id == CELL::MouseButton::Left){Ray     ray =   _camera.createRayFromScreen(absx,absy);float3  pos     =   ray.getOrigin();float   tm      =   abs((pos.y - 0) / ray.getDirection().y); float3  target  =   ray.getPoint(tm);_role.setTarget(float3(target.x,0,target.z));_leftButtonDown = true;_ptMouseDown = int2(absx, absy);}

 首先根据鼠标的位置算出一条射线,然后根据这条射线计算出与场景的交点并记录,在鼠标移动的时候改变摄像机的位置和视点位置。

            if (_leftButtonDown){int2    pos(absx, absy);/***   首先计算出来一个像素和当前场景的比例*/Ray   ray0 = _camera.createRayFromScreen(pos.x, pos.y);Ray   ray1 = _camera.createRayFromScreen(_ptMouseDown.x, _ptMouseDown.y);real3  pos0 = calcIntersectPoint(ray0);real3  pos1 = calcIntersectPoint(ray1);real3  offset = pos1 - pos0;if (offset.x == 0 && offset.y == 0 && offset.z == 0){//offset = float3(0.1f, 0, 0.1f);}_ptMouseDown = pos;real3  newEye = _camera.getEye() + offset;real3  newTgt = _camera.getTarget() + offset;_camera.setEye(newEye);_camera.setTarget(newTgt);_camera.update();}

鼠标右击

 鼠标右击和鼠标左击也是一样的,都是获取到与场景的交点后改变视图矩阵。
鼠标按下事件

            else if( id== CELL::MouseButton::Right){_mousePos           =   float2(absx,absy);_rightButtonDown    =   true;///  计算和地面的交点Ray     ray =   _camera.createRayFromScreen(absx,absy);float3  pos     =   ray.getOrigin();float   tm      =   abs((pos.y - 0) / ray.getDirection().y); _mouseRot       =   ray.getPoint(tm);_mouseRot.y     =   0;}

鼠标移动事件

            if(_rightButtonDown){float2  curPos(absx,absy);float2  offset      =   curPos - _mousePos;_mousePos   =   curPos;_camera.rotateViewYByCenter(offset.x * 0.5f,_mouseRot);_camera.rotateViewXByCenter(offset.y * 0.5f,_mouseRot);//_camera.rotateViewX(offset.y * 0.5f);_camera.update();}
        virtual void    rotateViewYByCenter(real angle,real3  pos){real        len(0);real        len1(0);matrix4r    mat(1);mat.rotate(angle, real3(0, 1, 0));real3   vDir = pos - _eye;len1    =   CELL::length(vDir);vDir    =   CELL::normalize(vDir);vDir    =   vDir * mat;_eye    =   pos - vDir * len1;_dir    =   _dir * mat;_up     =   _up * mat;_right  =   CELL::normalize(cross(_dir, _up));len     =   CELL::length(_eye - _target);_target =   _eye + _dir * len;_matView=   CELL::lookAt<real>(_eye, _target, _up);}
        virtual void    rotateViewXByCenter(real angle,real3  pos){//! 计算眼睛到鼠标点的方向real3   vDir    =   pos - _eye;/// 得到摄像机和旋转点之间的距离real    len1    =   length(vDir);/// 得到摄像机和旋转点之间方向vDir    =   normalize(vDir);real    len     =   0;/// 产生旋转矩阵matrix4r mat(1);mat.rotate(angle, _right);vDir    =   vDir * mat;/// 计算眼睛的位置_eye    =   pos - vDir * len1;/// _dir    =   _dir * mat;_up     =   _up * mat;_right  =   CELL::normalize(CELL::cross(_dir, _up));len     =   CELL::length(_eye - _target);/// 根据眼睛的位置计算观察点的位置_target     =   _eye + _dir * len;_matView    =   CELL::lookAt<real>(_eye, _target, _up);}

滚轮滚动

 要实现根据根据鼠标位置对视图进行放大缩小,首先需要获取到放置的鼠标与场景的交点。

            if (absz){real    persent =   absz > 0 ? 1.1f : 0.9f;Ray     ray =   _camera.createRayFromScreen(absx,absy);float3  pos     =   ray.getOrigin();float   tm      =   abs((pos.y - 0) / ray.getDirection().y); float3  center  =   ray.getPoint(tm);center.y    =   0;_camera.scaleCameraByPos(center,persent);}
        /***	指定点推进摄像机*/virtual void    scaleCameraByPos(const real3& pos,real persent){real3   dir     =   CELL::normalize(pos - _eye);real    dis     =   CELL::length(pos - _eye) * persent;real    disCam  =   CELL::length(_target - _eye) * persent;real3   dirCam  =   CELL::normalize(_target - _eye);_eye    =   pos - dir * dis;_target =   _eye + dirCam * disCam;update();}

 首先需要注意的是摄像机看向的方向是一直没变,否则会出现视角一直在转动偏移的情况,先通过摄像机的位置和求出的交点位置计算出方向和距离后求出摄像机的位置,在通过摄像机的位置和方向求出视点位置,在重新计算视图矩阵即可

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词