问题来源
- 导出的文件中给出的
相机参数
不能作为cesium
中设置相机的参数,所以需要根据已有数据计算。 - 导出的文件中给出的
相机参数
不能作为黑洞引擎[秉匠科技]
中设置相机的参数,所以需要根据已有数据计算。 路线漫游
给出的信息只包含每个点的相机位置,不包括相机朝向
等信息,所以我
规定每个点的相机朝向
是朝向下一个相机的位置,这样的话也需要根据相邻相机的位置计算朝向。cesium
是计算欧拉角
,黑洞引擎
是计算四元数
,因为黑洞引擎
设置相机参数时采用四元数
,虽然支持欧拉角
,但黑洞引擎本身存在缺陷所以在这里我
不使用欧拉角来设置。
代码实现(cesium)
路线漫游
js
/**
* 根据两个坐标点,获取heading(朝向)
* @param {Cartesian3} pointA camera_1的位置信息
* @param {Cartesian3} pointB camera_2的位置信息
* @returns {Number} camera_1的heading值
*/
function getHeading(pointA, pointB) {
// 建立以点A为原点,X轴为east,Y轴为north,Z轴朝上的坐标系
const transform = Cesium.Transforms.eastNorthUpToFixedFrame(pointA);
// 向量AB
const positionvector = Cesium.Cartesian3.subtract(pointB, pointA, new Cesium.Cartesian3());
/**
* 因 transform 是将A为原点的 eastNorthUp 坐标系中的点转换到世界坐标系的矩阵
* AB 为世界坐标中的向量
* 因此将 AB 向量转换为 A 原点坐标系中的向量,需乘以 transform 的逆矩阵
*/
const vector = Cesium.Matrix4.multiplyByPointAsVector(Cesium.Matrix4.inverse(transform, new Cesium.Matrix4()), positionvector, new Cesium.Cartesian3());
// 归一化
const direction = Cesium.Cartesian3.normalize(vector, new Cesium.Cartesian3());
// heading
const heading = Math.atan2(direction.y, direction.x) - Cesium.Math.PI_OVER_TWO;
// 计算出的弧度制
const radianHeading = Cesium.Math.TWO_PI - Cesium.Math.zeroToTwoPi(heading);
// 转换为角度制
const degreeHeading = Cesium.Math.toDegrees(radianHeading);
return degreeHeading;
}
/* 使用 */
camera_1.heading = getHeading(Cesium.Cartesian3.fromDegrees(camera_1.lng, camera_1.lat), Cesium.Cartesian3.fromDegrees(camera_2.lng, camera_2.lat));
视角跳转
js
/**
* 根据相机参数计算相机的direction向量,up向量
*/
function calcCameraParams(data) {
let center = data.center.map(parseFloat); // 相机朝向的目标点
let eye = data.eye.map(parseFloat); // 相机位置
let up = data.up.map(parseFloat); // up向量
let direction = [];
center.forEach((item, index) => {
direction.push(item - eye[index]);
});
let normalizedDir = Cesium.Cartesian3.normalize(new Cesium.Cartesian3(direction[0], direction[1], direction[2]), new Cesium.Cartesian3());
let normalizedUp = Cesium.Cartesian3.normalize(new Cesium.Cartesian3(up[0], up[1], up[2]), new Cesium.Cartesian3());
console.log("单位化方向向量和up向量:", normalizedDir, normalizedUp);
eye = cartesianToLatLng(eye[0], eye[1], eye[2]);
return { eye, headingPitchRoll: directionUpToHeadingPitchRoll(normalizedDir, normalizedUp) };
}
/**
* 根据direction向量,up向量计算欧拉角
*/
function directionUpToHeadingPitchRoll(direction, up) {
let heading;
if (!Cesium.Math.equalsEpsilon(Math.abs(direction.z), 1.0, Cesium.Math.EPSILON3)) {
heading = Math.atan2(direction.y, direction.x) - Cesium.Math.PI_OVER_TWO;
} else {
heading = Math.atan2(up.y, up.x) - Cesium.Math.PI_OVER_TWO;
}
heading = Cesium.Math.toDegrees(Cesium.Math.TWO_PI - Cesium.Math.zeroToTwoPi(heading));
let pitch = Math.asin(direction.z);
// 将弧度转换为角度
pitch = Cesium.Math.toDegrees(pitch);
return { heading, pitch };
}
代码实现(黑洞引擎)
设置相机-CASE1
js
/**
* 计算旋转四元数,用于黑洞引擎设置相机方向
* @param {object} data 文件中的相机姿态
* @returns 四元数数组
*/
function getQuaternionFromCameraAttitude(data) {
let eye = data.eye.map(parseFloat); // 相机位置
let center = data.center.map(parseFloat); // 相机的目标点
let vector_up = data.up.map(parseFloat); // up向量
let vector_direction = [];
// 计算 方向向量
center.forEach((item, index) => {
vector_direction.push(item - eye[index]);
});
// 单位化
let vector_normalized_dir = Cesium.Cartesian3.normalize(new Cesium.Cartesian3(vector_direction[0], vector_direction[1], vector_direction[2]), new Cesium.Cartesian3());
let vector_normalized_up = Cesium.Cartesian3.normalize(new Cesium.Cartesian3(vector_up[0], vector_up[1], vector_up[2]), new Cesium.Cartesian3());
// 计算 右向量
let vector_right = Cesium.Cartesian3.cross(vector_normalized_dir, vector_normalized_up, new Cesium.Cartesian3());
// 构造旋转矩阵
let matrix3_rotation = new Cesium.Matrix3(vector_right.x, vector_normalized_up.x, -vector_normalized_dir.x, vector_right.y, vector_normalized_up.y, -vector_normalized_dir.y, vector_right.z, vector_normalized_up.z, -vector_normalized_dir.z);
// 从旋转矩阵中提取四元数
let quaternion = Cesium.Quaternion.fromRotationMatrix(matrix3_rotation);
return quaternion;
}
设置相机-CASE2
js
/**
* 已知点A和点B坐标,点B坐标为相机位置,点A坐标为相机看向的位置
* 构建此时相机的旋转矩阵 并 求出四元数
* @param {Array} camera_pos 相机位置数组
* @param {Array} center_pos 目标位置数组
* @returns 相机的旋转四元数数组
*/
function getQuaternionFromTwoPoints(camera_pos, center_pos) {
// 构造数据
let point_a = new Cesium.Cartesian3(center_pos[0], center_pos[1], center_pos[2]);
let point_b = new Cesium.Cartesian3(camera_pos[0], camera_pos[1], camera_pos[2]);
// 计算方向向量
let vector_dir = Cesium.Cartesian3.subtract(point_a, point_b, new Cesium.Cartesian3());
let vector_normalized_dir = Cesium.Cartesian3.normalize(vector_dir, new Cesium.Cartesian3());
// 构造up向量 并计算right向量
let vector_normalized_up = Cesium.Cartesian3.clone(Cesium.Cartesian3.UNIT_Z);
let vector_right = new Cesium.Cartesian3();
Cesium.Cartesian3.cross(vector_normalized_dir, vector_normalized_up, vector_right);
Cesium.Cartesian3.normalize(vector_right, vector_right);
Cesium.Cartesian3.cross(vector_right, vector_normalized_dir, vector_normalized_up);
Cesium.Cartesian3.normalize(vector_normalized_up, vector_normalized_up);
// 构造旋转矩阵
let matrix3_rotation = new Cesium.Matrix3(vector_right.x, vector_normalized_up.x, -vector_normalized_dir.x, vector_right.y, vector_normalized_up.y, -vector_normalized_dir.y, vector_right.z, vector_normalized_up.z, -vector_normalized_dir.z);
// 从旋转矩阵中提取四元数
let quaternion = Cesium.Quaternion.fromRotationMatrix(matrix3_rotation);
return quaternion;
}
设置相机-CASE3
js
/**
* 获取相机朝向
* @param {Array} camera_pos 初始位置
* @param {Array} center_pos 目标位置
* @param {number} an 固定角度 注意:俯视角度为正
* @return 旋转四元数(初始位置处朝向目标位置并且应用了旋转角度)
*/
function getQuaternionForRoam(camera_pos, center_pos, an) {
let point_a = new Cesium.Cartesian3(center_pos[0], center_pos[1], center_pos[2]);
let point_b = new Cesium.Cartesian3(camera_pos[0], camera_pos[1], camera_pos[2]);
// 计算方向向量
let vector_dir = Cesium.Cartesian3.subtract(point_a, point_b, new Cesium.Cartesian3());
let vector_normalized_dir = Cesium.Cartesian3.normalize(vector_dir, new Cesium.Cartesian3());
// 构造up向量 并计算right向量
let vector_normalized_up = Cesium.Cartesian3.clone(Cesium.Cartesian3.UNIT_Z);
let vector_right = new Cesium.Cartesian3();
Cesium.Cartesian3.cross(vector_normalized_dir, vector_normalized_up, vector_right);
Cesium.Cartesian3.normalize(vector_right, vector_right);
Cesium.Cartesian3.cross(vector_right, vector_normalized_dir, vector_normalized_up);
Cesium.Cartesian3.normalize(vector_normalized_up, vector_normalized_up);
// 构造旋转矩阵
let matrix3_init = new Cesium.Matrix3(vector_right.x, vector_normalized_up.x, -vector_normalized_dir.x, vector_right.y, vector_normalized_up.y, -vector_normalized_dir.y, vector_right.z, vector_normalized_up.z, -vector_normalized_dir.z);
// 将角度转换为弧度
let angle = Cesium.Math.toRadians(an);
let matrix_rotation = new Cesium.Matrix3();
Cesium.Matrix3.fromRotationX(-angle, matrix_rotation); // 注意这里的负号表示向下旋转
// 得到目标矩阵
let matrix_result = new Cesium.Matrix3();
Cesium.Matrix3.multiply(matrix3_init, matrix_rotation, matrix_result);
// 得到四元数
let quaternion = new Cesium.Quaternion();
Cesium.Quaternion.fromRotationMatrix(matrix_result, quaternion);
quaternion = isNaN(quaternion.x) ? [0.5, 0.5, 0.5, 0.5] : quaternion;
return quaternion;
}
设置相机-CASE4-模型旋转引起的{*}旋转
js
/**
*
* @param {*} qu 四元数
* @param {*} cc 相机/点/文字位置 的 坐标
* @param {*} mc 模型中心点 的 坐标
* @returns 相机/点/文字位置 绕 模型中心点 做 qu 的变换
*/
function getRotedPos(qu, cc, mc) {
// 定义点A和点B的坐标
let pointA = new Cesium.Cartesian3(cc[0], cc[1], cc[2]);
let pointB = new Cesium.Cartesian3(mc[0], mc[1], mc[2]);
// 定义给定的四元数qu
let quaternion = new Cesium.Quaternion(qu[0], qu[1], qu[2], qu[3]);
// 计算点A相对于点B的坐标偏移量
let offset = new Cesium.Cartesian3();
Cesium.Cartesian3.subtract(pointA, pointB, offset);
// 将偏移量转换为四元数表示
let offsetQuaternion = new Cesium.Quaternion(offset.x, offset.y, offset.z, 0);
// 计算旋转后的点A的坐标
let rotatedOffset = new Cesium.Quaternion();
Cesium.Quaternion.multiply(quaternion, offsetQuaternion, rotatedOffset);
Cesium.Quaternion.conjugate(quaternion, quaternion);
Cesium.Quaternion.multiply(rotatedOffset, quaternion, rotatedOffset);
// 计算旋转后的点A的位置
let rotatedPointA = new Cesium.Cartesian3();
Cesium.Cartesian3.add(pointB, new Cesium.Cartesian3(rotatedOffset.x, rotatedOffset.y, rotatedOffset.z), rotatedPointA);
// 输出旋转后的点A的位置
return Object.values(rotatedPointA);
}
/**
*
* @param {*} qc 相机四元数
* @param {*} qm 模型四元数
* @returns
*/
function getRotedCamQu(qc, qm) {
let q_c = new Cesium.Quaternion(qc[0], qc[1], qc[2], qc[3]);
let q_m = new Cesium.Quaternion(qm[0], qm[1], qm[2], qm[3]);
let q_res = Cesium.Quaternion.multiply(q_m, q_c, new Cesium.Quaternion());
return Object.values(q_res);
}