我正在使用 Three.js 创建一个小项目,其中相机位于有纹理的毛衣内。旋转相机是利用手机在空间中的方向来完成的。乍一看,我的解决方案工作正常,但当设备接近垂直位置(角度 Beta 接近 90 度)时,图像会变得疯狂并意外旋转。看起来设备已绕其中一个轴(对应于 alpha 角)旋转了 360 度。
createScene() {
const scene = new THREE.Scene();
console.log('scene', scene);
// create a new Three.js perspective camera
const camera = new THREE.PerspectiveCamera(
75, // field of view
window.innerWidth / window.innerHeight, // aspect ratio
0.1, // near plane
1000 // far plane
);
// create a new Three.js renderer
const renderer = new THREE.WebGLRenderer();
// set the renderer size to the window dimensions
renderer.setSize(window.innerWidth, window.innerHeight);
// get the container element for the background
const backgroundContainer = document.querySelector('.vr-sphere');
if (backgroundContainer == null) return;
backgroundContainer.appendChild(renderer.domElement);
const loader = new THREE.TextureLoader();
const texture = loader.load(
'../../assets/images/oil_painting_van_gogh_starry_night.jfif'
);
const geometry = new THREE.SphereGeometry(500, 60, 40);
// invert the geometry to render it inside out
geometry.scale(-1, 1, 1);
const material = new THREE.MeshBasicMaterial({
map: texture,
});
const sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
// set the camera's position in the scene
camera.position.set(0, 0, 0);
// enable VR mode based on DeviceOrientation API
if (window.DeviceOrientationEvent) {
window.addEventListener('deviceorientation', (event) => {
let alpha = event.alpha ? THREE.MathUtils.degToRad(event.alpha) : 0; // Z
let beta = event.beta ? THREE.MathUtils.degToRad(event.beta) : 0; // X'
if (alpha && beta) {
if (beta >= 0 && beta <= 180) {
// up/down rotation
camera.rotation.set(-Math.PI / 2 + beta, 0, 0);
// left/right rotation
sphere.rotation.y = -alpha;
}
});
}
function animate() {
// request the next frame of the animation
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
}
我读到这个问题的解决方案可以是使用四元数或旋转矩阵。我尝试使用 Three.js 内置函数来实现它们,但效果是相同的。有没有人遇到过类似的问题并且知道如何解决?
你发现问题了:使用四元数。
我编写了这个类,您可以自由地重用或修改(欢迎引用),以将设备方向控件添加到 Three.js 项目中。
我使用 AbsoluteOrientationSensor 来代替 DeviceOrientationEvent
关键部分在这里:
// Retrieve device absolute rotation quaternion
const q1 = new THREE.Quaternion().fromArray(this.sensor.quaternion)
// Ispired by https://gist.github.com/kopiro/86aac4eb19ac29ae62c950ad2106a10e
// Apply rotation around x axis (camera points toward the back of the phone, not up)
// and then update camera quaternion
const q2 = new THREE.Quaternion().setFromAxisAngle( new THREE.Vector3(1, 0, 0), -Math.PI/2 )
this.camera.quaternion.multiplyQuaternions(q2, q1)