『转载』在Cesium中绘制动态线段

转载自 https://blog.csdn.net/yue1241630499/article/details/118344231 请支持原创!

在Cesium中绘制动态线段

以下实现方法均属野路子。

理想中,可以扩展polylinegeometry增加一个customdata属性用于提交除位置外的其他顶点数据,类似于下文使用的color

计划实现类似于蚂蚁线的效果,在Cesium中翻看各种材质,没法发现满足条件的material或Appearance。那么只能自己手工实现了。

『转载』在Cesium中绘制动态线段

基本思路

  • 传递顶点时,附加每个顶点距线段起点的距离,用此距离来实现线段分段
  • shader中对传入的距离取模,即可实现分段绘制不通的颜色

使用fabric

查看了fabric的相关资料后,发现fabric只能自定义uniform变量,没有提供接口可以传递额外的顶点数据。Fabric仅是材质,想来不可能通过材质提交新的顶点数据。此路不通,但是可以使用uniform类型的变量。

如何提交除顶点外的其他顶点数据?

看到PolylineGeometry可以额外提供顶点颜色,此时考虑可以将顶点距离通过每个顶点的color传入到shader中。

传入的color为归一化后的数据,因此计算距离时,需要将距离归一化到0~1之间。

最终解决方案

PolylineColorAppearance + Fabric

PolylineColorAppearance中提交自己的fragmentShaderSource,此处需要注意,同Material不同,Material中需要重写获取材质方法,但是Appearance中需要完全替换main方法。

创建Appearance后,将appearance的材质替换为fabric,此时可以通过color获取每个顶点对应的距离。

此时已经可以分段绘制线段了。

分段的线要动起来,可以传入Cesium绘制的时间,通过preRender事件可以获取绘制的起始事件,当前时间减起始时间即为已经开始绘制的时间。

uniform变量如何传递??

都知道fabric中可以指定uniforms属性来传递额外的变量,而且uniforms中的变量均为cesium生成对应的glsl代码,这一步过程暂不想去改动。将appearance的material修改后,通过调试发现,cesium将一次生成uniforms中的uniform变量的声明,但是会将变量名称做修改,即添加变量的序号作为尾缀,如

fabric:{
   
     
     
            uniforms:{
   
     
     						//glsl中对应的变量名
              u_itime:10.0,					//u_itime_0
              u_trailpercent:0.3,			//u_trailpercent_1
              u_trailmovespeed:5.0/50.0,	//u_trailmovespeed_2
            }
          }

到这里就没有问题了。。。。。

shader里实现分段,使用mod即可,拖尾效果使用smoothstep实现,shader代码如下:

precision highp float;

varying vec4 v_color;

void main(){
    float r=v_color.r-u_itime_0*u_trailmovespeed_2;
    float dist=smoothstep(0.0,u_trailpercent_1,mod(r,u_trailpercent_1));
    //红白混合
    gl_FragColor=mix(vec4(1.0,0.0,0.0,1.0),vec4(1.0),dist);
}

完整代码,可在sandcastle中运行

var viewer = new Cesium.Viewer("cesiumContainer");
var scene = viewer.scene;

var material=new Cesium.Material({
   
     
     
  fabric:{
   
     
     
    uniforms:{
   
     
     
      u_itime:10.0,
      u_trailpercent:0.3,
      u_trailmovespeed:5.0/50.0,
    }
  }
});

var primitive = new Cesium.Primitive({
   
     
     
  geometryInstances: new Cesium.GeometryInstance({
   
     
     
    geometry: new Cesium.PolylineGeometry({
   
     
     
      positions: Cesium.Cartesian3.fromDegreesArray([
        0.0,
        6.0,
        5.0,
        6.0,
        7.0,
        8.0,
      ]),
      width: 50.0,
      vertexFormat: Cesium.PolylineColorAppearance.VERTEX_FORMAT,
      colorsPerVertex: true,
      colors: [
        new Cesium.Color(0.1,0.0,0.0,1.0), 
        new Cesium.Color(0.7,0.0,0.0,1.0),
        new Cesium.Color(1.0,0.0,0.0,1.0)
      ],
    }),
  }),
  appearance: new Cesium.PolylineColorAppearance({
   
     
     
    translucent: false,
    fragmentShaderSource: `precision highp float;

    varying vec4 v_color;

    void main(){
      float r=v_color.r-u_itime_0*u_trailmovespeed_2;
      float dist=smoothstep(0.0,u_trailpercent_1,mod(r,u_trailpercent_1));
      gl_FragColor=mix(vec4(1.0,0.0,0.0,1.0),vec4(1.0),dist);
    }
    `,
  }),
});

primitive.appearance.material=material;

scene.primitives.add(primitive);

scene.preRender.addEventListener(function(s,t){
   
     
     
  let elaspTime=Cesium.JulianDate.now().secondsOfDay-t.secondsOfDay;
  primitive.appearance.material.uniforms.u_itime=elaspTime;
});

现有问题

拐角处,被弯折

图中,中间的白色色带明显未沿线方向,而是有一处弯折,这是由于,cesium将线扩充为面时,是沿顶点法线方向向两侧扩充,但是扩充后的两个点对应的线的距离是一样的,但是实际长度不同明显下方拐角处的点要比上放的点的距离要长才符合实际情况,导致颜色变化时不一致。

『转载』在Cesium中绘制动态线段

如果纯用fragmentshader来写是可以避免弯折的情况的,但是目前还不知道如何将完整的canvas绘制到cesium上。对用shahder绘制线感兴趣的,可以参考这篇文章,采用距离场方式绘制线段。

后续有时间通过理想方式实现一遍。

PS: 有其他实现方法的欢迎留言

上一篇:27.(cesium之家)cesium接入百度影像地图


下一篇:Cesium快速上手9-Camera和Scene中的其他函数使用