https://mp.weixin.qq.com/s/-pDyfHWmj08CfBuKQq7hCg

https://gitee.com/m3d/cesium-for-fluid

01

Cesium基础大气雾

基于ShaderToy的基础大气散射雾效果样例

比较基础的大气散射雾效,主要是对瑞利/米氏空气散射的近似模拟
// 基于屏幕空间的Ray Marching球面近似大气散射


vec4 get_incident_light(_in(ray_t) ray) {
    vec3 dir = ray.direction;
    vec3 start = ray.origin;
    float a = dot( dir, dir);
    float b = 2.0 * dot(dir, start);
    float radius2 = atmosphere.radius * atmosphere.radius;
    float c = dot(start, start) - radius2;
    float d = (b * b) - 4.0 * a * c;
    if (d < 0.0) return vec4(0.0);
    float squaredD = sqrt(d);
    vec2 ray_length = vec2(
    max((-b - squaredD) / (2.0 * a), 0.0), min((-b + squaredD) / (2.0 * a), plane.distance)
    );
    if (ray_length.x > ray_length.y) return vec4(0.0);
    float march_step = (ray_length.y - ray_length.x) / float(num_samples);
    float mu = dot(ray.direction, normalize(czm_sunPositionWC));
    float phaseR = rayleigh_phase_func(mu);
    float phaseM = 
    #if 1
        henyey_greenstein_phase_func(mu);
    #else
        schlick_phase_func(mu);
    #endif
    float optical_depthR = 0.;
    float optical_depthM = 0.;
    vec3 sumR = vec3(0);
    vec3 sumM = vec3(0);
    float march_pos = 0.;
    for (int i = 0; i < num_samples; i++) {
        vec3 s = ray.origin +
        ray.direction * (march_pos + 0.5 * march_step);
        float height = length(s) - 6360e3;
        float hr = exp(-height / hR) * march_step;
        float hm = exp(-height / hM) * march_step;
        optical_depthR += hr;
        optical_depthM += hm;
        ray_t light_ray = _begin(ray_t)
        s, normalize(czm_sunPositionWC)
        _end;
        float optical_depth_lightR = 0.;
        float optical_depth_lightM = 0.;
        bool overground = get_sun_light(
            light_ray,
            optical_depth_lightR,
            optical_depth_lightM);
        if (overground) {
            vec3 tau =
                betaR * (optical_depthR + optical_depth_lightR) +
                betaM * 1.1 * (optical_depthM + optical_depth_lightM);
            vec3 attenuation = exp(-tau);
            sumR += hr * attenuation;
            sumM += hm * attenuation;
        }
        march_pos += march_step;
    }
    float attenuation = length(exp(-((betaM * optical_depthM)
    + (betaR * optical_depthR)) * 4.));
    return vec4(
    23. *
    (sumR * phaseR * betaR +
    sumM * phaseM * betaM), 1.0-attenuation);
}

02

Cesium地形高度图

基于Cesium实时地形绘制当前视角的地形高度图样例

生成高度图的方式有很多,这里提供一种比较快捷且性能尚可的方案,通过提取当前场景的地形Command创建新的深度derivedProgram,做一次rtt绘制到深度纹理中,快速实现一次rtt深度图的流程。

样例代码:

// 生成高度图纹理

_generateHeightMapTexture() {
    const context = this.viewer.scene.context;
    const fbo = RenderUtil.createDepthFramebuffer(
        context,
        this.config.resolution.x,
        this.config.resolution.y
    );
    // 保存原始状态
    const passState = this.viewer.scene._view.passState;
    const originalCamera = this.viewer.scene.camera;
    const originalFramebuffer = context._currentFramebuffer;
    const originalViewport = passState.viewport;
    // 配置渲染状态
    passState.viewport.x = 0;
    passState.viewport.y = 0;
    passState.viewport.width = this.config.resolution.x
    passState.viewport.height = this.config.resolution.y
    passState.framebuffer = fbo;
    this.viewer.scene.camera = this.heightMapCamera;
    // 在渲染深度预处理前添加着色器处理
    this._processHeightMapShaders();
    // 执行深度渲染
    this._renderDepthPrepass(passState);
    // 创建高度图纹理
    const heightMap = RenderUtil.createTexture({
        context: context,
        width: this.config.resolution.x,
        height: this.config.resolution.y,
        flipY: false,
        pixelFormat: Cesium.PixelFormat.RGBA,
        pixelDatatype: Cesium.PixelDatatype.FLOAT
    });
    // 拷贝数据到高度图纹理
    const copyFBO = RenderUtil.createFramebuffer(context, heightMap);
    this._copyTexture(fbo.getColorTexture(0), copyFBO);
    // 恢复原始状态
    passState.framebuffer = originalFramebuffer;
    passState.viewport = originalViewport;
    this.viewer.scene.camera = originalCamera;
    return heightMap;
}

// 执行深度预渲染


_renderDepthPrepass(passState) {
    const frameState = this.viewer.scene.frameState;
    // 更新相机状态
    frameState.camera = this.heightMapCamera;
    this.viewer.scene.frameState.context.uniformState.updateCamera(this.heightMapCamera);
    // 执行渲染命令
    const commands = this._getDepthRenderCommands();
    commands.forEach(cmd => cmd.execute(this.viewer.scene.context, passState));
}


03

Cesium流体体渲染

基于ShaderToy的流体体渲染迁移至Cesium合并样例
这里是对之前的Cesium for Fluid样例的完善和补充,增加了Cesium自身的地形结合原生的体渲染法进行流体的体渲染和展示。
ShaderToy: https://www.shadertoy.com/view/7tSSDD
Cesium for Fluid: https://gitee.com/m3d/gis-shadertoy-fluid
// 设置渲染管线
setupRenderPipeline() {
this.
createComputePasses();
this.createMainRenderPass();
this.
startRenderLoop();
}
// 创建计算通道


_createComputePasses() {
    const commonUniforms = {
        iTime: () => this._time,
        iFrame: () => this._frameCount,
        resolution: () => this.config.resolution,
        waterSize: () => this.config.waterSize,
        fluidParam: () => this.config.fluidParams,
        customParam: () => this.config.customParams,
        minHeight: () => this.config.heightRange.min,
        maxHeight: () => this.config.heightRange.max,
        heightMap: () => this._heightMap,
    };
    this.computePasses = [
        this._createComputePass('A', {
            uniforms: {
                ...commonUniforms,
                iChannel0: () => this.textures.C,
                iChannel1: () => this.textures.D
            },
            shaderSource: BufferA
        }),
        this._createComputePass('B', {
            uniforms: {
                ...commonUniforms,
                iChannel0: () => this.textures.A,
                iChannel1: () => this.textures.D
            },
            shaderSource: BufferB
        }),
        this._createComputePass('C', {
            uniforms: {
                ...commonUniforms,
                iChannel0: () => this.textures.A,
                iChannel1: () => this.textures.B
            },
            shaderSource: BufferC
        }),
        this._createComputePass('D', {
            uniforms: {
                ...commonUniforms,
                iChannel0: () => this.textures.C,
                iChannel1: () => this.textures.B
            },
            shaderSource: BufferD
        })
    ];
}

// 计算通道工厂方法


_createComputePass(outputTextureName, { uniforms, shaderSource }) {
    return new CustomPrimitive({
        commandType: 'Compute',
        uniformMap: uniforms,
        fragmentShaderSource: new Cesium.ShaderSource({
            sources: [Command, shaderSource]
        }),
        geometry: RenderUtil.getFullscreenQuad(),
        outputTexture: this.textures[outputTextureName],
        preExecute: (cmd) => cmd.commandToExecute.outputTexture = this.textures[outputTextureName]
    });
}

// 创建主渲染通道

_createMainRenderPass() {
    const modelMatrix = generateModelMatrix([...this.config.lonLat, this.config.dimensions.z / 2], [90, 0, 0], [this.config.dimensions.x, this.config.dimensions.z, this.config.dimensions.y])
    this.mainRenderPass = new CustomPrimitive({
        commandType: 'Draw',
        uniformMap: this._getMainRenderUniforms(),
        vertexShaderSource: this._getVertexShader(),
        fragmentShaderSource: new Cesium.ShaderSource({
            sources: [Command, renderShaderSource]
        }),
        geometry: this._createBoxGeometry(),
        modelMatrix: modelMatrix,
        attributeLocations: this._getAttributeLocations(),
        rawRenderState: this._createRenderState()
    });
}

// 获取主渲染Uniform

_getMainRenderUniforms() {
    return {
        iTime: () => this._time,
        iFrame: () => this._frameCount,
        iResolution: () => this.config.resolution,
        iChannel0: () => this.textures.C,
        heightMap: () => this._heightMap,
        customParam: () => this.config.customParams,
        colorTexture: () => this.viewer.scene.view.globeDepth.colorFramebufferManager._colorTextures[0]
    };
}

// 启动渲染循环

_startRenderLoop() {
    this.postRenderHandler = this.viewer.scene.postRender.addEventListener(() => {
        if (!this._isActive) return;
        this._time = performance.now() / 1000;
        this._frameCount += this.config.timeStep;
    });
    this.computePasses.forEach(p => this.viewer.scene.primitives.add(p));
    this.viewer.scene.primitives.add(this.mainRenderPass);
}

仓库地址:

https://gitee.com/m3d/cesium-for-fluid

在线源码:

文档更新时间: 2025-06-04 22:09   作者:admin