Cesium 視域分析
好玩的東西大家都喜歡,但好玩又難嚼的東西就總讓人心癢癢。視域分析應該說是GIS空間分析里比較經典又實用很廣的功能,理論起來很簡單,就是看得見與看不見的區分,上面實現的功能看得見綠色表示,看不見紅色表示,對於工程師而已就是0與1的區分。
那么怎么來實現呢?在群里多次討論過,為尋方便,就放這里了。
首先要明確一點,實現視域分析思路並不難,關鍵在於細節處理。記住這點,你就能實現它!
僅僅利用Cesium自帶的API來懟出來是不太切合實際的,看過有人畫面來貼建築,示意線也畫出來了,但真的好無趣,最后只能是浪費大量時間。我們地走正路,從圖形學的角度出發,實現視域分析大方向可以通過模板和深度來實現,可能有的人眼前一亮,0與1之間的區分,模板不是最直接的選擇嗎,但從筆者實踐來看,模板在Cesium里實現起來可沒有深度那么來得方便,綜合最終效果而言,個人也比較推薦用深度來實現比較好。
好了,大政方針確定了,開始實施。
我們視域分析的結果,不管可見與不可見,最終像素它都是在所分析對應的觀察視角的CVV里,之外的像素是不用關心的,因此先將相機移到我們的觀察視角,正常獲取一張深度圖(至於如何在Cesium里獲取到深度圖可參照另外一篇文章:Cesium渲染流程https://www.cnblogs.com/GISCesium/p/10420492.html)。在和正常場景渲染的深度圖做對比,即可確定那些像素對於觀察視角可見,那些不可見。
也可以自己獲取深度圖,我們需要自己先創建一個Framebuffer來承載我們的深度信息,在Cesium里就是放在一個紋理對象中來管理如下:
var framebuffer = new Framebuffer({
context : context,
depthStencilTexture : new Texture({
context : context,
width : context.drawingBufferWidth,
height : context.drawingBufferHeight,
pixelFormat : PixelFormat.DEPTH_STENCIL,
pixelDatatype : PixelDatatype.UNSIGNED_INT_24_8
})
});
具體參數就不過多介紹了,此時此刻,我們擁有了一個正常批次渲染中的“一幀”,再綁定到PassState上即可獲取到了:
scene.getDepth(clearCommand, PassState, secondCamera);
pickDepth.update(context, PassState.framebuffer.depthStencilTexture);
pickDepth.executeCopyDepth(context, PassState);
建議是通過自定義的方式獲取,這樣比較靈活,磨刀不誤砍柴工,我一開始就自己封裝好了不同階段深度信息的獲取類,到后面才真正體驗到那叫一個方便啊。
原始場景的深度信息獲取就更簡單了,方式也多樣,比如可以直接通過屏幕坐標反算得出:
.......
var drawingBufferPosition = SceneTransforms.transformWindowToDrawingBuffer(this._scene, windowCoordinate, new Cartesian2());
drawingBufferPosition.y = this._scene.drawingBufferHeight - drawingBufferPosition.y;
pickDepth.getDepth(context, drawingBufferPosition.x, drawingBufferPosition.y);
......
當然最偷懶的方式通過相機矩陣,利用向量變換也可以還原出實際深度值。
可能我們還是覺得這樣一通操作,還是麻煩了,之前也說過,利用Cesium自帶的ShadowMap也可以實現視域分析,這種方式確實在Cesium基礎之上改一改就完事。對於要加強Cesium源碼掌控的不建議這樣做,但為了盡快出效果可以先試試這種方式。基於ShadowMap的思路比較簡單,這里就簡明扼要的記錄一下。
Cesium以Command的方式周而復始地實時刷新場景的樣子,讓我們看到一個個三維場景,而有的命令具備這樣的特征:一方面它具有通用特性,甚至是影響全局的;另外一方面它並不是每一幀都需要的。針對這些命令把他們放在derivedCommands的計算流程里,根據實際情況進行必要的計算。
在updateDerivedCommands函數里我們看得就比較清晰:
這里可謂分工明確,其中就有我們關心的陰影生成相關系的shadows分支命令的計算。這里需要強調一下,Scene場景下用於陰影分支計算的是單獨的ShadowMap,而非自己實例化的ShadowMap,這里處理
不好,會影響后面視域分析結果與原生陰影之間的關系,甚至沖突。
至於Shader,直接看ShadowMap這個類就行了,判斷一下那些在陰影區,那些不在,判斷的方法Cesium也提供了直接的方法,我們唯一要做的就是添加指定顏色uniform變量以及其它調控參數即可,上圖並
不是唯一的效果,你也可以改出這樣的的效果:


因為采用了傳統透視投影的視錐體示意,完全可以根據弧度自己畫示意線即可,這都不是什么核心問題了。

總結一下,利用Cesium原生的技術即可完成視域分析功能這條路確實行得簡單脫俗,但這里面隱藏了很多值得去深挖的問題,一個牽扯一個,環環相扣;再改造Cesium源碼的過程中很多表面上莫名其妙
的報錯往往是因為沒有看清源碼意圖導致的,所以有時間還是實戰一把對技術提升必將大有裨益。開源如此炫麗,但問題的根本不在於誰在用開源,而在於誰利用得好。
祝願周末沒事,技術繼續分享交流,群685834990