• WorldWind源码剖析系列:WorldWind如何确定与视点相关的地形数据的LOD层级与范围


    1WorldWind如何确定与视点相关的地形数据的LOD层级与范围?

    问题描述:WW中是如何判断LOD层次的呢,即在什么情况下获得哪一层级的数据?是否只通过相机视点的高度进行判断?

    问题切入:要解决这个问题,我先说明一下WW的渲染机制,在渲染线程中,Render函数只负责渲染可渲染物体,而不负责视点的更新和Lod的判断。在m_World.Render(this.drawArgs)中可渲染的物体都是通过另一个更新线程WorkerThreadFunc来控制的,具体由m_World.Update(this.drawArgs)实现。因此要找到这个问题的答案需要从Update着手。重点注意m_World.Update(this.drawArgs)中的两段代码。如下所示:

    可渲染物体的递归更新:

           if (this.RenderableObjects != null)
                {
                    this.RenderableObjects.Update(drawArgs);
                }
                if (this.m_WorldSurfaceRenderer != null)
                {
                    this.m_WorldSurfaceRenderer.Update(drawArgs);
                }
                if (this.m_projectedVectorRenderer != null)
                {
                    this.m_projectedVectorRenderer.Update(drawArgs);
                }

    相机视点的高度更新:

           if (this.TerrainAccessor != null)
                {
                    if (drawArgs.WorldCamera.Altitude < 300000)
                    {
                        if (System.DateTime.Now - this.lastElevationUpdate > TimeSpan.FromMilliseconds(500))
                        {
                            drawArgs.WorldCamera.TerrainElevation = (short)this.TerrainAccessor.GetElevationAt(drawArgs.WorldCamera.Latitude.Degrees, drawArgs.WorldCamera.Longitude.Degrees, 100.0 / drawArgs.WorldCamera.ViewRange.Degrees);
                            this.lastElevationUpdate = System.DateTime.Now;
                        }
                    }
                    else
                        drawArgs.WorldCamera.TerrainElevation = 0;
                }
                else
                {
                    drawArgs.WorldCamera.TerrainElevation = 0;
                }

    上面代码是WorldWind V1.4.0.0中实现的。但是在WorldWind  V1.4.0.1中被移入worldwindow.cs文件的WorldWindow.Render()函数中。如下图选中的代码。

    问题答案:搜索全局Update,重点关注SurfaceTile的Update和QuadTile的Update。

    SurfaceTile的Update:

             if(drawArgs.WorldCamera.TrueViewRange < Angle.FromDegrees(3.0f*tileSize) && MathEngine.SphericalDistance( Angle.FromDegrees(centerLatitude), Angle.FromDegrees(centerLongitude), drawArgs.WorldCamera.Latitude, drawArgs.WorldCamera.Longitude) < Angle.FromDegrees( 2.9f*tileSize )& drawArgs.WorldCamera.ViewFrustum.Intersects(m_BoundingBox))
                    {
                        if(m_NorthWestChild == null || m_NorthEastChild == null || m_SouthWestChild == null || m_SouthEastChild == null)
                        {
                            ComputeChildrenTiles(drawArgs);
                        }
                        else
                        {
                            if(m_NorthEastChild != null)
                            {
                                m_NorthEastChild.Update(drawArgs);
                            }
                            if(m_NorthWestChild != null)
                            {
                                m_NorthWestChild.Update(drawArgs);
                            }
                            if(m_SouthEastChild != null)
                            {
                                m_SouthEastChild.Update(drawArgs);
                            }
                            if(m_SouthWestChild != null)
                            {
                                m_SouthWestChild.Update(drawArgs);
                            }
                        }            
                    }
                    else 
                    {
                        if(m_NorthWestChild != null)
                        {
                            m_NorthWestChild.Dispose();
                            m_NorthWestChild = null;
                        }
    
                        if(m_NorthEastChild != null)
                        {
                            m_NorthEastChild.Dispose();
                            m_NorthEastChild = null;
                        }
    
                        if(m_SouthEastChild != null)
                        {
                            m_SouthEastChild.Dispose();
                            m_SouthEastChild = null;
                        }
    
                        if(m_SouthWestChild != null)
                        {
                            m_SouthWestChild.Dispose();
                            m_SouthWestChild = null;
                        }
                    }

    QuadTile的Update:

               if (DrawArgs.Camera.ViewRange < Angle.FromDegrees(QuadTileSet.TileDrawDistance * tileSize)&& MathEngine.SphericalDistance(CenterLatitude, CenterLongitude,DrawArgs.Camera.Latitude, DrawArgs.Camera.Longitude) < Angle.FromDegrees(QuadTileSet.TileDrawSpread * tileSize)&& DrawArgs.Camera.ViewFrustum.Intersects(BoundingBox))
                        {
                            if (northEastChild == null || northWestChild == null || southEastChild == null || southWestChild == null)
                            {
                                ComputeChildren(drawArgs);
                            }
    
                            if (northEastChild != null)
                            {
                                northEastChild.Update(drawArgs);
                            }
    
                            if (northWestChild != null)
                            {
                                northWestChild.Update(drawArgs);
                            }
    
                            if (southEastChild != null)
                            {
                                southEastChild.Update(drawArgs);
                            }
    
                            if (southWestChild != null)
                            {
                                southWestChild.Update(drawArgs);
                            }
                        }
                        else
                        {
                            if (northWestChild != null)
                            {
                                northWestChild.Dispose();
                                northWestChild = null;
                            }
    
                            if (northEastChild != null)
                            {
                                northEastChild.Dispose();
                                northEastChild = null;
                            }
    
                            if (southEastChild != null)
                            {
                                southEastChild.Dispose();
                                southEastChild = null;
                            }
    
                            if (southWestChild != null)
                            {
                                southWestChild.Dispose();
                                southWestChild = null;
                            }
                        }

    在这里我们看到判断Lod的级别主要有三个条件:

    1、相机视角范围,视角范围越大,所包含的tileSize就越大;

    2、相机与瓦片距离,距离越远,所包含的tileSize也就越大;

    3、相机视锥与瓦片是否相交。

    相对应我们可以把视角剔除方法理解成以下三步:

    1、根据视角范围,画个大圈,把大圈里的大瓦片全加进来;

    2、根据相机与瓦片的距离进行细化瓦片,如果太远的瓦片仍然保持很大,而近处的瓦片需要进一步更新,计算子瓦片;

    3、每计算一个瓦片都需要判断它是否在视锥里,否则剔除身后看不到的瓦片。

    在理解以上三个条件的前提下,需要理解的是视角、距离到底与tileSize有什么关系。对于不同级别影像或地形,ww中设置了相应的大小,根据一定的比例系数和经验参数寻找与距离和视角的关系。即通过调整参数 可控制在怎样的距离下可以看到怎样级别的地形或影像。例如在地形中设置为3.0、2.9等常数,在影像中设置为TileDrawDistance和TileDrawSpread等常数。只是反映了一定的经验参数而已。

     

    2WorldWind中对地块接边的处理是有裂缝的,采用裙摆设置的原理和方法实现是什么?

     问题描述:对于不同级别的地形要进行接边是个很重要的问题,这个问题在我推荐的文献《全球多分辨率虚拟地形环境关键技术的研究》(作者杜莹2005博士)中有详细说明,而WW只是选择了最简单最原始的接地处理,也就是常说的裙摆处理。这种处理方法有很多的不利之处,但是实现方法简便,在此做简单说明。

     问题切入:要想追踪这个问题,首先想到的是QuadTile的Update,而不从Render着手。Update就像在炒菜前把所有材料都准备的工作,如果没准备好就不能炒。在QuadTile的Update中,从CreateTileMesh切入,发现了CreateElevatedMesh和CreateFlatMesh的区别,显然裙摆是针对地形网格而做的处理在CreateElevatedMesh再深入到子函数,其中有一段很重要的说明:

    /// <summary>
            /// Builds flat or terrain mesh for current tile//为当前瓦片创建平坦或高程格网
            /// </summary>
            public virtual void CreateTileMesh()
            {
                verticalExaggeration = World.Settings.VerticalExaggeration;
                m_CurrentOpacity = QuadTileSet.Opacity;
                renderStruts = QuadTileSet.RenderStruts;
    
                if (QuadTileSet.TerrainMapped && Math.Abs(verticalExaggeration) > 1e-3)
                    CreateElevatedMesh();
                else
                    CreateFlatMesh();
            }

    CreateElevatedMesh再深入到子函数,其中有一段很重要的说明:

    /// <summary>
            /// Build the elevated terrain mesh
            /// </summary>
            protected virtual void CreateElevatedMesh()
            {
                isDownloadingTerrain = true;
                // Get height data with one extra sample around the tile
                double degreePerSample = LatitudeSpan / vertexCountElevated;
                TerrainTile tile = QuadTileSet.World.TerrainAccessor.GetElevationArray(North + degreePerSample, South - degreePerSample, West - degreePerSample, East + degreePerSample, vertexCountElevated + 3);
                float[,] heightData = tile.ElevationData;
    
                int vertexCountElevatedPlus3 = vertexCountElevated / 2 + 3;
                int totalVertexCount = vertexCountElevatedPlus3 * vertexCountElevatedPlus3;
                northWestVertices = new CustomVertex.PositionNormalTextured[totalVertexCount];
                southWestVertices = new CustomVertex.PositionNormalTextured[totalVertexCount];
                northEastVertices = new CustomVertex.PositionNormalTextured[totalVertexCount];
                southEastVertices = new CustomVertex.PositionNormalTextured[totalVertexCount];
                double layerRadius = (double)QuadTileSet.LayerRadius;
    
                // Calculate mesh base radius (bottom vertices)
                // Find minimum elevation to account for possible bathymetry
                float minimumElevation = float.MaxValue;
                float maximumElevation = float.MinValue;
                foreach (float height in heightData)
                {
                    if (height < minimumElevation)
                        minimumElevation = height;
                    if (height > maximumElevation)
                        maximumElevation = height;
                }
                minimumElevation *= verticalExaggeration;
                maximumElevation *= verticalExaggeration;
    
                if (minimumElevation > maximumElevation)
                {
                    // Compensate for negative vertical exaggeration
                    minimumElevation = maximumElevation;
                    maximumElevation = minimumElevation;
                }
    
                double overlap = 500 * verticalExaggeration; // 500m high tiles
    
                // Radius of mesh bottom grid
                meshBaseRadius = layerRadius + minimumElevation - overlap;
    
                CreateElevatedMesh(ChildLocation.NorthWest, northWestVertices, meshBaseRadius, heightData);
                CreateElevatedMesh(ChildLocation.SouthWest, southWestVertices, meshBaseRadius, heightData);
                CreateElevatedMesh(ChildLocation.NorthEast, northEastVertices, meshBaseRadius, heightData);
                CreateElevatedMesh(ChildLocation.SouthEast, southEastVertices, meshBaseRadius, heightData);
    
                BoundingBox = new BoundingBox((float)South, (float)North, (float)West, (float)East, (float)layerRadius, (float)layerRadius + 10000 * this.verticalExaggeration);
    
                QuadTileSet.IsDownloadingElevation = false;
    
                // Build common set of indexes for the 4 child meshes    
                int vertexCountElevatedPlus2 = vertexCountElevated / 2 + 2;
                vertexIndexes = new short[2 * vertexCountElevatedPlus2 * vertexCountElevatedPlus2 * 3];
    
                int elevated_idx = 0;
                for (int i = 0; i < vertexCountElevatedPlus2; i++)
                {
                    for (int j = 0; j < vertexCountElevatedPlus2; j++)
                    {
                        vertexIndexes[elevated_idx++] = (short)(i * vertexCountElevatedPlus3 + j);
                        vertexIndexes[elevated_idx++] = (short)((i + 1) * vertexCountElevatedPlus3 + j);
                        vertexIndexes[elevated_idx++] = (short)(i * vertexCountElevatedPlus3 + j + 1);
    
                        vertexIndexes[elevated_idx++] = (short)(i * vertexCountElevatedPlus3 + j + 1);
                        vertexIndexes[elevated_idx++] = (short)((i + 1) * vertexCountElevatedPlus3 + j);
                        vertexIndexes[elevated_idx++] = (short)((i + 1) * vertexCountElevatedPlus3 + j + 1);
                    }
                }
    
                calculate_normals(ref northWestVertices, vertexIndexes);
                calculate_normals(ref southWestVertices, vertexIndexes);
                calculate_normals(ref northEastVertices, vertexIndexes);
                calculate_normals(ref southEastVertices, vertexIndexes);
    
                isDownloadingTerrain = false;
            }

    这段注释的意思是说在:创建地形mesh的时候,在网格点的四周围多加一个点用于之后法向量的计算,而使用这种柱状顶点的效果将在法向量计算完后再折叠起来。这就是所谓的裙摆效果。在接下来的calculate_normals中,一眼就能看出折叠函数ProjectOnMeshBase,其具体实现如下:

    // Project an elevated mesh point to the mesh base
            private Point3d ProjectOnMeshBase(Point3d p)
            {
                p = p + this.localOrigin;
                p = p.normalize();
                p = p * meshBaseRadius - this.localOrigin;
                return p;
            }

                                   

      声明:本文的原创著作权利属于武汉大学GIS(地理信息系统)专业2009级本科毕业生杨建顺学长,本文的成文仅用于个人学习和研究,在此对原著者表示感谢。

  • 相关阅读:
    boost::asio::error的用法浅析
    boost::asio::buffer
    sqlserver2008安装图解
    WCF 聊天室程序代码详细讲解教程
    C#中使用Property Grid(属性面板)控件
    TXT>Access 使用DAO数据源!(VB Code)
    读取INI文件 VbCode
    Pet Shop 4
    模式行为型
    C#编程规范
  • 原文地址:https://www.cnblogs.com/rainbow70626/p/5294095.html
Copyright © 2020-2023  润新知