WebGL:如何在更改旋转中心时防止相机反弹

时间:2013-04-08 04:50:51

标签: javascript math graphics 3d webgl

我遇到了各种各样的心理障碍,并且正在寻找一些建议或建议。我的问题是:

我有一个WebGL场景(我没有使用第三方库,除了gl-matrix),用户可以上下左右旋转摄像机(绕X / Y轴旋转)。它们也可以旋转模型(偏航/俯仰)。

要查看问题,假设模型在场景中有两个块A和B,A位于中心,B位于右侧(在视口中),旋转中心位于A的中心。用户旋转模型,它围绕块A的中心旋转。但是如果用户点击对象B,我需要能够将旋转中心更改为B的中心,但仍然保持当前的摄像机方向。目前,当旋转中心切换到B时,块B移动到屏幕的中心,块A移动到左侧。基本上,代码总是以当前中心或旋转为中心。

我使用以下代码进行模型视图矩阵更新:

var mvMatrix = this.mvMatrix;

mat4.identity(mvMatrix);

mat4.translate(mvMatrix, mvMatrix, this.orbit);
mat4.rotateY(mvMatrix, mvMatrix, this.orbitYaw);
mat4.rotateX(mvMatrix, mvMatrix, this.orbitPitch);

mat4.translate(mvMatrix, mvMatrix, this.eye);
mat4.rotateY(mvMatrix, mvMatrix, this.eyeYaw);
mat4.rotateX(mvMatrix, mvMatrix, this.eyePitch);

我正在试图找出轨道和眼睛的正确偏航和俯仰值,以便向后移动当前位置并实现当前相机/眼睛方向以避免来自一个物体的“反弹”当旋转中心移动时,到另一个。

我搜索了很多,似乎无法找到最好的方法(我目前的尝试有问题​​)。任何示例代码,或只是良好的描述将不胜感激。

修改

我遵循了gman的建议并尝试了以下代码,但切换轨道只是跳了起来。我的模型由多个物体组成,轨道中心可以改变,但是在改变轨道后,摄像机的方向需要保持稳定,这就是我必须计算轨道偏航/俯仰和眼睛偏航/俯仰的校正的原因。在改变轨道后将眼睛放回同一个位置并指向相同的方向。顺便说一句,根据当前轨道的位置,我只有一个轨道偏航和俯仰,所以这与gman的样本有点不同:

Camera.prototype.changeOrbit = function (newOrbit) {
var matA = mat4.create();
var matB = mat4.create();

mat4.translate(matA, matA, this.orbit);
mat4.rotateY(matA, matA, this.orbitYaw);
mat4.rotateX(matA, matA, this.orbitPitch);

mat4.translate(matB, matB, newOrbit);
mat4.rotateY(matB, matB, this.orbitYaw);
mat4.rotateX(matB, matB, this.orbitPitch);

var matInverseNewOrbit  = mat4.create();
var matNewOrbitToCamera = mat4.create();

mat4.invert(matInverseNewOrbit, matB);
mat4.multiply(matNewOrbitToCamera, matInverseNewOrbit, matA);

var m = matNewOrbitToCamera;

this.eye[0] = m[12];
this.eye[1] = m[13];
this.eye[2] = m[14];

this.eyePitch = ExtractPitch(m);
this.eyeYaw   = ExtractYaw(m);

this.update();
};

ExtractPitch和ExtractYaw按照gman指定的方式工作,但我确实围绕不同的轴旋转,因为音高通常围绕Y轴定义,依此类推。谢谢你的建议。

1 个答案:

答案 0 :(得分:0)

我不确定我能解释一下,但基本上是这样:

在切换时从A切换到B

  1. 计算相机的矩阵A(上面的代码)。 (camera
  2. 计算BmatB
  3. 的矩阵
  4. 计算B矩阵的倒数。 (inverseMatB
  5. camera乘以inverseMatB。 (matBtoCamera

    现在你有一个从B到相机的矩阵。

  6. 将此矩阵(matBToCamera)分解为平移和旋转。

  7. 不幸的是我不知道一个好的分解矩阵函数指向你。我很久没需要了。翻译基本上是矩阵的元素12,13,14。 (假设您使用的是16个元素矩阵,我认为这是glMatrix使用的)。

    var translation = [m[12], m[13], m[14]];
    

    对于旋转,矩阵的上/左3x3部分表示旋转。只要不涉及缩放或倾斜,根据此页面(http://nghiaho.com/?page_id=846),它就是

    var rotXInRadians = Math.atan2(m[9], m[10]);
    var rotYInRadians = Math.atan2(-m[8], Math.sqrt(m[9] * m[9] + m[10] * m[10]));
    var rotZInRadians = Math.atan2(m[4], m[0]);
    

    这是一个例子

    http://jsfiddle.net/greggman/q7Bsy/

    我会将此处的代码专门粘贴到glMatrix

    // first let's make 3 nodes, 'a', 'b', and 'camera
    
    var degToRad = function(v) {
      return v * Math.PI / 180;
    }
    
    var a = {
      name: "a",
      translation: [0, -50, -75],
      pitch: 0,
      yaw: degToRad(30),
    };
    
    var b = {
      name: "b",
      translation: [0, 100, 50],
      pitch: 0,
      yaw: degToRad(-75),
    }
    
    var camera = {
      name: "cam",
      translation: [0, 15, 10],
      pitch: 0,
      yaw: degToRad(16),
      parent: a,
    };
    

    这是计算每个

    矩阵的代码
    var matA = mat4.create();
    mat4.identity(matA);
    mat4.translate(matA, matA, a.translation);
    mat4.rotateY(matA, matA, a.pitch);
    mat4.rotateX(matA, matA, a.yaw);
    a.mat = matA;
    
    var matB = mat4.create();
    mat4.identity(matB);
    mat4.translate(matB, matB, b.translation);
    mat4.rotateY(matB, matB, b.pitch);
    mat4.rotateX(matB, matB, b.yaw);
    b.mat = matB;
    
    var matCamera = mat4.create();
    mat4.identity(matCamera);
    
    var parent = camera.parent;
    
    mat4.translate(matCamera, matCamera, parent.translation);
    mat4.rotateY(matCamera, matCamera, parent.pitch);
    mat4.rotateX(matCamera, matCamera, parent.yaw);
    
    mat4.translate(matCamera, matCamera, camera.translation);
    mat4.rotateY(matCamera, matCamera, camera.pitch);
    mat4.rotateX(matCamera, matCamera, camera.yaw);
    
    camera.mat = matCamera;
    

    这是交换相机的代码

    // Note: Assumes matrices on objects are updated.
    var reparentObject = function(obj, newParent) {
      var matInverseNewParent = mat4.create();
      var matNewParentToObject = mat4.create();
      mat4.invert(matInverseNewParent, newParent.mat);
      mat4.multiply(matNewParentToObject, matInverseNewParent, obj.mat);
    
      var m = matNewParentToObject;
      obj.translation[0] = m[12];
      obj.translation[1] = m[13];
      obj.translation[2] = m[14];
    
      var rotXInRadians = Math.atan2(m[9], m[10]);
      var rotYInRadians = Math.atan2(-m[8], Math.sqrt(m[9] * m[9] + m[10] * m[10]));
      var rotZInRadians = Math.atan2(m[4], m[0]);
    
      obj.pitch = rotYInRadians;
      obj.yaw = rotXInRadians;
      obj.parent = newParent;
    };
    
    var newParent = camera.parent == a ? b : a;
    reparentObject(camera, newParent);