天天看點

WorldWind源碼剖析系列:經緯度格網類LatLongGrid

經緯度格網類LatLongGrid繼承自可渲染對象類RenderableObject,是WorldWind中用來在星球外表繪制經緯度格網的封裝類。其類圖如下所示。

WorldWind源碼剖析系列:經緯度格網類LatLongGrid
繪制經緯網格的主體函數為Render(),其内部主要調用以下函數完成繪制:

ComputeGridValues()//計算格網值

RenderTropicLine()//繪制回歸線      

計算格網值ComputeGridValues()内部通過相機的真實視場角drawArgs.WorldCamera.TrueViewRange.Radians來指定經緯網格的緯度分割間隔為1、2、5、10四類,然後指定經度分割間隔等于緯度間隔。接下來,判斷目前相機的視景體是否包含南北極點,如果包含,則将指定經度分割間隔等于10度,進而使極點部分的經緯網格分割間隔比較大,而不用繪制的非常密集。接着,計算相機可視場景的最大/小可見經度值,最大/小可見緯度值,均取整型。且當經度分割間隔等于10度時,最小可見經度等于-180度,最大可見經度等于180度。接着,計算沿經度方向分割時每一行分割點數,沿緯度方向分割時每一列分割點數,均比分割出來的單元格行數、列數大1,這和我先前一篇部落格文章《基于DirectX的半球形天空類的C++和C#實作》中的做法是一樣的,這樣做都是為了使球體首尾互相銜接。然後求出經度點數(行向)和緯度點數(列向)之間的較大值,然後利用該值來申請星球體劃分時所需要的存儲的三維坐标點空間:

if (lineVertices == null || vertexPointCount > lineVertices.Length)

  lineVertices = new CustomVertex.PositionColored[Math.Max(LatitudePointCount, LongitudePointCount)];      

接着,設定星球的經緯網格所在的球體半徑為為構造該類對象時傳入的星球對象World所攜帶的半徑值。 接下來要判斷目前相機的高度是否小于0.1*WorldRadius;如果是則不開啟Zbuffer深度測試。如果否則開啟Zbuffer深度測試,且設定1.01* WorldRadius和(WorldRadius + 0.015f * drawArgs.WorldCamera.Altitude)兩個值之間的較大者,最終為星球的經緯網格所在的球體半徑。

接下來,就按照DirectX 3D的固定圖形渲染管線設定是否開啟Zbuffer深度測試、設定紋理階段顔色操作ColorOperation、設定頂點格式VertexFormat、設定世界變換矩陣drawArgs.device.Transform.World、設定關閉渲染狀态光照效果drawArgs.device.RenderState.Lighting。經緯網格每一個交點都是通過将格網點的球面坐标轉換為笛卡爾控件直角坐标得到的,通過下面的公式将網格定點的球面坐标轉換為空間直角坐标來獲得逼近的半球面格網剖分模型。

WorldWind源碼剖析系列:經緯度格網類LatLongGrid

 其中,其中B和L分别為球體的經度和緯度,取值範圍為:-PI/2≤B≤PI/2,-PI≤L≤PI,R為設定的球體半徑。可以使用、、、坐标來生成所需要的球面格網。根據不同的精度要求,可對緯向和經向剖分間隔設定不同的值。

紋理坐标可以首先根據緯向和經向剖分間隔分别計算出緯向和經向剖分網格點數,然後根據目前球面坐标計算出格網點所在的緯向和經向網格行列号,最後根據下面公式計算出雲彩貼圖的紋理坐标。其中,先将目前行号和列号強制取浮點型是為了保證除法運算按浮點型運算,使最終結果落在[0.0,1.0]之間。詳細代碼請參照我先前的一篇部落格文章《基于DirectX的半球形天空類的C++和C#實作》。