1. 程式人生 > 其它 >『轉載』在Cesium中繪製動態線段

『轉載』在Cesium中繪製動態線段

轉載自 https://blog.csdn.net/yue1241630499/article/details/118344231 請支援原創!

在Cesium中繪製動態線段

以下實現方法均屬野路子。

理想中,可以擴充套件polylinegeometry增加一個customdata屬性用於提交除位置外的其他頂點資料,類似於下文使用的color

計劃實現類似於螞蟻線的效果,在Cesium中翻看各種材質,沒法發現滿足條件的material或Appearance。那麼只能自己手工實現了。

基本思路

  • 傳遞頂點時,附加每個頂點距線段起點的距離,用此距離來實現線段分段
  • 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將線擴充為面時,是沿頂點法線方向向兩側擴充,但是擴充後的兩個點對應的線的距離是一樣的,但是實際長度不同明顯下方拐角處的點要比上放的點的距離要長才符合實際情況,導致顏色變化時不一致。

如果純用fragmentshader來寫是可以避免彎折的情況的,但是目前還不知道如何將完整的canvas繪製到cesium上。對用shahder繪製線感興趣的,可以參考這篇文章,採用距離場方式繪製線段。

後續有時間通過理想方式實現一遍。

PS: 有其他實現方法的歡迎留言