• 【OpenGL】杂谈一、通过鼠标拖拽实现相机绕空间中的某点进行球面旋转查看


    1. 前言

    这是我最近遇到的一个问题,如题所示,我需要通过鼠标拖拽实现相机绕点的球面旋转,原本的想法很简单,类似笔记八、摄像机中提到的那样,从聚焦中心点的视角出发,将鼠标移动的距离xoffset和yoffset转换为yaw和pitch角,然后计算出该视角的向量,与球面求交从而得到相机的当前位置,但是实际过程中出现了一个比较严重的问题,就是欧拉角的致命缺陷——万向锁,如果pitch角超过±90°,我们就会遇到各种问题,在设计第一视角时我们可以控制pitch角在90°到-90°之间,这不影响我们的体验,但是如果我们要控制相机进行球面旋转的话,这样偷懒可能就不对了,那么能不能有别的方法来实现这种效果呢?当然是有的,一种是四元数,它可以实现球面旋转的平滑插值过渡,但是今天我要介绍另一种更为直观的实现方法

    2. 相机的球面旋转

    同样的,我们需要记录每次鼠标移动的距离xoffset和yoffset,这两个值该怎么去运用呢?
    在这里插入图片描述
    可以画出示意图:
    在这里插入图片描述
    其中A和B是相机移动前后的点,O点是我们相机的聚焦点,那么现在要做的事就很明显了,原本相机的视角是平行的圆面,我们把它看作投影平面,而移动相机时,我们是在OAB形成的圆面上移动,那么我们的任务就是利用OA求得OB

    首先我们计算出OA向量,求出当前的旋转半径

    	void updateCameraVectors()
        {
            if (xoffset == 0 && yoffset == 0)
                return;
            glm::vec3 target, offset;
            target = Position - Front;
            double radius = glm::length(target);
            target = glm::normalize(target);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    之后我们将当前相机向上方向与OA向量叉乘得到投影面的右向量

            Right = glm::normalize(glm::cross(Up, target));
    
    • 1

    其次再用OA向量与求得的叉乘得到投影面的上向量

            Up = glm::normalize(glm::cross(target, Right));
    
    • 1

    然后我们求屏幕位移在投影面上对应的向量AB(将投影面的右向量与上向量分别乘以xy位移值并相加)

            offset = Up * yoffset + Right * xoffset ;
    
    • 1

    之后我们将位移的的弧长转换为OAB圆的弧度并取负,保证物体的相对旋转方向与鼠标推拽方向一致(相机旋转方向与鼠标位移方向相反)

            double len = glm::length(offset);
            double angle = -len / radius;
            offset = glm::normalize(offset);
    
    • 1
    • 2
    • 3

    最后我们计算新的target向量OB = (OAcos(OAB)+ABsin(OAB)),并将它乘以半径的长度加在聚焦点,从而得到我们的相机位置

            double cr = cos(angle) * radius, sr = sin(angle) * radius;
            Position = Front + glm::vec3(target.x * cr + offset.x * sr, target.y * cr + offset.y * sr, target.z * cr + offset.z * sr);
    	}
    
    • 1
    • 2
    • 3

    3.效果

    在这里插入图片描述

  • 相关阅读:
    概要设计:描绘软件结构的图形工具,结构图既能表示模块间的数据信息、控制信息,也能表示选择调用和循环调用关系。
    Flutter 打包APK aab
    丢掉乱七八糟的软件,留下这4个,让你告别索然无味
    c++ trivial, standard layout和POD类型解析
    【LeetCode:689. 三个无重叠子数组的最大和 | 序列dp+前缀和】
    竞赛 题目:基于LSTM的预测算法 - 股票预测 天气预测 房价预测
    Ubuntu22.04如何开机重新自动运行脚本
    Hive数据查询语言-DQL-含示例演练(Select查询数据、Join查询)
    RabbitMQ官方案例学习记录
    scala基础
  • 原文地址:https://blog.csdn.net/ycrsw/article/details/125326511