Graphics Tech in Cesium - Vertex Compression | cesium.comhtml
計算機圖形學中一種常見的做法是打包、壓縮頂點屬性。它增長了頂點着色器中的代碼,達到減少內存佔用的效果,同時也減小了數據經過總線從CPU到GPU的時間,下降了GPU內存帶寬。函數
另外一個好處是,能夠存放超過頂點屬性的最大數量的其餘額外頂點屬性。學習
減小頂點屬性的一種方法是將全部的屬性成四維向量,並確保全部的份量充分獲得使用。例如,代替以下頂點屬性:優化
attribute vec3 axis; attribute float rotation;
能夠用四維向量來代替:ui
attribute vec4 axisAndRotation; // 使用它 vec3 axis = axisAndRotation.xyz; float rotation = axisAndRotation.w;
經過將多個頂點屬性打包到單個浮點數中,能夠進一步操做。舉個例子,爲何在一個無符號的 byte 中存儲一個 bool 類型的頂點屬性?顯然,bool 類型的只須要 1bit(1byte能存8bit)。做者但願 WebGL 2.0 中會有 GLSL 逐位運算符,不過在此以前,能夠用乘除2的次方的方法來移位操做。編碼
一個 32 位浮點數有 24 位精度,因此,能夠將 24 位打包成一個浮點數。以 Cesium 中的 Billboard 的屬性打包來舉例,首先:code
var UPPER_BOUND = 32768.0; // 2^15 var LEFT_SHIFT7 = 128.0; var LEFT_SHIFT5 = 32.0; var LEFT_SHIFT3 = 8.0; var LEFT_SHIFT2 = 4.0; // value 小於min返回min,大於max返回max,不然返回value function clamp(value, min, max) { return value < min ? min : value > max ? max : value; } // pixelOffset 是屏幕空間的像素偏移量。假設它很短。 // 開始,將它限制在 ±2^15 範圍內。 var compressed = CesiumMath.clamp(pixelOffsetX, -UPPER_BOUND, UPPER_BOUND); // 而後加上 UPPER_BOUND,讓它回到範圍 [0, 2^16] 內 compressed = Math.floor(compressed + UPPER_BOUND); // 左移,以即可以使用剩餘的位 // [譯者注] js 的左移貌似不是這樣的,並且左移 128 位應該是不對的,應該是左移 7 位(2^7=128)? // [譯者注] 經查應該是 compressd << 7; compressed = * LEFT_SHIFT7; // 水平的對齊方式(horizontalOrigin)能夠是 中央、左邊、右邊,那麼用2bit就能夠表示。 // 整數範圍取值就是 [-1, 1],加一個1到 [0, 2],而後左移5位 // [譯者注] horizontalOrigin 應該是一個表達 billboard 對齊點位的方式,一共三種,用 -一、0、1能夠表示,加1就是 0、一、2,用二進制的 // 00、0一、10 徹底能夠表示,只佔 2bit,因此在上面移動了7位以後的7個bit中佔2bit便可,即向左移動5bit // [譯者注] 假設 horizontalOrigin 是 2,那麼二進制就是 0000 0010,由於前面左移空出7個bit,因此把頂頭這個1移動5個bit便可: // 0000 0010 -> 0100 0000 // [譯者注] 因此這裏這個乘號我也不知道他是否是寫錯代碼了 compressed += (horizontalOrigin + 1.0) * LEFT_SHIFT5; // 垂直方向的同理 compressed += (verticalOrigin + 1.0) * LEFT_SHIFT3; // 是否顯示是一個 bool 值,只用1bit就夠了,恰好用完剩下的1bit compressed += (show ? 1.0 : 0.0) * LEFT_SHIFT2;
只須要在 GLSL 中反過來操做就能夠獲取值,見 BillboardCollectionVS.glsl
中的頂點着色器。orm
有關更多的頂點壓縮信息,見參考。htm
譯者注blog
這很坑啊,js代碼裏的位運算貌似寫錯了。不過大致意思我是讀懂了,根據特定場合優化數據的存儲,從二進制入手。
只須要進行位運算,空出一些 bit,將一些自己並不須要很長存儲位的變量塞進去就好了,在 glsl 中進行反操做,就能夠獲取壓縮的值。
官方使用八進制表示法來表示 Cesium 中的單位向量。此表示方法將一個三維單位向量壓縮爲二維向量,這個二維向量的每一個份量用 8 bit 存儲。
參考 AttributeCompression.octEncode
方法來學習如何將單位三維向量轉換成一個八進制編碼的二維向量。
十進制編碼的向量能夠存儲爲二維無符號byte 類型的頂點屬性。若是有更多可用的數據,或許能夠更進一步打包向量。
還能夠打包兩個8bit 份量進一個浮點數,爲其餘數據留出 8bit。參考 AttributeCompression.octEncodeFloat
這個方法來學習編碼過程,參考 czm_octDecode
這個 glsl 函數來解碼。
還能夠將三個單位向量打包成兩個浮點數,參考 AttributeCompression.octPack
方法來學習如何編碼,參考 czm_octDecode
這個 glsl 函數來解碼。
將三個單位向量編碼爲兩個浮點數僅對不相關的單位向量有用,例如,切線向量僅須要編碼兩個向量,可使用其餘兩個的叉積做爲第三個向量。
譯者注
主要是各類縮縮縮,摳1bit是1bit,知道是單位向量的前提下能夠儘量減小bit的佔用,並且知道關係的話,能夠用數學計算完成表達的,就減小一個數據的存儲也是不錯的,例如這裏提到的叉積。
對於 Cesium 的 Billboard 生成的幾何,它的紋理座標並不須要用完浮點數的全24位精度,只須要12位精度便可。
紋理重複並不須要,每一個紋理座標嚴格地限制在 0 到1之間。參考 AttributeCompression.compressTextureCoordinates
方法,它將紋理座標壓縮成一個浮點數。
在 Cesium 中,這麼壓縮可能會致使 billboard 失真,可是截至發文時,官方還沒看到失真的效果。
Primitive
類有一個 compressVertices
屬性,默認值是 true,這意味着 切線空間向量、紋理座標將會被壓縮。
BillboardCollection
和 LabelCollection
的每一個頂點具備18個屬性,每一個屬性又是不一樣的類型和維度。壓縮和打包後,每一個頂點的屬性被壓縮成 8 個四維浮點向量。
參考 BillboardCollection
和它的頂點着色器(BillboardCollectionVS.glsl)。
譯者小結
經過預判各類屬性的狀況,充分利用位運算和邏輯關係,能夠將數據安插在有限的二進制位中,壓縮的工做交給數據生產者和JavaScript。
隨後由頂點着色器完成逆向解析數據的過程,充分利用GPU的計算能力。
[Calver02] Dean Calver. Vertex Decompression in a Shader. In Direct3D ShaderX: Vertex and Pixel Shader Tips and Tricks. Edited by Wolfgang F. Engel. 2002.
[Cigolle14] Cigolle, Donow, Evangelakos, Mara, McGuire, Meyer, A Survey of Efficient Representations for Independent Unit Vectors, Journal of Computer Graphics Techniques (JCGT), vol. 3, no. 2, 1-30, 2014.
[Persson12] Emil Persson. Creating Vast Game Worlds. 2012.
[Pranckevičius09] Aras Pranckevičius. Compact Normal Storage for Small G-Buffers. 2009.