Three.js:通过触摸和设备方向旋转相机

2024-04-23

我正在使用 Threejs 制作一个 3D 项目,它允许使用计算机设备的鼠标控制相机,还允许使用触摸事件和智能手机的设备方向事件进行控制。 举个例子,这个网站 http://lacostewinter.seeourwork.cn/en/intro工作方式与我想做的相同。

当我在 PC 版本上使用 OrbitControls 移动相机时,我将 touchstart/move/end 事件绑定到 mousedown/move/up 和它工作得很好.
问题是当我尝试添加设备方向事件的值时。这是我尝试在 OrbitControls.js 中添加的内容:

THREE.OrbitControls = function (object, domElement) {
  const scope = this;
  let lastBeta = 0;
  let lastGamma = 0;
  this.deviceOrientation = {};

  function onDeviceOrientationChangeEvent(event) {
    scope.deviceOrientation = event;
    // Z
    var alpha = scope.deviceOrientation.alpha
      ? THREE.Math.degToRad(scope.deviceOrientation.alpha) 
      : 0;

    // X'
    var beta = scope.deviceOrientation.beta
      ? THREE.Math.degToRad(scope.deviceOrientation.beta)
      : 0;

    // Y''
    var gamma = scope.deviceOrientation.gamma 
      ? THREE.Math.degToRad(scope.deviceOrientation.gamma) 
      : 0;

    // O
    var orient = scope.screenOrientation 
      ? THREE.Math.degToRad(scope.screenOrientation) 
      : 0;

    rotateLeft(lastGamma - gamma);
    rotateUp(lastBeta - beta);

    lastBeta = beta; //is working
    lastGamma = gamma; //doesn't work properly
  }

  window.addEventListener('deviceorientation', onDeviceOrientationChangeEvent, false);
};

由于 beta 的值在 [-180,180] 度范围内,垂直旋转不会遇到问题,而 gamma 的范围是 [-90,90],并且当向上和向下定位设备屏幕时,值也会突然变化(即使我认为,它应该返回水平旋转)。 即使转换伽马范围使其值介于 -180 到 180 之间,突然的变化也会使一切出错。

我想我必须在 deviceOrientationControls.js 中使用四元数,但我真的不知道它是如何工作的,而且到目前为止我所做的每一次尝试都失败了。有人能帮助我吗?

PS:这里有一个链接deviceorientation 事件的描述 http://w3c.github.io/deviceorientation/spec-source-orientation.html#deviceorientation更好地理解什么是 alpha beta 和 gamma。

EDIT
我在下面添加了一个片段来显示 beta 和 gamma 的变化。

let deltaBeta = 0;
let deltaGamma = 0;

if (window.DeviceOrientationEvent) {
  window.addEventListener('deviceorientation', function (e) {
    const beta = (e.beta != null) ? Math.round(e.beta) : 0;
    const gamma = (e.gamma != null) ? Math.round(e.gamma) : 0;

    deltaBeta = Math.abs(beta - deltaBeta);
    deltaGamma = Math.abs(gamma - deltaGamma);

    $("#beta").html("Beta: " + beta);
    $("#gamma").html("Gamma: " + gamma);
    
    if (Math.abs(deltaBeta) > Math.abs(Number($("#deltaBeta").html()))) {
      $("#deltaBeta").html(deltaBeta);
      if (Number($("#deltaBeta").html()) >= 30) {
        $("#deltaBeta").removeAttr("class", "blue").addClass("red");
      }
    }
    if (Math.abs(deltaGamma) > Math.abs(Number($("#deltaGamma").html()))) {
      $("#deltaGamma").html(deltaGamma);
      if (Number($("#deltaGamma").html()) >= 30) {
        $("#deltaGamma").removeAttr("class", "blue").addClass("red");
      }
    }
  }, true);

} else {
  $("#gamma").html("deviceorientation not supported");
}
.red {
  color: red;
  font-weight: bold;
}
.blue {
  color: blue;
  font-weight: bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<body>
  <div>
    <span id="beta"></span>
    <span> [-180; 180]</span>
  </div>
  <div>
    <span>DeltaMax</span>
    <span id="deltaBeta" class="blue">0</span>
  </div>
  <div>
    <span id="gamma"></span>
    <span> [-90; 90]</span>
  </div>
  <div>
    <span>DeltaMax</span>
    <span id="deltaGamma" class="blue">0</span>
  </div>
</body>

我找到了一个使用函数将四元数转换为弧度的解决方案,因此如果有人想使用 OrbitControls 进行点击/触摸+设备方向控制,我想分享它。

我采用初始方向 (x1,y1,z1) 并计算新方向 (x2,y2,z3),它们之间的差异是相机所做的旋转的变化。我将这些行添加到初始的update功能

this.update = function () {
  // Z
  const alpha = scope.deviceOrientation.alpha 
    ? THREE.Math.degToRad(scope.deviceOrientation.alpha)
    : 0;

  // X'
  const beta = scope.deviceOrientation.beta
    ? THREE.Math.degToRad(scope.deviceOrientation.beta)
    : 0;

  // Y''
  const gamma = scope.deviceOrientation.gamma 
    ? THREE.Math.degToRad(scope.deviceOrientation.gamma)
    : 0;

  // O
  const orient = scope.screenOrientation
    ? THREE.Math.degToRad(scope.screenOrientation) 
    : 0;

  const currentQ = new THREE.Quaternion().copy(scope.object.quaternion);

  setObjectQuaternion(currentQ, alpha, beta, gamma, orient);
  const currentAngle = Quat2Angle(currentQ.x, currentQ.y, currentQ.z, currentQ.w);

  // currentAngle.z = left - right
  this.rotateLeft((lastGamma - currentAngle.z) / 2);
  lastGamma = currentAngle.z;

  // currentAngle.y = up - down
  this.rotateUp(lastBeta - currentAngle.y);
  lastBeta = currentAngle.y;
}

听众

function onDeviceOrientationChangeEvent(event) {
  scope.deviceOrientation = event;
}

window.addEventListener('deviceorientation', onDeviceOrientationChangeEvent, false);

function onScreenOrientationChangeEvent(event) {
  scope.screenOrientation = window.orientation || 0;
}

window.addEventListener('orientationchange', onScreenOrientationChangeEvent, false);

功能

var setObjectQuaternion = function () {
  const zee = new THREE.Vector3(0, 0, 1);
  const euler = new THREE.Euler();
  const q0 = new THREE.Quaternion();
  const q1 = new THREE.Quaternion(-Math.sqrt(0.5), 0, 0,  Math.sqrt(0.5));

  return function (quaternion, alpha, beta, gamma, orient) {
    // 'ZXY' for the device, but 'YXZ' for us
    euler.set(beta, alpha, -gamma, 'YXZ');

    // Orient the device
    quaternion.setFromEuler(euler);

    // camera looks out the back of the device, not the top
    quaternion.multiply(q1);

    // adjust for screen orientation
    quaternion.multiply(q0.setFromAxisAngle(zee, -orient));
  }
} ();

function Quat2Angle(x, y, z, w) {
  let pitch, roll, yaw;

  const test = x * y + z * w;
  // singularity at north pole
  if (test > 0.499) {
    yaw = Math.atan2(x, w) * 2;
    pitch = Math.PI / 2;
    roll = 0;

    return new THREE.Vector3(pitch, roll, yaw);
  }

  // singularity at south pole
  if (test < -0.499) {
    yaw = -2 * Math.atan2(x, w);
    pitch = -Math.PI / 2;
    roll = 0;
    return new THREE.Vector3(pitch, roll, yaw);
  }

  const sqx = x * x;
  const sqy = y * y;
  const sqz = z * z;

  yaw = Math.atan2((2 * y * w) - (2 * x * z), 1 - (2 * sqy) - (2 * sqz));
  pitch = Math.asin(2 * test);
  roll = Math.atan2((2 * x * w) - (2 * y * z), 1 - (2 * sqx) - (2 * sqz));

  return new THREE.Vector3(pitch, roll, yaw);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Three.js:通过触摸和设备方向旋转相机 的相关文章

随机推荐