cesium 光墙

559 阅读2分钟

所有的类型都引入 cesium 1.102.0 版本

glsl 自定义着色器+代码注释

interface DirectionWallShaderProps {
    /** 表示应用动画效果的重复次数 */
    count: number;
    /** 表示动画的方向,可以是+(正向)或-(负向) */
    direction: '+' | '-';
    /** 纹理坐标的方向 vertical 垂直 cross 水平 */
    freely: 'vertical' | 'cross';
}
/**
 *  glsl 自定义材质
    vec4 colorImage: 声明一个vec4变量,用于存储从纹理图像中采样得到的颜色。
    texture(image, ...): 使用image纹理进行采样。image是一个uniform变量,需要在着色器之外赋值。
    vec2(fract(float(${options.count}) * ${options.freely === 'vertical' ? 'st.s' : 'st.t'} ${options.direction} animation), fract(st.t)): 这是纹理采样所需的二维坐标。坐标的两个分量都在0到1之间。这里的表达式通过插值实现了动画效果。
    ${options.count}: 一个选项,表示应用动画效果的重复次数。
    ${options.freely === 'vertical' ? 'st.s' : 'st.t'}: 根据选项freely的值,选择使用纹理坐标的s分量(垂直方向)或t分量(水平方向)。
    ${options.direction}: 选项,表示动画的方向,可以是+(正向)或-(负向)。
    animation: 一个变量,通过czm_frameNumber/60.0计算得到,表示从开始渲染至今经过的时间(以秒为单位)。其值会随着时间的推移而改变,从而实现动画效果。
    通过将纹理坐标与动画时间结合,代码实现了根据时间改变纹理坐标,从而实现动画效果。最后,将采样得到的颜色存储在colorImage变量中。
 */
const getDirectionWallShader = (options: DirectionWallShaderProps) => {
    return `czm_material czm_getMaterial(czm_materialInput materialInput) {
        // 获取默认的材质
        czm_material material = czm_getDefaultMaterial(materialInput);
        // 获取纹理坐标
        vec2 st = materialInput.st;
        // 基于帧数计算动画值
        float animation = czm_frameNumber/60.0;
        // 计算纹理坐标和动画值的组合
        vec4 colorImage = texture(image, vec2(fract(float(${options.count}) * ${options.freely === 'vertical' ? 'st.s' : 'st.t'} ${
        options.direction
    } animation), fract(st.t)));
        // 定义输出的片段颜色
        vec4 fragColor;
        // 计算片段颜色的 RGB 值
        fragColor.rgb = (colorImage.rgb+color.rgb) / 1.0;
        // 应用 gamma 矫正
        fragColor = czm_gammaCorrect(fragColor);
        // 设置材质的漫反射、透明度和自发光属性
        material.diffuse = colorImage.rgb;
        material.alpha = colorImage.a;
        material.emission = fragColor.rgb;
        // 返回最终的材质
        return material;
    }`;
};

自定义材质

class WallShader {
    private color: Color;
    isConstant!: boolean;
    definitionChanged = new Event();
    /** 图片 */
    image: string;
    freely: DirectionWallShaderProps['freely'];
    direction: DirectionWallShaderProps['direction'];
    count: DirectionWallShaderProps['count'];
    constructor({ color, image, freely, direction, count }: WallShaderProps) {
        this.definitionChanged = new Event();
        this.isConstant = false;
        this.color = color;
        this.image = image;
        this.freely = freely;
        this.direction = direction;
        this.count = count;

        this.addMaterial();
    }
    getType() {
        return 'WallShader';
    }
    equals(other: WallShader) {
        return this === other || other instanceof WallShader;
    }
    getValue(
        _time: JulianDate,
        result: {
            color: Color;
            image: string;
        }
    ) {
        return result;
    }
    addMaterial() {
        // 暂时只能这么写
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        (Material as any)._materialCache.addMaterial('WallShader', {
            fabric: {
                type: 'WallShader',
                uniforms: {
                    color: this.color,
                    image: this.image
                },
                source: getDirectionWallShader({
                    freely: this.freely,
                    direction: this.direction,
                    count: this.count
                })
            },

            translucent: () => {
                return true;
            }
        });
    }
}

代码使用

viewer.entities.add({
    id: 'wall',
    wall: {
        positions: positions,
        maximumHeights: positions.map(() => height),
        minimumHeights: positions.map(() => 0),
        material: new WallShader({
            color: Color.fromCssColorString(wallColor),
            image,
            freely,
            count,
            direction
        }) as unknown as MaterialProperty
    }
});