解决 Cesium.SkyBox 天空盒在近地面水平天际线视角下,倾斜问题(倾斜错误)

849 阅读3分钟

本篇记录了我在使用Cesium.SkyBox更换天空盒图片时,却遇到图片(方位)位置无法正常显示的问题。

问题:已解决。

来源

因为我时常在项目中使用到Cesium.SkyBox的功能,为了方面使用,就封装了一个(GUI 可控的)SkyBox组件,可是在使用中,却遇到这么个问题:

1.png

2.png

可以明显得看出来,天都倾斜,云朵下坠。太奇怪了................

3.png

排查

一开始,我觉得自己没有正确使用Cesium.SkyBox的 API,或者某些属性我没有设置正确,可是我再次仔细阅读文档:

4.png

并使用console.log和断点测试参数和代码,反复试验,觉得 API 的使用应该没有问题,符合文档的描述。

那么问题会不会是Cesium自身的局限导致的,我又搜索了一下相关资源,发现遇到这个问题的人还是不少的:

5.png

解难

于是乎,我借鉴了这篇文章(Cesium 自定义天空盒 近地时角度错误)的思路来解决问题。

这篇文章的作者是修改源码,重新打包一份Cesium.js库的形式,我不是很想修改源代码,担心产生其他奇奇怪怪的问题。

我的方式是,新建一个Cesium.SkyBox变体,修改它相关的内容,达到我的需求,然后使用它,来替代使用Cesium.SkyBox

  1. 复制一份源代码SkyBox.js,以及相关的方法类

6.png

  1. 设置天空盒绘制 Shader 的uniform资源,更改绘制命令command的模型矩阵modelMatrix
  let command = this._command;
  command.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(
    frameState.camera._positionWC
  );

将模型矩阵command.modelMatrix转为uv绘制矩阵u_rotateMatrix

command.uniformMap = {
  u_cubeMap: function () {
    return that._cubeMap;
  },
  u_rotateMatrix: function () {
    if (!Cesium.defined(Cesium.Matrix4.getRotation)) {
      return Cesium.Matrix4.getMatrix3(command.modelMatrix, skyboxMatrix3);
    }
    return Cesium.Matrix4.getRotation(command.modelMatrix, skyboxMatrix3);
  },
};
  1. 修改天空盒绘制 Shader 程序的代码:
if (!Cesium.defined(command.shaderProgram) || this._useHdr !== useHdr) {
  let fs = new Cesium.ShaderSource({
    defines: [useHdr ? "HDR" : ""],
    sources: [SkyBoxFS],
  });
  command.shaderProgram = Cesium.ShaderProgram.fromCache({
    context: context,
    vertexShaderSource: SkyBoxVS,
    fragmentShaderSource: fs,
    attributeLocations: this._attributeLocations,
  });
  this._useHdr = useHdr;
}
//片元着色器
let SkyBoxFS = `uniform samplerCube u_cubeMap;
  in vec3 v_texCoord;
  void main()
  {
    vec4 color = texture(u_cubeMap, normalize(v_texCoord));
    out_FragColor = vec4(czm_gammaCorrect(color).rgb, czm_morphTime);
  }
`;

// 顶点着色器,主要修改是乘了一个旋转矩阵(之前计算出来当前相机方位的旋转矩阵)
let SkyBoxVS = `
  uniform mat3 u_rotateMatrix;
  in vec3 position;
  out vec3 v_texCoord;
  void main()
  {
    vec3 p = czm_viewRotation * u_rotateMatrix * (czm_temeToPseudoFixed * (czm_entireFrustum.y * position));
    gl_Position = czm_projection * vec4(p, 1.0);
    v_texCoord = position.xyz;
  }
 `;

这就完成了,我将这个Cesium.SkyBox变体命名为SkyBoxOnGround,接下来就是测试使用了。

这里的片元着色器和顶点着色器和源码有些不一样,是因为 WebGL 的版本不一样而修改的,后面我会讲解下。

使用

后面,我又根据原本封装SkyBox的思路重新整合封装了一个新的SkyBoxOnGround组件,以下就是使用效果:

import "./app.css";
import * as dat from "dat.gui";
import { viewer } from "./main";
import Scene from "./Scene/index";
import SkyBoxOnGround from "./SkyBoxOnGround/index";
import Camera from "./Camera/index";

const gui = new dat.GUI({
  name: "Cesium GUI",
  width: 450,
  autoPlace: true,
  closed: false,
});
gui.domElement.id = "gui";
gui.show();

viewer.scene.skyAtmosphere.show = false;

const camera = new Camera(
  viewer,
  gui,
  {
    position: {
      longitude: 114.056178,
      latitude: 22.46328,
      height: 500,
    },
    headingPitchRoll: {
      heading: 0.0,
      pitch: -3,
      roll: 0.0,
    },
  },
  true
);

const scene = new Scene(viewer, gui);
const skyBox = new SkyBoxOnGround(
  viewer,
  gui,
  {
    show: true,
    sourcesType: "default",
    sourcesList: [
      {
        name: "star1",
        sources: {
          positiveX: "./static/skybox/stars/00h+00.jpg",
          negativeX: "./static/skybox/stars/12h+00.jpg",
          positiveY: "./static/skybox/stars/06h+00.jpg",
          negativeY: "./static/skybox/stars/18h+00.jpg",
          positiveZ: "./static/skybox/stars/06h+90.jpg",
          negativeZ: "./static/skybox/stars/06h-90.jpg",
        },
      },
      {
        name: "star2",
        sources: {
          positiveX: "./static/skybox/stars/Version2_dark_px.jpg",
          negativeX: "./static/skybox/stars/Version2_dark_mx.jpg",
          positiveY: "./static/skybox/stars/Version2_dark_py.jpg",
          negativeY: "./static/skybox/stars/Version2_dark_my.jpg",
          positiveZ: "./static/skybox/stars/Version2_dark_pz.jpg",
          negativeZ: "./static/skybox/stars/Version2_dark_mz.jpg",
        },
      },
      {
        name: "star3",
        sources: {
          positiveX: "./static/skybox/stars/tycho2t3_80_pxs.jpg",
          negativeX: "./static/skybox/stars/tycho2t3_80_mxs.jpg",
          positiveY: "./static/skybox/stars/tycho2t3_80_pys.jpg",
          negativeY: "./static/skybox/stars/tycho2t3_80_mys.jpg",
          positiveZ: "./static/skybox/stars/tycho2t3_80_pzs.jpg",
          negativeZ: "./static/skybox/stars/tycho2t3_80_mzs.jpg",
        },
      },
      {
        name: "day1",
        sources: {
          positiveX: "./static/skybox/skys/rightav9.jpg",
          negativeX: "./static/skybox/skys/leftav9.jpg",
          positiveY: "./static/skybox/skys/frontav9.jpg",
          negativeY: "./static/skybox/skys/backav9.jpg",
          positiveZ: "./static/skybox/skys/topav9.jpg",
          negativeZ: "./static/skybox/skys/bottomav9.jpg",
        },
      },
      {
        name: "day2",
        sources: {
          positiveX: "./static/skybox/skys/SunSetRight.png",
          negativeX: "./static/skybox/skys/SunSetLeft.png",
          positiveY: "./static/skybox/skys/SunSetFront.png",
          negativeY: "./static/skybox/skys/SunSetBack.png",
          positiveZ: "./static/skybox/skys/SunSetUp.png",
          negativeZ: "./static/skybox/skys/SunSetDown.png",
        },
      },
      {
        name: "day3",
        sources: {
          positiveX: "./static/skybox/skys/Right.jpg",
          negativeX: "./static/skybox/skys/Left.jpg",
          positiveY: "./static/skybox/skys/Front.jpg",
          negativeY: "./static/skybox/skys/Back.jpg",
          positiveZ: "./static/skybox/skys/Up.jpg",
          negativeZ: "./static/skybox/skys/Down.jpg",
        },
      },
    ],
  },
  false
);

7.png

8.png

9.png

相关资料