Cesium 模型开挖

发布时间:2024年01月11日

和模型压平类似,此处开挖也是采用修改了模型的顶点,通过判断定的位置来判断改顶点是否加载。
缺点:会有毛边。
后续我会再上个修改源码无毛边的版本。

// 三维模型裁剪
class TilesetCut {
    constructor(tileset, opt) {
        if (!tileset) {
            console.log("缺少模型");
            return;
        }
        this.tileset = tileset;
        this.opt = opt || {};
        /**
         * @property {Boolean} iscutOutter 是否为外部裁剪,默认为内部裁剪
         */
        this._iscutOutter = this.opt.iscutOutter; // 是否为外部裁剪  默认为内部裁剪
        this.cutRegions = []; // 当前裁剪面数组对象
        /* this.modelMatrix = new Cesium.Matrix4(); // 世界坐标系--》模型坐标系
        Cesium.Matrix4.inverseTransformation(this.tileset.root.computedTransform, this.modelMatrix) */
        // 建立模型中心点坐标系
        const center = this.tileset.boundingSphere.center;
        const enuMtx4 = Cesium.Transforms.eastNorthUpToFixedFrame(center);
        this.modelMatrix = Cesium.Matrix4.inverse(enuMtx4, new Cesium.Matrix4());
        this.canvas = undefined;
    }

    get iscutOutter() {
        return this._iscutOutter
    }

    set iscutOutter(val) {
        this._iscutOutter = val;
        this.updateShader();
    }

    /**
     * 添加裁剪面
     * @param {Object} attr 参数
     * @param {Cesium.Cartesian3[]} attr.positions 压平面坐标
     * @param {Number} attr.id 唯一标识
     */
    addRegion(attr) {
        let { positions, id } = attr || {};
        if (!id) id = (new Date()).getTime() + "" + Number(Math.random() * 1000).toFixed(0);
        if (!positions || positions.length < 3) {
            console.log("缺少裁剪面坐标");
            return;
        }
        const index = this.cutRegions.findIndex(item => item.id === id)
        if (index == -1) {
            this.cutRegions.push({
                id: id,
                positions: positions
            })
        } else {
            this.cutRegions[index].positions = positions;
        }
        this.updateShader()
    }
    /**
     * 移除裁剪面
     * @param {String} id 
     */
    removeRegionById(id) {
        if (id) { // 表示移除所有的裁剪面
            const index = this.cutRegions.findIndex(item => item.id === id)
            if (index != -1) this.cutRegions.splice(index, 1)
        } else { // 表示移除单个的裁剪面
            this.cutRegions = [];
        }
        this.updateShader();
    }

    /**
     * 销毁
     */
    destroy() {
        this.tileset.customShader = undefined;
    }

    /**
     * 修改模型着色器
     */
    updateShader() {
        debugger
        // 定义着色器中裁剪函数
        const fs_textureMapRect = `
            vec4 textureMapRect(vec4 rect, sampler2D map, vec2 xy) {
                // 判断当前图元坐标和多边形关系 如果在多边形内 进行纹素拾取
                if (xy.x >= rect.x && xy.x <= rect.z && xy.y >= rect.y && xy.y <= rect.w) {
                    float w = rect.z - rect.x;
                    float h = rect.w - rect.y;
                    float s = (xy.x - rect.x) / w;
                    float t = (xy.y - rect.y) / h;
                    vec4 color = texture(map, vec2(s, 1.0 - t));
                    return color;
                }
                return vec4(1.0);
            }
        `;

        let allUniforms = {
            u_inverseModel: {
                type: Cesium.UniformType.MAT4,
                value: this.modelMatrix.clone()
            },
            u_unionCutRegions: {
                type: Cesium.UniformType.BOOL,
                value: this._iscutOutter
            }
        }

        // 构建多区域着色器
        let fs = ``;
        this.cutRegions.forEach(element => {
            const uniforms = this.createUniforms(element.positions, element.id)
            allUniforms = Cesium.combine(allUniforms, uniforms)
            fs += `
                vec4 color_${element.id} = textureMapRect(u_rect_${element.id}, u_map_${element.id}, xy);
                cutColor *= color_${element.id};
            `
        })

        fs += `
            if (u_unionCutRegions) {
                material.diffuse *= (vec3(1.0) - cutColor.rgb);
            } else {
                material.diffuse *= cutColor.rgb;
            }
            if (material.diffuse.r <= 0.0001 && material.diffuse.g <= 0.0001 && material.diffuse.b <= 0.0001) {
                discard;
            }
        `;

        this.tileset.customShader = new Cesium.CustomShader({
            uniforms: allUniforms,
            fragmentShaderText: ` 
            ${fs_textureMapRect}
            void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {
                vec4 positionMC = u_inverseModel * vec4(fsInput.attributes.positionWC, 1.0);
                vec2 xy = positionMC.xy;
                vec4 cutColor = vec4(1.0);
                ${fs}
            }`
        })

    }

    /**
     * 根据坐标创建片元着色器
     * @param {Cartesian3[]} positions 
     * @param {String} id 
     */
    createUniforms(positions, id) {
        if (!positions || positions.length < 3) {
            console.log("缺少裁剪面坐标");
            return;
        }
        id = id || Math.ceil(Math.random() * 100000) + '_' + Math.ceil(Math.random() * 100000)
        // 根据世界坐标范围计算相对模型坐标范围
        const xs = [], ys = [], zs = []
        // 计算模型坐标系下坐标
        const modelPoints = positions.map(p => {
            const point = Cesium.Matrix4.multiplyByPoint(this.modelMatrix, p, new Cesium.Cartesian3());
            xs.push(point.x)
            ys.push(point.y)
            zs.push(point.z)
            return point
        })
        // 计算当前裁剪面边界范围(模型坐标系下)
        const rect = new Cesium.Cartesian4(Math.min.apply(null, xs), Math.min.apply(null, ys), Math.max.apply(null, xs), Math.max.apply(null, ys))
        const canvas = document.createElement('canvas')
        canvas.width = 1024
        canvas.height = 1024
        const width = rect.z - rect.x
        const height = rect.w - rect.y
        const ctx = canvas.getContext('2d')
        ctx.fillStyle = '#fff' // 设置整体背景为白色
        ctx.fillRect(0, 0, canvas.width, canvas.height)
        ctx.beginPath()

        ctx.moveTo(canvas.width * (modelPoints[0].x - rect.x) / width, canvas.height * (modelPoints[0].y - rect.y) / height)

        for (let i = 1; i < modelPoints.length; i++) {
            ctx.lineTo(canvas.width * (modelPoints[i].x - rect.x) / width, canvas.height * (modelPoints[i].y - rect.y) / height)
        }

        ctx.closePath()
        ctx.fillStyle = '#000' // 根据填充的黑色来裁剪模型
        ctx.fill()

        this.canvas = canvas;

        const uniforms = {}
        uniforms[`u_rect_${id}`] = {
            type: Cesium.UniformType.VEC4,
            value: rect
        }
        uniforms[`u_map_${id}`] = {
            type: Cesium.UniformType.SAMPLER_2D,
            value: new Cesium.TextureUniform({
                url: canvas.toDataURL()
            }),
            minificationFilter: Cesium.TextureMinificationFilter.LINEAR,
            magnificationFilter: Cesium.TextureMagnificationFilter.LINEAR
        }

        return uniforms
    }

}

export default TilesetCut

调用方式:
?

let cutTool = new Cut(tileset);
 cutTool.addRegion({
                positions : positions,
                id : new Date().getTime()
            });

仓库地址:https://gitee.com/caozl1132/CesiumExp-tilesetFlat

文章来源:https://blog.csdn.net/caozl1132/article/details/135533874
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。