Three.js:同时通过触摸和设备方向旋转相机 [英] Three.js: rotate camera with both touch and device orientation
问题描述
我正在使用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屋!