Three.js:同时通过触摸和设备方向旋转相机 [英] Three.js: rotate camera with both touch and device orientation

查看:113
本文介绍了Three.js:同时通过触摸和设备方向旋转相机的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用threejs制作3D项目,该项目可以用鼠标控制计算机设备的相机,还可以控制智能手机的触摸事件和设备方向事件. 例如,此网站的工作方式与我想要的相同.

I am making a 3D project with threejs which allows control of the camera with mouse for computer devices, and also allows control with touch events and deviceorientation event for smartphones. As an example, this site works the same way as what I want to do.

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

As I am using OrbitControls to move camera on the PC version, I bound the touchstart/move/end events to mousedown/move/up and it works perfectly.
The problem is when I try to add the device orientation event's values. Here is what I tried to add in 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的值,突然的变化也会使所有操作出错.

As beta's values are within a [-180,180] degree range the vertical rotation encounters no problem, whereas gamma's range is [-90,90] and values are also changing suddenly when orientating device' screen up and down (even if, I think, it should return horizontal rotation). And even when converting gamma's range to make it takes values from -180 to 180, the sudden shifts make everything goes wrong.

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

I guess that I have to use quaternions as in deviceOrientationControls.js, but I really don't know how it works and every attempt I've made so far was a fail. Can someone help me please?

PS:这是指向有关deviceorientation事件的描述的链接更好地了解什么是alpha beta和gamma.

PS: Here is a link to the description on the deviceorientation event to have a better comprehension of what really are alpha beta and gamma.

编辑
我添加了以下代码段以显示beta和gamma的变化.

EDIT
I added a snippet bellow to show the beta and gamma variations.

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.

I found a solution using a function to convert quaternions to radians, so I wanted to share it if someone wants to do a click/touch+device orientation control using OrbitControls.

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

I take the initial orientation (x1,y1,z1) and calculate the new one (x2,y2,z3) and the difference between them is the variation of the rotation done by the camera. I add these line to the initial update function

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);
}

这篇关于Three.js:同时通过触摸和设备方向旋转相机的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆