SuperMap iClient3D for Cesium 如何制作3D标注

SuperMap iClient3D for Cesium 如何制作3D标注

功能介绍

Cesium 中实现标注或者文字标签一般都是使用biliboard,biliboard开发简单快速,但是效果一般,只能是二维效果,一直面向屏幕。本篇文章通过生成贴图,在赋值给面或者墙体来实现三维文字效果。

效果展示

实现思路

主要实现思路是通过canvas生成一个背景透明的图片,然后通过贴到polygon或者wall上来实现三维label效果。

使用canvas生成背景透明文字图片

首先通过document创建一个canvas

javascript 复制代码
//新建图片(贴图)
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')

然后通过下面方法,通过传入canvas,和option来创建透明文字图片具体参数可以参考text变量

javascript 复制代码
// 文字内容
var text = {
 txt : '测试大桥',//文字内容
 color : 'rgb(255,255,0)',//文字颜色
 fontSize : 80,//文字大小
 position : [116.14845286822154,40.232665482746306,18.2629], // 中心坐标
 rotation : 0 // 旋转角度°(顺时针)
}

function createImage(canvas,ctx,option) {

 ctx.clearRect(0, 0, ctx.width, ctx.height)
 const txt =option.txt
    
 const chineseLength = txt.match(/[^\u0000-\u00ff]/g)?.length || 0
 const txtLength = (txt.length - chineseLength) / 2 + chineseLength
 const fontSize = option.fontSize

 const padding = fontSize / 4
 const canvasHeight = fontSize + padding
 const canvasWidth = txtLength * fontSize + padding * 2
 canvas.height = canvasHeight
 canvas.width = canvasWidth

 ctx.font = fontSize + 'px Arial'
 ctx.fillStyle = option.color
 ctx.fillText(txt, padding, fontSize)
 return { canvasWidth, canvasHeight }
}

创建polygon/wall并且使用自定义贴图

根据option中的中心点,自动计算ry,rx 计算wall的hierarchy,最后在material设置为图片

javascript 复制代码
     function addWall(canvas,ctx,option) {
         const unitWidth = 0.000005
         const unitHeight = 6 / 20
         const coods = option.position
         const { canvasWidth, canvasHeight } = createImage(canvas,ctx,option)
         const w = unitWidth * canvasWidth / 2
         const h = unitHeight * canvasHeight
      
         const ry = Math.sin(option.rotation / 180 * Math.PI) * w
         const rx = Math.cos(option.rotation / 180 * Math.PI) * w
          // 水平效果
         var txtpolygon = viewer.entities.add({
             polygon: {
                     hierarchy: Cesium.Cartesian3.fromDegreesArray([
                       coods[0]- rx,
                       coods[1],
                       coods[0]- rx,
                       coods[1]- rx/3,
                       coods[0],
                       coods[1]- rx/3,
                       coods[0],
                       coods[1]]
                      ),
                      height:  coods[2],
                   material: new Cesium.ImageMaterialProperty({
                       image: canvas.toDataURL('image/png'),
                       transparent: true
                     })
             } 
         }) }

存在问题

当文字图片和赋值的polygon/wall角度或者外接形状不同时,会导致文字大小不可控,文字出现变形的问题,只能一点一点调整角度进行适配。

完整代码

javascript 复制代码
function onload(Cesium) {
     var viewer = new Cesium.Viewer('cesiumContainer');

     var scene = viewer.scene;
     scene.lightSource.ambientLightColor = new Cesium.Color(0.65, 0.65, 0.65, 1);

     var promise = scene.open(URL_CONFIG.SCENE_MOVINGLIGHT);

     Cesium.when(promise, function (layers) {
         // 文字内容
         var text = {
           txt : '测试大桥',
           color : 'rgb(255,255,0)',
           fontSize : 80,
           position : [116.14845286822154,40.232665482746306,18.2629], // 中心坐标
           rotation : 0 // 旋转角度°(顺时针)
         }
         //新建图片(贴图)
         const canvas = document.createElement('canvas')
         const ctx = canvas.getContext('2d')
         //创建面
         addWall(canvas,ctx,text);
     });
     
     function createImage(canvas,ctx,option) {
     
         ctx.clearRect(0, 0, ctx.width, ctx.height)
         const txt =option.txt
            
         const chineseLength = txt.match(/[^\u0000-\u00ff]/g)?.length || 0
         const txtLength = (txt.length - chineseLength) / 2 + chineseLength
         const fontSize = option.fontSize
       
         const padding = fontSize / 4
         const canvasHeight = fontSize + padding
         const canvasWidth = txtLength * fontSize + padding * 2
         canvas.height = canvasHeight
         canvas.width = canvasWidth
    
         ctx.font = fontSize + 'px Arial'
         ctx.fillStyle = option.color
         ctx.fillText(txt, padding, fontSize)
         return { canvasWidth, canvasHeight }
     }
     
     function addWall(canvas,ctx,option) {
      
         const unitWidth = 0.000005
         const unitHeight = 6 / 20
         const coods = option.position
         const { canvasWidth, canvasHeight } = createImage(canvas,ctx,option)
         const w = unitWidth * canvasWidth / 2
         const h = unitHeight * canvasHeight
      
         const ry = Math.sin(option.rotation / 180 * Math.PI) * w
         const rx = Math.cos(option.rotation / 180 * Math.PI) * w
          // 水平效果
         var txtpolygon = viewer.entities.add({
             polygon: {
                     hierarchy: Cesium.Cartesian3.fromDegreesArray([
                       coods[0]- rx,
                       coods[1],
                       coods[0]- rx,
                       coods[1]- rx/3,
                       coods[0],
                       coods[1]- rx/3,
                       coods[0],
                       coods[1]]
                      ),
                      height:  coods[2],
                   material: new Cesium.ImageMaterialProperty({
                       image: canvas.toDataURL('image/png'),
                       transparent: true
                     })
             } 
         })
            // 创建线效果
     var redLine = viewer.entities.add({
         name : 'Red line on the surface',
         polyline : {
             positions : Cesium.Cartesian3.fromDegreesArrayHeights([coods[0]- rx,coods[1]- rx/3, coods[2],coods[0],coods[1]- rx/3, coods[2]]),
             width : 5,
             material : Cesium.Color.RED
         }
     });
         
         
             // 创建垂直效果
         var txtWall = viewer.entities.add({
           name: 'WallTxt',
           wall: {
             positions: Cesium.Cartesian3.fromDegreesArrayHeights([
               coods[0] - rx,
               coods[1] - ry,
               coods[2],
               coods[0] + rx,
               coods[1] + ry,
               coods[2]
             ]),
             minimumHeights: [coods[2], coods[2]],
             maximumHeights: [coods[2] + h, coods[2] + h],
             material: new Cesium.ImageMaterialProperty({
               image: canvas.toDataURL('image/png'),
               transparent: true
             })
           }
         })
      }
 }
 if (typeof Cesium !== 'undefined') {
     window.startupCalled = true;
     onload(Cesium);
 }
相关推荐
探物 AI8 天前
【3D·感知】从PointNet到PointPillars:如何让自动驾驶汽车“实时“看见3D世界?
3d·自动驾驶·汽车
苏州邦恩精密9 天前
GOM三维扫描在制造中的真实价值:让“修模”从经验动作变成数据动作
人工智能·科技·机器学习·3d·自动化·制造
YHHLAI9 天前
CSS 3D 硬核解析:四个属性手写旋转立方体
前端·css·3d
云飞云共享云桌面9 天前
传统工作站 vs 云飞云共享云桌面:制造业设计云桌面选型深度对比
运维·服务器·前端·网络·3d·架构·制造
LONGZETECH9 天前
无人机仿真教学软件选型实战:5 个硬核技术维度,避开实训建设踩坑
3d·无人机·交互·cocos2d
装不满的克莱因瓶9 天前
了解3D卷积原理——从空间感知到时空建模的深度学习核心算子
人工智能·pytorch·python·深度学习·机器学习·3d·ai
雪的季节9 天前
Qt Graphs 2D+3D介绍
qt·3d
CG_MAGIC9 天前
3ds Max材质编辑器:精简模式与Slate模式对比
3d·编辑器·材质·贴图·uv·建模教程
装不满的克莱因瓶9 天前
掌握3D CNN模型结构——从时空特征建模到视频理解与医学影像核心架构
人工智能·pytorch·python·深度学习·神经网络·3d·cnn
AniShort10 天前
AniShort携3D世界+3D导演台王炸组合AI短剧协作平台亮相2026横店AI短剧大会 近亿元融资赋能短剧工业化
人工智能·microsoft·3d