演示 http://csworld.cc:3005/Panorama_Sphere.html
全景图片一般有两种实现方式,一是把图片贴到一个球体的表面,二是把全景图分成 6 张图片分别贴到一个正方体的 6 个面上。
在 Three.js 中把全景图片贴到球体的表面非常简单
var textureLoader = new THREE.TextureLoader();
textureLoader.load('powu.jpg', function (texture) {
var material = new THREE.MeshBasicMaterial({map: texture});
var sphere = new THREE.SphereGeometry(500, 60, 40);
// 翻转 X 轴使所有的面都朝里(改变了法向量的方向)
sphere.scale(-1, 1, 1);
var mesh = new THREE.Mesh(sphere, material);
scene.add(mesh);
});
相机在球体的中心默认指向 Z 轴负方向。下面的关键是通过移动鼠标来改变相机的朝向,可通过计算经纬度来计算相机的朝向。
coords.png如图球面上任意一个点 A 在赤道面上的投影 为 B, OAB 与赤道平面的夹角是纬度(lat), OB 与水平轴的夹角是经度(lon)。 默认的经纬度为零 (lon=0, lat=0), 当我们移动鼠标时根据鼠标移动的距离改变 lat, lon 的值,然后再根据 lat, lon 计算出 A 点的坐标,让相机指向 A 就行了。
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>球体全景图</title>
<style>
body {
margin: 0;
}
canvas {
width: 100%;
height: 100%
}
</style>
</head>
<body>
<script src="three.js"></script>
<script>
var scene, camera, renderer;
var isUserInteracting = false,
onMouseDownX = 0, onMouseDownY = 0,
lon = 0, lat = 0,
phi = 0, theta = 0;
var touchX = 0, touchY = 0;
init();
animate();
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000);
// 自定义属性 target ,相机默认指向 (0,0,0)
camera.target = new THREE.Vector3(0, 0, 0);
var textureLoader = new THREE.TextureLoader();
textureLoader.load('powu.jpg', function (texture) {
var material = new THREE.MeshBasicMaterial({map: texture});
var sphere = new THREE.SphereGeometry(500, 60, 40);
// 翻转 X 轴使所有的面都朝里(改变了法向量的方向)
sphere.scale(-1, 1, 1);
var mesh = new THREE.Mesh(sphere, material);
scene.add(mesh);
});
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
document.addEventListener('mousedown', onMouseDown, false);
document.addEventListener('mousemove', onMouseMove, false);
document.addEventListener('mouseup', onMouseUp, false);
document.addEventListener('wheel', onMouseWheel, false);
document.addEventListener('touchstart', onTouchStart, false);
document.addEventListener('touchmove', onTouchMove, false);
document.addEventListener('touchend', onTouchEnd, false);
window.addEventListener('resize', onWindowResize, false);
}
function animate() {
requestAnimationFrame(animate);
updateCamera();
renderer.render(scene, camera);
}
function onMouseDown(event) {
event.preventDefault();
isUserInteracting = true;
onMouseDownX = event.clientX;
onMouseDownY = event.clientY;
}
function onMouseMove(event) {
if (isUserInteracting === true) {
lon -= (event.clientX - onMouseDownX ) * 0.1; // 经度
lat += (event.clientY - onMouseDownY) * 0.1; // 纬度
onMouseDownX = event.clientX;
onMouseDownY = event.clientY;
}
}
function onMouseUp(event) {
isUserInteracting = false;
}
function onMouseWheel(event) {
camera.fov += event.deltaY * 0.05;
camera.updateProjectionMatrix();
}
function onTouchStart(event) {
event.preventDefault();
isUserInteracting = true;
var touch = event.touches[0];
touchX = touch.screenX;
touchY = touch.screenY;
}
function onTouchMove(event) {
if (isUserInteracting === true) {
var touch = event.touches[0];
lon -= (touch.screenX - touchX) * 0.1;
lat += (touch.screenY - touchY) * 0.1;
touchX = touch.screenX;
touchY = touch.screenY;
}
}
function onTouchEnd(event) {
isUserInteracting = false;
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function updateCamera() {
if (isUserInteracting === false) {
lon += 0.1;
}
lat = Math.max(-85, Math.min(85, lat)); // 纬度限定在 [-85,85]
phi = THREE.Math.degToRad(90 - lat); // 90 - 纬度
theta = THREE.Math.degToRad(lon); // 经度
// 通过经纬度计算球面上点的坐标
camera.target.x = 500 * Math.sin(phi) * Math.cos(theta);
camera.target.y = 500 * Math.cos(phi);
camera.target.z = 500 * Math.sin(phi) * Math.sin(theta);
// 调整相机指向
camera.lookAt(camera.target);
}
</script>
</body>
</html>
网友评论