【Cesium 歷史博客】Cesium 中的圖形技術:渲染體系結構

翻譯有誤請指出,規範性轉載。@秋意正寒。api

原文出處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年)。ui

渲染器長啥樣?

在 Cesium 1.9 中,渲染器的主要組件(js對象)是:編碼

VertexArray、RenderState、ShaderProgram、FrameBufferprototype

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.

相關文章
相關標籤/搜索