Cesium中Primitive讲解

前面介绍了entity方式绘制对象,现在我们开始接触primitive方式,primitive方式更接近渲染引擎底层,由于我对webgl知之甚少,因此primitive接口我现在也是一知半解,写这个博客我参考了这一篇博客Cesium(三) 几何图形与外观。(https://blog.csdn.net/happyduoduo1/article/details/51868042

1、Primitive由两个部分组成

(1)几何形状(Geometry):定义了Primitive的结构,例如三角形、线条、点等
(2)外观(Appearance ):定义Primitive的着色(Sharding),包括GLSL(OpenGL着色语言,OpenGL ShadingLanguage)顶点着色器和片段着色器( vertex and fragment shaders),以及渲染状态(render state)
Cesium支持以下几何图形:


image.png

image.png

image.png

使用Geometry和Appearance 具有以下优势:
(1)性能:绘制大量Primitive时,可以将其合并为单个Geometry以减轻CPU负担、更好的使用GPU。合并Primitive由web worker线程执行,UI保持响应性
(2)灵活性:Geometry与Appearance 解耦,两者可以分别进行修改
(3)低级别访问:易于编写GLSL 顶点、片段着色器、使用自定义的渲染状态
同时,具有以下劣势:
(1)需要编写更多地代码
(2)需要对图形编程有更多的理解,特别是OpenGL的知识

下面代码是entity与primitive方式对比:

       //entity方式
        viewer.entities.add({
            rectangle: {
                coordinates: Cesium.Rectangle.fromDegrees(110.20, 34.55, 111.20, 35.55),
                material: new Cesium.StripeMaterialProperty({
                    evenColor: Cesium.Color.WHITE,
                    oddColor: Cesium.Color.BLUE,
                    repeat:5
                })
            }
        });
 

        //primitive方式
        var instance = new Cesium.GeometryInstance({
            geometry: new Cesium.RectangleGeometry({
                rectangle: Cesium.Rectangle.fromDegrees(105.20, 30.55, 106.20, 31.55),
                vertexFormat:Cesium.EllipsoidSurfaceAppearance.VERTEXT_FORMAT
            })
        });
        viewer.scene.primitives.add(new Cesium.Primitive({
            geometryInstances: instance,
            appearance: new Cesium.EllipsoidSurfaceAppearance({
                material:Cesium.Material.fromType('Stripe')
            })
        }));

2、合并几何图形(Combing Geometries)

合并多个GeometryInstances 为一个Primitive可以极大的提高性能,下面的例子创建了2592个颜色各异的矩形,覆盖整个地球 :

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 = -90.0; lat < 90.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 )
            } ),
            attributes : {
                color : Cesium.ColorGeometryInstanceAttribute.fromColor( Cesium.Color.fromRandom( {
                    alpha : 0.5
                } ) )
            }
        } ) );
    }
}
 
scene.primitives.add( new Cesium.Primitive( {
    geometryInstances : instances, //合并
    //某些外观允许每个几何图形实例分别指定某个属性,例如:
    appearance : new Cesium.PerInstanceColorAppearance()
} ) );

3、选取几何图形(Picking)

即使多个 GeometryInstance被合并为单个Primitive,让然可以独立的被访问。我们可以为每一个GeometryInstance指定一个id,并且可以通过Scene.pick来判断该实例是否被选取:

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 )
    } ),
    id : 'rectangle-1',
    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 === 'rectangle-1' ) )
    {
        console.log( '矩形被选取' );
    }
}, Cesium.ScreenSpaceEventType.LEFT_CLICK );

4、几何图形实例(Geometry Instances)

上面的例子中,我们已经用到了GeometryInstances,注意GeometryInstance与Geometry的关系:前者是后者的容器,多个Instance可以共用一个Geometry,并且可以通过GeometryInstances.modelMatrix属性提供不同position、scale、rotate等位置、缩放、旋转信息。例如,下面的例子使用同一个Geometry绘制了两个Instance,一个位于另一个的上方:


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 ) ),
    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 ) ),
    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
    } )
} ) );

5、更新单个GeometryInstance的属性

在添加到Primitive中以后,仍然可以修改几何图形的某些属性:
(1)颜色:如果Primitive设置了PerInstanceColorAppearance外观,则可以修改ColorGeometryInstanceAttribute类型的颜色
(2)可见性:任何实例可以修改可见性

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 ) ),
        show : new Cesium.ShowGeometryInstanceAttribute( true ) //显示或者隐藏
    },
    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 );

6、外观(Appearances)

Primitive由两个重要部分组成:几何图形实例、外观,一个Primitive只能有一个外观,而可以有多个实例。几何图形定义了结构,外观定义了每个像素被如何着色,外观可能使用材质(Material)。这些对象的关系如下图所示:


image.png

外观定义了需要在GPU上执行的完整的GLSL顶点、片段着色器,通常不需要修改这一部分,除非需要定义自己的外观。
外观还定义了完整的render state,用于在绘制Primitive时控制GPU的状态,可以直接或者通过高层API来定义render state:

//下面的外观可用于定义一个Viewer不可进入的不透明盒子
var appearance = new Cesium.PerInstanceColorAppearance( {
    translucent : false,
    closed : true
} );
//下面的代码效果同上
var translucent = new Cesium.PerInstanceColorAppearance( {
    renderState : {
        depthTest : {
            enabled : true
        },
        cull : {
            enabled : true,
            face : Cesium.CullFace.BACK
        }
    }
} );

一旦外观被创建,其render state就不可再变,但是其材质是可以替换的。另外Primitive的外观也是不可修改的。
大部分外观具有flat、faceForward属性,可以间接的控制GLSL 着色器:
(1)flat:扁平化着色,不考虑光线的作用
(2)faceForward:布尔值,控制光照效果

7、Geometry与Appearance的兼容性

需要注意,不是所有外观和所有几何图形可以搭配使用,例如EllipsoidSurfaceAppearance与WallGeometry就不能搭配,原因是后者是垂直于地表的。
即使外观与几何图形兼容,它们还必须有匹配的顶点格式(vertex formats)—— 即几何图形必须具有外观可以作为输入的数据格式,在创建Geometry时可以提供VertexFormat。
为了简便,可以让Geometry计算所有顶点属性(vertex attributes),以使之适用于任何外观,但这样做效率较差:

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

而如果我们使用外观EllipsoidSurfaceAppearance,其实只需要知道位置:

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

大部分外观具有vertexFormat属性或者VERTEX_FORMAT 静态常量,创建形状时只需要使用这些顶点格式即可:


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,才能被合并到一个Primitive中。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容