美文网首页开源
Cesium中Entity的渲染流程草图

Cesium中Entity的渲染流程草图

作者: jb3d | 来源:发表于2021-04-09 16:10 被阅读0次

    1.cesium关系草图  

    Viewer

        DataSourceDisplay

            DataSourceCollection

            CustomDataSource

                EntityCollection

                    Entity

    2.

    Viewer.entities.add({

        rectangle : {

            coordinates : Cesium.Rectangle.fromDegrees(-92.0, 20.0, -86.0, 27.0),

            outline : true,

            outlineColor : Cesium.Color.WHITE,

            outlineWidth : 4,

            stRotation : Cesium.Math.toRadians(45),

      }

    });

    当我们调用EntityCollection.add方法时,根据参数会new一个Entity对象,此时EntityCollection充当了一个容器的作用。接着,Cesium内部通过事件的机制,在DataSourceDisplay中根据Entity类型安排具体模块,最终该模块完成对应Entity的解析工作。

    3.渲染前DataSourceDisplay就拿到所有的Visualizer(可视化生产器  理解成模子) 

    这里添加的是Rectangle所以用到的是GeometryVisualizer:

      DataSourceDisplay.defaultVisualizersCallback =function(scene, entityCluster, dataSource) {

        varentities = dataSource.entities;

        return[

    。。。。。
     new GeometryVisualizer(RectangleGeometryUpdater, scene, entities),

    。。。。。

    ];

    };

    把渲染工作交接给GeometryVisualizer的_onCollectionChanged函数:

    entityCollection.collectionChanged.addEventListener(GeometryVisualizer.prototype._onCollectionChanged,this)

    加入到对应到队列中排队(这里的队列是_addedEntities)  针对EntityCollection的每一种队列,Visualizer分别提供了_addedObjects,_removedObjects,_changedObjects三个队列一一对接。

    EntityCollection.prototype.add =function(entity) {

        if(!(entityinstanceof Entity)) {

            entity =new Entity(entity);

        }

        varid = entity.id;

        if(!this._removedEntities.remove(id)) {

            this._addedEntities.set(id, entity);

        }

        fireChangedEvent(this);

        return entity;

    };

    总结:DataSourceDisplay初始化的时候会调用defaultVisualizersCallback,会针对所有Geometry的Type创建对应的Visualizer;EntityCollection.Add每次添加一个Entity,会通过一系列事件传递,将该Entity传递到每一个Visualizer,保存到Visualizer中_addedObjects队列中。

    Viewer初始化时会绑定clock.onTick事件,确保每一帧都会调用。而其内部则调用DataSourceDisplay.update,进而遍历所有的Visualizer,调用其update方法

    GeometryVisualizer.prototype.update做了三件事:

    1.new Updater

    function RectangleGeometryUpdater(entity, scene) {

        this._options =new GeometryOptions(entity);

        this._onEntityPropertyChanged(entity, 'rectangle', entity.rectangle, undefined);

    }

    每一个GeometryVisualizer都绑定一个具体的Updater,用来解析Entity,以Rectangle为例// Rectangle则由对应的RectangleGeometryUpdater来解析// 通过new Updater,将Entity对应的RectangleGraphics解析为RectangleGeometryUpdater的GeometryOptionsupdater =newthis._type(entity,this._scene);

            this._updaters.set(id, updater);

            // 根据该RectangleGeometryUpdater的材质风格创建对应的GeometryInstance,分到对应的批次队列中// 每一个批次队列中的Geometry风格相同,因此可以通过一次DrawCommand渲染该队列中所有Geometry// 目的是减少渲染次数,提高渲染效率

    2.insertUpdaterIntoBatch

    不准确的说(但有助于理解),GeometryOptions主要对应RectangleGraphics的几何数值,而在insertUpdaterIntoBatch中则根据RectangleGraphics的材质风格进行分组,只有材质一致的RectangleGeometryUpdater才能分到一起,进行后面的批次。比如学校分班,优等生,中等生分到不同的班级,老师根据不同班级的能力进行适当的区分,就是一种通过分组的方式来优化的思路。打组批次也是同样一个道理。

    3.batch.update

           之前的步骤1和步骤2,我们对当前这一帧中新增的Entity进行解析,构造成对应的GeometryInstance,放到对应的Batch队列中。比如有两个Rectangle类型的Entity,假设他们的风格一样,都是纯色的,当然颜色可能不相同,但最终都是在一个批次队列(StaticOutlineGeometryBatch)。接下来,1每一个批次队列会构建一个Primitive,包括该队列中所有的GeometryInstances,因为显卡强大的并行能力,绘制一个三角面和绘制N个三角面的所需的时间是一样的(N取决于顶点数),2所以尽可能的将多个Geometry封装成一个VBO是提高渲染性能的一个关键思路(批次&实例化)。而这个batch.update完成的前半部分,而Primitive.update则完成了最后,也是最关键的一步。

    总结上面大致流程:

    DataSourceDisplay.prototype.update

        GeometryVisualizer.prototype.update

            updater =newthis._type(entity,this._scene);

                new GeometryOptions(entity);

                _onEntityPropertyChanged()

            insertUpdaterIntoBatch

                StaticGeometryColorBatch.prototype.add

                    RectangleGeometryUpdater.prototype.createFillGeometryInstance

                        new GeometryInstance()

                    Batch.prototype.add

            batches[i].update(time)

                StaticGeometryColorBatch.prototype.update

                    Batch.prototype.update

                        new Primitive()

                        primitives.add(primitive);

    Primitive.prototype.update

    this._scene.render(currentTime);

        Scene.prototype.render

            function render(scene, time)

                function updateAndExecuteCommands()

                    function executeCommandsInViewport()

                        function updatePrimitives()

                            PrimitiveCollection.prototype.update()

                                for (var i = 0; i < primitives.length; ++i) {

                                    primitives[i].update(frameState);

                                }

    loadAsynchronous(多线程创建vbo),createVertexArray以及create*这几个内容

    createVertexArray

    上面的Geometry已经将数据处理为indexBuffer和vertexBuffer,下面则需要将该数据结合attributes创建为vbo&vao,这个过程就是通过createVertexArray完成

    createRS

    创建RenderState

    createSP

    创建ShaderProgram

           很明显,渲染主要是数据+风格,当我们满足了geometry的数据部分已经符合WebGL渲染的格式后,结合appearance封装的材质,设置对应的RenderState以及Shader和所需要的参数。最后,我们构造出最终的DrawCommand,添加到DrawCommandList中,完成最终的渲染

    相关文章

      网友评论

        本文标题:Cesium中Entity的渲染流程草图

        本文链接:https://www.haomeiwen.com/subject/cnnikltx.html