zoukankan      html  css  js  c++  java
  • 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级本科毕业生杨建顺学长,本文的成文仅用于个人学习和研究,在此对原著者表示感谢。

  • 相关阅读:
    java io系列23之 BufferedReader(字符缓冲输入流)
    java io系列22之 FileReader和FileWriter
    java io系列21之 InputStreamReader和OutputStreamWriter
    java io系列20之 PipedReader和PipedWriter
    java io系列19之 CharArrayWriter(字符数组输出流)
    java io系列18之 CharArrayReader(字符数组输入流)
    java io系列17之 System.out.println("hello world")原理
    java io系列16之 PrintStream(打印输出流)详解
    java io系列15之 DataOutputStream(数据输出流)的认知、源码和示例
    java io系列14之 DataInputStream(数据输入流)的认知、源码和示例
  • 原文地址:https://www.cnblogs.com/rainbow70626/p/5294095.html
Copyright © 2011-2022 走看看