1

我需要实现轨迹球相机。我得到了类似的东西,但它的工作原理非常弯曲(角度急剧变化,向右/向左转动时,相机强烈地向上/向下抬起)。

这是我的源代码,你能告诉我哪里出错了:

bool get_arcball_vec(double x, double y, glm::vec3& a) 
{

  glm::vec3 vec = glm::vec3((2.0 * x) / window.getWidth() - 1.0, 1.0 - (2.0 * y) / window.getHeight(), 0.0);


 if (glm::length(vec) >= 1.0)
 {
   vec = glm::normalize(vec);
 }
 else 
 {
   vec.z = sqrt(1.0 - pow(vec.x, 2.0) - pow(vec.y, 2.0));
 } 
 a = vec;

return true;
}

...
void onMouseMove(double x, double y) {
  if (rightMouseButtonPressed) {
    glm::vec3 a,b;
    cur_mx = x;
    cur_my = y;
    if (cur_mx != last_mx || cur_my != last_my)
      if (get_arcball_vec(last_mx, last_my, a) && get_arcball_vec(cur_mx, cur_my, b))
        viewport.getCamera().orbit(a,b);
  
    last_mx = cur_mx;
    last_my = cur_my;

      ...

void Camera::orbit(glm::vec3 a, glm::vec3 b)
{
     forward = calcForward();
     right = calcRight();


    double alpha = acos(glm::min(1.0f, glm::dot(b, a)));


     glm::vec3 axis = glm::cross(a, b);


     glm::mat4 rotationComponent = glm::mat4(1.0f);

    rotationComponent[0] = glm::vec4(right, 0.0f);
    rotationComponent[1] = glm::vec4(up, 0.0f);
    rotationComponent[2] = glm::vec4(forward, 0.0f);


    glm::mat4 toWorldCameraSpace = glm::transpose(rotationComponent);


    axis = toWorldCameraSpace * glm::vec4(axis, 1.0);

    glm::mat4 orbitMatrix = glm::rotate(glm::mat4(1.0f), (float)alpha, axis);


    eye = glm::vec4(target, 1.0) + orbitMatrix * glm::vec4(eye - target, 1.0f);

    up = orbitMatrix * glm::vec4(up, 1.0f);
   }
4

1 回答 1

0

我使用此代码将 2D 鼠标位置映射到球体:

Vector3 GetArcBallVector(const Vector2f & mousePos) {

    float radiusSquared = 1.0; //squared radius of the sphere

    //compute mouse position from the centre of screen to interval [-half, +half]
    Vector3 pt = Vector3(
       mousePos.x - halfScreenW,
       halfScreenH - mousePos.y,
       0.0f
    );

    //if length squared is smaller than sphere diameter
    //point is inside
    float lengthSqr = pt.x * pt.x + pt.y * pt.y;
            
    if (lengthSqr < radiusSquared){
        //inside        
       pt.z = std::sqrtf(radiusSquared - lengthSqr);
    }
    else {              
       pt.z = 0.0f;     
    }

    pt.z *= -1;
    return pt;
}

为了计算旋转,我使用最后一个 ( startPt) 和当前 ( endPt) 映射位置并执行以下操作:

Quaternion actRot = Quaternion::Identity();
Vector3 axis = Vector3::Cross(endPt, startPt);
    
if (axis.LengthSquared() > MathUtils::EPSILON) {        
    float angleCos = Vector3::Dot(endPt, startPt);
    actRot = Quaternion(axis.x, axis.y, axis.z, angleCos);      
}

我更喜欢在矩阵上使用四元数,因为它们很容易相乘(用于累积旋转)和插值(用于一些 smooting)。

于 2020-09-04T11:55:41.960 回答