Cesium官方教程8-- 幾何體和外觀效果

原文地址:https://cesiumjs.org/tutorials/Geometry-and-Appearances/

幾何體和外觀效果(Geometry and Appearances)

這篇教程會教大家學(xué)習(xí)Primitive API中支持的幾何體和外觀效果。這篇教程并不是面向Cesium的普通用戶,主要討論Cesium的高級知識,包括自定義三角網(wǎng)(mesh),形狀(shape),體(volume)以及他們的外觀。如果你是初學(xué)者,建議先學(xué)下這篇教程
Cesium可以使用Entity創(chuàng)建不同的幾何體,比如多邊形和橢圓等。比如把下面代碼拷貝到 Sandcastle 的Hello World 就能創(chuàng)建一個帶條紋狀材質(zhì)的矩形:

var viewer = new Cesium.Viewer('cesiumContainer');

viewer.entities.add({
    rectangle : {
        coordinates : Cesium.Rectangle.fromDegrees(-100.0, 20.0, -90.0, 30.0),
        material : new Cesium.StripeMaterialProperty({
            evenColor: Cesium.Color.WHITE,
            oddColor: Cesium.Color.BLUE,
            repeat: 5
        })
    }
});

條紋矩形

這篇教程里,我們深入到圖元內(nèi)部,使用 Geometry類和 Appearance 類來創(chuàng)建效果。幾何體定義了圖元的結(jié)構(gòu),比如三角網(wǎng)、線、點等。外觀(appearance)定義了圖片的著色效果,包含完整的頂點(vertex)和片段(fragment)著色器(shader)以及著色器狀態(tài)。

Cesium支持下列幾何體:


Box Geometry

BoxGeometry

Box Outline Geometry

BoxOutlineGeometryA box

Circle Geometry

CircleGeometry

Circle Outline Geometry

CircleOutlineGeometry

Corridor Geometry

CorridorGeometry

Corridor Outline Geometry

CorridorOutlineGeometry 以米為單位的折線 和 一個擠壓高度

Cylinder Geometry

CylinderGeometry

Cylinder Outline Geometry

CylinderOutlineGeometry圓柱, 椎體,半椎體

Ellipse Geometry

EllipseGeometry

Ellipse Outline Geometry

EllipseOutlineGeometry橢圓或者垂直擠壓的橢圓

Ellipsoid Geometry

EllipsoidGeometry

Ellipsoid Outline Geometry

EllipsoidOutlineGeometry橢球體

Extent Geometry

RectangleGeometry

Extent Outline Geometry

RectangleOutlineGeometry矩形或者垂直擠壓矩形

Polygon Geometry

PolygonGeometry

Polygon Outline Geometry

PolygonOutlineGeometry多邊形,支持帶洞以及垂直擠壓

Polyline Geometry

PolylineGeometry

Polyline Outline Geometry

SimplePolylineGeometry像素寬度定義的折線段

Volume Geometry

PolylineVolumeGeometry

Volume Outline Geometry

PolylineVolumeOutlineGeometry一個二維圖形沿著折線的延伸體。

Sphere Geometry

SphereGeometry

Sphere outline Geometry

SphereOutlineGeometry球體

Wall Geometry

WallGeometry

Wall Outline Geometry

WallOutlineGeometry垂直于地表的墻面

幾何體全家福

使用幾何體和外觀的優(yōu)勢:

  • 性能 - 尤其是繪制大量靜態(tài)圖元(比如整個美國的郵政編碼區(qū)域多邊形),使用幾何體可以把他們組合成一個單一的幾何體,這樣會減少cpu的開銷,并且充分利用GPU的能力。組合幾何體可以在web worker中完成,不會影響用戶界面的響應(yīng)。

  • 靈活性 - 圖元由幾何體和外觀構(gòu)成。不過他們可以單獨修改。新建的幾何體可以兼容多種不同的外觀,反之亦然。

  • 底層訪問 - 外觀提供了近乎最底層的渲染訪問,但是又不需要直接擔(dān)心渲染 Renderer 的細節(jié)技術(shù) 。外觀使下面的技術(shù)簡單了很多:

    • 編寫完整的頂點和片段著色器GLSL代碼。
    • 使用用戶自定義的渲染狀態(tài)。

當(dāng)然也有一些缺點:

  • 使用幾何體和外觀需要寫更多的代碼,并且需要對圖形知識有深刻的理解。Entity是應(yīng)用層的抽象;而幾何體和外觀更像是一個傳統(tǒng)3D引擎的級別。
  • 對于靜態(tài)數(shù)據(jù),幾何體合并非常有效,但是對于動態(tài)數(shù)據(jù)不適合。
    使用幾何體和外觀來重新編寫示例代碼:
var viewer = new Cesium.Viewer('cesiumContainer');
var scene = viewer.scene;

// 原始代碼
//viewer.entities.add({
//    rectangle : {
//        coordinates : Cesium.Rectangle.fromDegrees(-100.0, 20.0, -90.0, 30.0),
//        material : new Cesium.StripeMaterialProperty({
//            evenColor: Cesium.Color.WHITE,
//            oddColor: Cesium.Color.BLUE,
//            repeat: 5
//        })
//    }
//});

var instance = new Cesium.GeometryInstance({
  geometry : new Cesium.RectangleGeometry({
    rectangle : Cesium.Rectangle.fromDegrees(-100.0, 20.0, -90.0, 30.0),
    vertexFormat : Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT
  })
});

scene.primitives.add(new Cesium.Primitive({
  geometryInstances : instance,
  appearance : new Cesium.EllipsoidSurfaceAppearance({
    material : Cesium.Material.fromType('Stripe')
  })
}));

沒有用矩形的entity,我們使用了普通的 Primitive, 它里面連接和幾何體和外觀?,F(xiàn)在先忽略 Geometry和 a GeometryInstance 的區(qū)別,只需知道instance是geometry的容器。
創(chuàng)建矩形幾何體 RectangleGeometry的時候,這個矩形區(qū)域的三角網(wǎng)會貼合地球曲率。

網(wǎng)格效果

因為我們預(yù)先知道這個幾何體是在球面上,所以直接使用 EllipsoidSurfaceAppearance。這樣做也能節(jié)省內(nèi)存 ,支持所有的材質(zhì),因為幾何體是在橢球體上方的固定高度(譯者注:個人理解是說頂點可以只需要二維坐標(biāo),高度值可以當(dāng)作uniform傳進去)。

幾何體合并

當(dāng)使用一個圖元去繪制多個靜態(tài)幾何體的時候,會有些效率提升。比如我們畫兩個矩形:

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

var instance = new Cesium.GeometryInstance({
  geometry : new Cesium.RectangleGeometry({
    rectangle : Cesium.Rectangle.fromDegrees(-100.0, 20.0, -90.0, 30.0),
    vertexFormat : Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT
  })
});

var anotherInstance = new Cesium.GeometryInstance({
  geometry : new Cesium.RectangleGeometry({
    rectangle : Cesium.Rectangle.fromDegrees(-85.0, 20.0, -75.0, 30.0),
    vertexFormat : Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT
  })
});

scene.primitives.add(new Cesium.Primitive({
  geometryInstances : [instance, anotherInstance],
  appearance : new Cesium.EllipsoidSurfaceAppearance({
    material : Cesium.Material.fromType('Stripe')
  })
}));

兩個矩形

創(chuàng)建了另一個矩形的instance,然后把兩個instance都添加到一個圖元里,使用同一個外觀去繪制。 一些外觀允許為每個instance設(shè)置不同的屬性(attribute)。比如,使用 PerInstanceColorAppearance 對每個instance著不同顏色。

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

var instance = new Cesium.GeometryInstance({
  geometry : new Cesium.RectangleGeometry({
    rectangle : Cesium.Rectangle.fromDegrees(-100.0, 20.0, -90.0, 30.0),
    vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
  }),
  attributes : {
    color : new Cesium.ColorGeometryInstanceAttribute(0.0, 0.0, 1.0, 0.8)
  }
});

var anotherInstance = new Cesium.GeometryInstance({
  geometry : new Cesium.RectangleGeometry({
    rectangle : Cesium.Rectangle.fromDegrees(-85.0, 20.0, -75.0, 30.0),
    vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
  }),
  attributes : {
    color : new Cesium.ColorGeometryInstanceAttribute(1.0, 0.0, 0.0, 0.8)
  }
});

scene.primitives.add(new Cesium.Primitive({
  geometryInstances : [instance, anotherInstance],
  appearance : new Cesium.PerInstanceColorAppearance()
}));

不同顏色的矩形

每個intance有一個Color 屬性。圖元里創(chuàng)建一個PerInstanceColorAppearance,它知道使用每個instance的color屬性去著色。

幾何體合并允許Cesium高效的渲染大量幾何體。下面示例繪制了2592個不同顏色的矩形。優(yōu)化之后,渲染非常塊。

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

var instances = [];

for (var lon = -180.0; lon < 180.0; lon += 5.0) {
  for (var lat = -85.0; lat < 85.0; lat += 5.0) {
    instances.push(new Cesium.GeometryInstance({
      geometry : new Cesium.RectangleGeometry({
        rectangle : Cesium.Rectangle.fromDegrees(lon, lat, lon + 5.0, lat + 5.0),
        vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
      }),
      attributes : {
        color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromRandom({alpha : 0.5}))
      }
    }));
  }
}

scene.primitives.add(new Cesium.Primitive({
  geometryInstances : instances,
  appearance : new Cesium.PerInstanceColorAppearance()
}));

幾何體合并

拾取

當(dāng)instance合并之后,仍然支持獨立訪問。通常,我們會設(shè)置一個id屬性, Scene.pick函數(shù)里通過它來判定哪個instance被拾取。這個id 可以任何js類型:字符串,數(shù)字,帶屬性的對象等等。
下面的示例創(chuàng)建一個帶id 的instance,當(dāng)它被點擊的時候控制臺會輸出一個消息。

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

var instance = new Cesium.GeometryInstance({
  geometry : new Cesium.RectangleGeometry({
    rectangle : Cesium.Rectangle.fromDegrees(-100.0, 30.0, -90.0, 40.0),
    vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
  }),
  id : 'my rectangle',
  attributes : {
    color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED)
  }
});

scene.primitives.add(new Cesium.Primitive({
  geometryInstances : instance,
  appearance : new Cesium.PerInstanceColorAppearance()
}));

var handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
handler.setInputAction(function (movement) {
    var pick = scene.pick(movement.position);
    if (Cesium.defined(pick) && (pick.id === 'my rectangle')) {
      console.log('Mouse clicked rectangle.');
    }
  }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

使用id 而不是用instance對象本身去判定,主要是為了避免在創(chuàng)建圖元之后,我們的圖元甚至我們的項目對所有的instance對象 以及 它的幾何體 一直被引用無法釋放內(nèi)存。因為幾何體一般包含了一個比較大的數(shù)組,這種方式就可以幫我們節(jié)省大量內(nèi)存。

幾何體intances

目前為止,我們創(chuàng)建的每個幾何體instance都只包含一個幾何體。此外,instance竟然用來把同一個幾何體放置在場景的不同位置,包括不同大小和方向。由于多個instance可以引用同一個幾何體( Geometry),而每個instance可以有不同的偏移矩陣(modelMatrix)。這樣,我們就只需要計算一次幾何體(計算頂點等)而多次使用它。

幾何體 instance

下面的代碼創(chuàng)建了一個EllipsoidGeometry 和 兩個instance. 每個instance 引用了相同的橢球幾何體,但是使用 modelMatrix放到不同位置,這里效果是一個疊在另一個之上。

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

var ellipsoidGeometry = new Cesium.EllipsoidGeometry({
    vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
    radii : new Cesium.Cartesian3(300000.0, 200000.0, 150000.0)
});

var cyanEllipsoidInstance = new Cesium.GeometryInstance({
    geometry : ellipsoidGeometry,
    modelMatrix : Cesium.Matrix4.multiplyByTranslation(
        Cesium.Transforms.eastNorthUpToFixedFrame(Cesium.Cartesian3.fromDegrees(-100.0, 40.0)),
        new Cesium.Cartesian3(0.0, 0.0, 150000.0),
        new Cesium.Matrix4()
    ),
    attributes : {
        color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.CYAN)
    }
});

var orangeEllipsoidInstance = new Cesium.GeometryInstance({
    geometry : ellipsoidGeometry,
    modelMatrix : Cesium.Matrix4.multiplyByTranslation(
        Cesium.Transforms.eastNorthUpToFixedFrame(Cesium.Cartesian3.fromDegrees(-100.0, 40.0)),
        new Cesium.Cartesian3(0.0, 0.0, 450000.0),
        new Cesium.Matrix4()
    ),
    attributes : {
        color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.ORANGE)
    }
});

scene.primitives.add(new Cesium.Primitive({
    geometryInstances : [cyanEllipsoidInstance, orangeEllipsoidInstance],
    appearance : new Cesium.PerInstanceColorAppearance({
        translucent : false,
        closed : true
    })
}));

橢球體instances

更新每個instance的屬性

即便是已經(jīng)添加到圖元里,每個instance的一些屬性也可以修改,包括:

  • Color : ColorGeometryInstanceAttribute 決定了幾何體顏色。不過圖元應(yīng)該設(shè)置一個 PerInstanceColorAppearance外觀。
  • Show :布爾變量決定instance是否可見,對任意instance都有效。
    下面代碼演示如何修改幾何體instance的顏色:
    This example shows how to change the color of the geometry instance:
var viewer = new Cesium.Viewer('cesiumContainer');
var scene = viewer.scene;

var circleInstance = new Cesium.GeometryInstance({
    geometry : new Cesium.CircleGeometry({
        center : Cesium.Cartesian3.fromDegrees(-95.0, 43.0),
        radius : 250000.0,
        vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
    }),
    attributes : {
        color : Cesium.ColorGeometryInstanceAttribute.fromColor(new Cesium.Color(1.0, 0.0, 0.0, 0.5))
    },
    id: 'circle'
});
var primitive = new Cesium.Primitive({
    geometryInstances : circleInstance,
    appearance : new Cesium.PerInstanceColorAppearance({
        translucent : false,
        closed : true
    })
});
scene.primitives.add(primitive);

setInterval(function() {
    var attributes = primitive.getGeometryInstanceAttributes('circle');
    attributes.color = Cesium.ColorGeometryInstanceAttribute.toValue(Cesium.Color.fromRandom({alpha : 1.0}));
},2000);

幾何體的屬性需要通過 primitive.getGeometryInstanceAttributes來獲取到。attributes 里的值可以直接修改。這里,我們每2秒鐘設(shè)置'circle'這個幾何體隨機顏色。

外觀(Appearances)

幾何體定義了結(jié)構(gòu)。圖元的另一個關(guān)鍵屬性是appearance,決定圖元的著色,也就說每個像素是如何上色的。一個圖元可以有若干個幾何體instance,但是只能有一個appearance屬性。根據(jù)appearance類型不同,一個appearance可能有一個 material 屬性,材質(zhì)屬性決定了大體的著色( the bulk of the shading)。

Appearances

Cesium 包含下述外觀類型:

MaterialAppearance

MaterialAppearance 所有幾何體都使用同一個外觀,支持使用 materials 去定義著色效果.

EllipsoidSurface

EllipsoidSurface MaterialAppearance 的簡化版本,假定幾何體都和地球橢球體平行,就像多邊形一樣。使用這個可以在計算大量頂點屬性的時候節(jié)省內(nèi)存

PerInstanceColorAppearance

PerInstanceColorAppearance 每個instance使用不同的顏色去著色。

PolylineMaterialAppearance

PolylineMaterialAppearance 支持在折線上設(shè)置材質(zhì)。

PolylineColorAppearance

PolylineColorAppearance支持折線在每個頂點或者每一段設(shè)置顏色。

外觀完整的定義了頂點和片段著色器代碼,在GPU中圖元渲染的時候使用。除非要自定義外觀,否則我們很少使用它們。外觀也定義了完整的渲染你狀態(tài),它控制了圖元渲染時候的GPU狀態(tài)。我們可以使用高級的屬性來定義渲染狀態(tài),比如 閉合closed半透明translucent,外觀會把他們轉(zhuǎn)換為真正的底層狀態(tài),比如:

//  一個不透明的盒子,視點永遠不會進到里面去 
//   那么就需要啟用背面裁剪,深度檢測,不需要混合。

var appearance  = new Cesium.PerInstanceColorAppearance({
  translucent : false,
  closed : true
});

// 這個和上面的設(shè)置等價
var anotherAppearance  = new Cesium.PerInstanceColorAppearance({
  renderState : {
    depthTest : {
      enabled : true
    },
    cull : {
      enabled : true,
      face : Cesium.CullFace.BACK
    }
  }
});

一旦我們的外觀創(chuàng)建了,我們不能修改它的renderState屬性,但是我們能修改它的材質(zhì) material。當(dāng)然,我們可以整個替換圖元的appearance屬性。

大部分外觀包含 flatfaceForward 屬性, 這個直接控制了GLSL的著色效果。

  • flat - 純色著色,不考慮光照效果。
  • faceForward - 當(dāng)有光照的的時候,當(dāng)視圖正對它的時候反轉(zhuǎn)法向量,避免墻體的背面是黑色的。
    flat : true | faceForward : false | faceForward : true |
flat:true
faceForward : false
faceForward : true

幾何體和外觀的匹配性

我們發(fā)現(xiàn)不是所有的外觀都能作用在任意幾何體上。比如EllipsoidSurfaceAppearance 不能用在WallGeometry 上,因為墻永遠垂直地表,而不是平行地表。
隱含之意,一個外觀能和一個幾何體匹配,需要頂點格式匹配,也就是說幾何體必須包含外觀需要的頂點格式數(shù)據(jù)。創(chuàng)建一個幾何體的時候,可以指定一個 VertexFormat 參數(shù)。

有時候為了簡化問題,但是接受一點點浪費和效率低,可以計算一個幾何體的所有頂點屬性格式,這樣就能和所有外觀兼容(忽略per-instance屬性)

var geometry = new Cesium.RectangleGeometry({
  vertexFormat : Cesium.VertexFormat.ALL
  // ...
});

兼容
不兼容

如果使用EllipsoidSurfaceAppearance,比如我們只創(chuàng)建了頂點的位置屬性,那么就會崩潰(get away)。

var geometry = new Ceisum.RectangleGeometry({
  vertexFormat : Ceisum.VertexFormat.POSITION_ONLY
  // ...
});

通常,我們怎么知道某種外觀需要哪種頂點格式?大部分外觀都有一個 vertexFormat 屬性, 甚至一個 VERTEX_FORMAT靜態(tài)常量。

var geometry = new Ceisum.RectangleGeometry({
  vertexFormat : Ceisum.EllipsoidSurfaceAppearance.VERTEX_FORMAT
  // ...
});

var geometry2 = new Ceisum.RectangleGeometry({
  vertexFormat : Ceisum.PerInstanceColorAppearance.VERTEX_FORMAT
  // ...
});

var appearance = new Ceisum.MaterialAppearance(/* ... */);
var geometry3 = new Ceisum.RectangleGeometry({
  vertexFormat : appearance.vertexFormat
  // ...
});

同樣,幾何體的 vertexFormat 屬性也決定了幾何體是否可以合并。如果要合并,可以幾何體類型不同,但是必須保證頂點格式一致。

相關(guān)資源

用戶手冊:

想了解材質(zhì)的更多內(nèi)容,請訪問Fabric。
想了解這塊的開發(fā)計劃,請訪問: Geometry and Appearances Roadmap.

中國最專業(yè)的Cesium開發(fā)者社區(qū)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容