Skip to content

问题来源

  1. 导出的文件中给出的相机参数不能作为cesium中设置相机的参数,所以需要根据已有数据计算。
  2. 导出的文件中给出的相机参数不能作为黑洞引擎[秉匠科技]中设置相机的参数,所以需要根据已有数据计算。
  3. 路线漫游给出的信息只包含每个点的相机位置,不包括相机朝向等信息,所以规定每个点的相机朝向是朝向下一个相机的位置,这样的话也需要根据相邻相机的位置计算朝向。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);
}