翻譯有誤請指出,規范性轉載,@秋意正寒,
原文出處Graphics Tech in Cesium - Renderer Architecture | cesium.com
Cesium 是一個 WebGL 引擎,自 WebGL 1.0 在 2011 年 3 月發布后,官方就開始開發了,
官方將 Cesium 的 Renderer 視為他們自己的第四代渲染器,因為它基于他們的 OpenGlobe 的經驗改進而來,除此之外,還有其他技術人員在 AGI 的 Insight 3D 和 STK 的經驗,所以說,Cesium 的渲染器并不是憑空設計而來,
為什么需要一個渲染器?
當然,可以把 WebGL 的調度分散在各處,但是集中在一個渲染器物件中有很多好處:
- 便于使用:渲染器提供了對 WebGL 的高級抽象,做了一層封裝后,Cesium 的其他部分用起來就簡單了,且不容易出問題,
- 著色器流水線:Cesium 需要用到著色器流水線,這集成在渲染器中了,
- 性能:快取、最小化 WebGL 的呼叫全部集中在渲染器內,使得 Cesium 在其他地方呼叫的時候能獲得不錯的性能,
- 狀態:WebGL 是一個狀態機器,渲染器就幫助 Cesium 在管理這些狀態,
- 可移植性:渲染器添加 WebGL 擴展、升級到 WebGL 2,或在特定的平臺上作業是容易實作的,
為什么要自己動手?
推出 Cesium 的渲染器的主要原因是官方有豐富的經驗,并且可以根據實際情況調整 Cesium 引擎以獲得最佳性能,
Cesium 的著色器流水線遠遠超出了普通圖形程式的概念范圍,
還有一個原因是因為,官方推出這個其實是想通過此學習 JavaScript,而且 WebGL 在 2011 年的時候并不成熟(發文時是 2015年),
渲染器長啥樣?
在 Cesium 1.9 中,渲染器的主要組件(js物件)是:
VertexArray、RenderState、ShaderProgram、FrameBuffer

Renderer 類在 Source/Renderer 目錄下,Renderer 的代碼并不是公開的 api,所以謹慎使用,
左邊一列的物件構成 Cesium 的繪制命令的基礎,它們封裝了 WebGL 的 drawcall 指令,為了渲染一幀,Cesium 在隨處都會執行這些命令,
- VertexArray(參考 VertexArray.js):頂點資料和索引資料,頂點屬性和索引存盤在
Buffer物件中(參考 Buffer.js),如果可行,頂點陣列使用OES_vertex_array_object的擴展名以減少 WebGL 的呼叫次數(這句不太懂), - RenderState(參考 RenderState.js):包括發出 drawcall 所需的 WebGL 狀態資料,例如深度、混合和狀態等,
- ShaderProgram(參考 ShaderProgram.js):表示已編譯、連接的 WebGLProgram,uniforms 可與 Cesium 的矩陣、笛卡爾變數、紋理、顏色等直接運作,
- Framebuffer(參考 Framebuffer.js):幀快取物件,包括紋理快取、渲染快取的容器,是 drawcall 所需要的素材,
著色器流水線(Shader Pipeline)
著色器保存在 Source/Shader 目錄下的 .glsl 后綴名的檔案中,
Cesium 會洗掉檔案內的注釋、無效空格,并轉換為 js 代碼(字串)以便其他 Cesium 物件能呼叫,而無需重新請求檔案,
Cesium 提供了一個龐大的 GLSL 函式庫,包括函式、結構體、常量,如果你的代碼需要用到自定義 glsl 代碼,你完全可以不宣告、不加入 #include 預編譯指令,可以直接使用它們,
它們以 czm_ 開頭標識,例如,這是一個天空大氣的片元著色器:
czm_ellipsoid ellipsoid = czm_getWgs84EllipsoidEC();
vec3 direction = normalize(v_positionEC);
czm_ray ray = czm_ray(vec3(0.0), direction);
czm_raySegment intersection = czm_rayEllipsoidIntersectionInterval(ray, ellipsoid);
if (!czm_isEmpty(intersection)) {
discard;
}
這些內置物件形成了有向無環圖(DAG),
在運行時,glsl 源代碼將傳遞給 ShaderSource 物件,這個物件查找 czm_ 字串并遍歷 DAG 以生成最終的著色器,
如果在拾取操作時用到了著色器,它還會輸出 pick id,而不是真正的顏色(這句話沒看太懂),
以上均在 Cesium 程式運行的時候完成的,而不是寫死在引擎內部,因為有的繪制直到它要進行繪制時才知道著色器的編碼順序等,
內置的 GLSL uniform 被稱為 自動 uniform,它也不需要宣告或引入 #include,遍歷 DAG 會一樣遍歷到,
自動 uniform 通常代表與幀相關(或視錐體相關、命令相關)的值,例如變換矩陣,見 AutomaticUniforms.js,
例如,天空盒的頂點著色器使用到了自動 uniform 來轉換一個 2x2x2 的立方體的坐標,這個立方體的中心在 True Equator Mean Equinox 框架,轉換到裁剪坐標的中心:
attribute vec3 position;
varying vec3 v_texCoord;
void main()
{
vec3 p = czm_viewRotation * (czm_temeToPseudoFixed * (czm_entireFrustum.y * position));
gl_Position = czm_projection * vec4(p, 1.0);
v_texCoord = position.xyz;
}
使用原始的 GLSL 源代碼作為 key 來快取著色器程式,以減少初始化和使用著色器時的 WebGL 呼叫數,參考 ShaderCache.js,
執行(繪制)命令
熟悉 WebGL 的讀者,應該知道繪制命令的執行是由 WebGLContext 物件的 drawXXX 函式執行的,它被封裝在 Cesium 的 Context.prototype.draw 方法中,它做了這些事:
- 如果與前一個命令不一樣,那么系結幀快取(framebuffer)
- 使當前渲染狀態生效,由于渲染狀態是不可變的,且能快取,因此比較前一個狀態和當前狀態的差異,就能產生一個對已改變部分進行處理的函式進行下一步處理
- 系結著色器程式(當然,如果有需要也會編譯、連接)并設定 uniforms 變數(包括 Cesium 自動的)
- 系結頂點陣列并觸發 drawElements 和 drawArrays
當一幀結束后,Context.prototype.endFrame 方法會解除著色器的 WebGLProgram 的系結、解除對幀快取、繪制快取、紋理的系結以清理狀態,這樣能減少每個繪制命令執行時渲染器的狀態管理量,
Cesium 中的渲染器結構
渲染器將被 Scene 物件給 Primitive 物件來創建 WebGL 資源,例如,Globe 本身和三維模型,并且,渲染器還將執行繪制命令,將這些資源繪制到一幀上,

未來的作業
WebGL 2.0 的出現,需要對渲染器進行很多改進,
Uniform Buffers
與時下很多引擎一樣,設定 uniform 變數是 Cesium 的瓶頸,Uniform buffers 在 WebGL 2 得到了性能上的提高,
Instancing
支持實體的繪制使得 Cesium 能渲染大量物件,例如樹,當然每棵樹可以有不同的屬性,例如位置、高度等,
致謝
感謝 Greg Beatty 和 Scott Hunter,他們撰寫了 glsl 著色器,
參考
[Cozzi11] Patrick Cozzi and Kevin Ring. 3D Engine Design for Virtual Globes. CRC Press. 2011.
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/226492.html
標籤:GIS
