• 作者:老汪软件技巧
  • 发表时间:2024-11-28 17:07
  • 浏览量:

大家好,我是日拱一卒的攻城师不浪,致力于技术与艺术的融合。这是2024年输出的第45/100篇文章。

前言

很多小伙伴经常会问,Cesium中如何绘制天空盒?

其实Cesium中的天空盒分2种:全景天空盒和近地天空盒。全景天空盒比较好做,因为Cesium直接提供了相关的绘制类:SkyBox,这个如何做我就不罗嗦了,不会的可以参照我的【Cesium开源项目】:/tingyuxuan2…

而近地天空盒的实现Cesium官方并没有给出,那么,今天我们就来看下如何实现一个近地天空盒。

父母颜值低孩子颜值高__高颜值高评分青春美剧

其实不管是全景还是近地,它们的原理基本上都差不多,所以我们在实现近地天空盒时,可以直接借鉴全景天空盒的源代码,然后稍微加以改进即可。

天空盒的原理

立方体贴图(Cube Mapping):天空盒利用立方体贴图技术,将六个2D图像(纹理)映射到一个立方体的六个面上。那么当我们看天空盒的时候,其实就是在这个矩形盒子中的视角。这些图像通常是全景图,可以是天空、星空或其他任何类型的环境背景。

相机视角:在渲染过程中,相机的视角决定了观察天空盒的方向。相机的朝向和位置被用来确定从立方体的哪个面渲染纹理。

纹理坐标:天空盒的每个顶点都被赋予了纹理坐标,这些坐标通常是立方体的角落坐标(-1到1)。当顶点通过顶点着色器处理时,这些坐标会被用来从立方图集中采样正确的纹理。

变换矩阵:为了使天空盒看起来随着相机移动而移动,需要将相机的位置和方向考虑进去。一般需要一个变换矩阵来实现,该矩阵将相机的视角旋转应用到天空盒的顶点上。

深度缓冲:由于天空盒应该始终在场景的最远端,它的深度值通常被设置为最大值,以确保它总是在其他3D对象的后面。

无缝连接:为了使天空盒的六个面之间没有可见的接缝,纹理图像需要正确对齐,并且在立方体的边缘处进行平滑过渡。

近地天空盒的不同

近地的原理其实还好,原理基本与全景相同,唯一不同的是需要根据你的视角高度来决定是否显示天空盒。

当你的视角高度低于某个阈值时,比如200公里,我们就显示一个近地的天空盒;当你飞得更高时,我们就切换回默认的天空盒。这样,近地天空盒就可以提供更加真实的视觉效果,尤其是在低空飞行或者地面观察时。

代码实现

首先找到Skybox的类的实现

高颜值高评分青春美剧_父母颜值低孩子颜值高_

Cesium的文档,搜到Skybox后可以直接点进去查看其源码,源码并不多,200来行,把源码搬出来,再来来看看我们要修改哪些地方。

根据视高调整天空盒显示逻辑

这里我定义了小于20万米的时候显示近地天空盒,大于20万的时候显示默认的天空盒;

init(options) {
    var height = !options.height ? "200000" : options.height;
    var defaultSkyBox = this.viewer.scene.skyBox;
    this.viewer.scene.postRender.addEventListener(() => {
        var e = this.viewer.camera.position;
        //判断当前视高
        if (Cesium.Cartographic.fromCartesian(e).height < height) {
            // 显示近地天空盒
            this.viewer.scene.skyBox = this;
            this.viewer.scene.skyAtmosphere.show = false;
        } else {
            // 显示默认天空盒
            this.viewer.scene.skyBox = defaultSkyBox;
            this.viewer.scene.skyAtmosphere.show = true;
        }
    });
}

顶点着色器修改

由于我用的是比较新版本的Cesium,它的shader语法已经切换成Webgl2语法,因此需要对着色器的部分语法进行修改。

const SkyBoxVS = `
#version 300 es
precision highp float;
in vec3 position;
out vec3 v_texCoord;
uniform mat3 u_rotateMatrix;

_高颜值高评分青春美剧_父母颜值低孩子颜值高

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; } `;

其中主要是将v_texCoord的前置定义varying改成输出语法out,因为v_texCoord是需要传递给片元着色器的。

片元着色器的修改

const SkyBoxFS = `
precision highp float;
uniform samplerCube u_cubeMap;
in vec3 v_texCoord;
out vec4 fragColor;
void main() {
    vec4 color = texture(u_cubeMap, normalize(v_texCoord));
    fragColor = vec4(czm_gammaCorrect(color).rgb, czm_morphTime);
}`;

这里主要是将varying vec3 v_texCoord;改成in vec3 v_texCoord;,因为v_texCoord是从顶点传过来的,因此前置语法要用in。

还有就是新版glsl去除了gl_FragColor,而是变成了输出变量:

out vec4 fragColor;
fragColor = vec4(czm_gammaCorrect(color).rgb, czm_morphTime);

使用

修改的地方也不多,那当我们封装好后,使用起来就非常方便了,跟全景天空盒基本没啥差别,只要你准备好全景图片即可。

import SkyboxGround from '@/classes/SkyboxGround.js'
const skyBoxGround = () => {
  new SkyboxGround(__viewer, {
    sources: {
      positiveX: '/images/skyboxGround/right.jpg',
      negativeX: '/images/skyboxGround/left.jpg',
      positiveY: '/images/skyboxGround/front.jpg',
      negativeY: '/images/skyboxGround/back.jpg',
      positiveZ: '/images/skyboxGround/up.jpg',
      negativeZ: '/images/skyboxGround/down.jpg'
    }
  })
}

最后

如果实在找不到近地天空盒的照片,可以私信我领取。

如果想系统学习Cesium,可以了解下我的Cesium系列教程《Cesium从入门到实战》,将Cesium的知识点进行串联,让不了解Cesium的小伙伴拥有一个完整的学习路线,并最终完成一个智慧城市的完整项目,+作者:brown_7778(备注来意)了解教程细节。

有需要进可视化&Webgis交流群可以加我:brown_7778(备注来意),另外也可接项目合作。


上一条查看详情 +一个高效的Java对象映射库Orika
下一条 查看详情 +没有了