在VR下面,曲面UI能夠提高用戶在場景中的沉浸感,得到更好的視覺體驗html
作一套基於曲面的UI
咱們項目中基本只用到Image和Text兩種,Image是比較好處理的,直接將Texture貼到一個曲面的mesh上就能夠了,可是Text相對比較麻煩。咱們沒法簡單的取到某一段文字的Texture,必須本身從字體文件裏面裁剪每一個文字的Texture,而後拼接到一個Texture,而後再將最終的紋理貼到一個曲面mesh上。緩存
單獨將平面UI渲染到一個紋理,而後貼到曲面mesh上
這個方案在實現曲面化上是沒有問題的,可是爲了知足VR下面的立體效果,咱們兩隻眼睛看到的東西是有必定差別的,經過這些差別纔有立體感,若是是同一個曲面的UI Texture,UI上的一些立體效果會損失(好比UI向前浮動)。另外,若是使用這種方法,咱們眼睛看到的UI和真實的UI有很大的差別,須要從新設計凝視輸入。bash
讓UI沿着曲面分佈,且每一個UI元素有曲面效果
讓UI沿着曲面分佈是比較好實現的,只須要計算合理的位置和角度,讓UI總體上呈現出曲面的效果。目前有不少VR的曲面效果都是採用這種簡單的方法實現的,可是這種方案實現的曲面效果並非很好,是一種假曲面化的效果。以下圖所示,在上下邊緣能夠看到很明顯的直線。
ide
結合實現難度和效果,咱們選擇了方案 3。函數
不管是讓UI沿着曲面分佈,仍是實現每一個UI自身的曲面效果,實質上都是作同一種數學運算——計算平面上的點映射到曲面上的座標。
因爲咱們曲面化是一個圓柱面,圓柱軸心線與Y軸平行,變化先後Y軸座標是同樣的,下面是原理:
字體
對每一個UI計算曲面化以後的座標,上面已經給出了計算方法,須要注意的是,計算玩座標以後還須要調整UI的角度,讓UI的前方是圓心到變換以後座標的方向。好比上面變換以後位於B點的UItransform.forward = transform.position.normalized
。
到了這一步,總體的UI就有了上面所說的假曲面化的效果。ui
Unity提供了BaseMeshEffect對UI元素生成的mesh作一些修改來實現一些效果,不一樣Unity版本這個API有些差別,這裏用到的是Unity5.3.4,主要是重寫ModifyMesh(VertexHelper vh)
方法。
在ModifyMesh
方法主要內容:this
public override void ModifyMesh(VertexHelper vh) {
base.ModifyMesh(vh);
if (!this.IsActive() || !bendEnable)
return;
/* 檢查是否須要從新生成或修改頂點座標,若是不須要,則使用已經緩存的頂點座標 */
if (cachedVertices == null || cachedTriangles == null || verticesDirty)
{
// 須要修改頂點,首先將Unity生成的頂點取出來
List<UIVertex> originUIVertices = new List<UIVertex>();
vh.GetUIVertexStream(originUIVertices);
/* 對頂點作一些變換,包括增長頂點以及從新計算頂點座標,對於Image和Text有不一樣的處理方式 */
}
// 若是材質改變,從新給定點着色
if (materialDirty)
{
UpdateVertiecsColor(cachedVertices);
materialDirty = false;
}
// 清除Unity生成的頂點,將咱們從新計算的頂點設置到mesh上
vh.Clear();
vh.AddUIVertexStream(cachedVertices, cachedTriangles);
// 根據生成頂點的類型也可使用vh.AddUIVertexTriangleStream(cachedVertices)設置頂點
}複製代碼
當Unity發現UI須要更新的時候會調用ModifyMesh(VertexHelper vh)
,Unity本身觸發UI更新的條件有尺寸改變和材質改變,咱們也可使用Graphic.SetAllDirty()
Graphic.SetVerticesDirty()
觸發。可是並不是全部的狀況下都須要從新計算頂點座標,當咱們計算出一個UI的曲面狀態下的頂點以後,不多須要從新計算,咱們只在UI尺寸改變的狀況下才觸發從新計算頂點,固然能夠根據實際使用狀況調整策略。頂點計算比較耗時,建議先判斷在計算。spa
取出來的頂點是一個UIVertex的列表,通常狀況下,列表中每3個構成一個三角形,若是改變列表中元素的位置,會致使UI顯示異常,因此最後輸出給VertexHelper的頂點也是有順序的。設計
這一步中對於Text和Image有較大差別,主要緣由在於通常的Text本身都有足夠多且細分的三角形,只須要從新計算頂點的座標就能夠有很好的曲面效果,可是通常狀況下,Image只有兩個三角形(Sliced模式下Tiled模式會多一些,可是依然不夠細分),四個頂點,對四個頂點從新計算以後依然是一個平面的效果,因此須要考慮給Image的mesh添加一些頂點,讓Image上的三角形足夠細分。
cachedVertices = new List<UIVertex>();
vh.GetUIVertexStream(cachedVertices);
BendMeshCylinder(cachedVertices);複製代碼
其中的BendMeshCylinder(cachedVertices)
函數就是將傳入的頂點變換到圓柱曲面上,須要注意的是UIVertex裏面的座標是相對UI自身的局部座標。處理過程不改變UIVertex列表的順序,處理完的依然保持以前的三角形順序,因此最後直接使用vh.AddUIVertexTriangleStream(cachedVertices)
設置頂點。
通常狀況下的Image只須要4個頂點就能夠構成兩個三角形,可是從VertexHelper裏面取出來的頂點有6個,每三個構成一個三角形,重複使用了其中的兩個頂點,以下圖所示,第0,1,2和3,4,5分別構成一個三角形,0和5爲同一個頂點,2和3爲同一個頂點。
List<UIVertex> originUIVertices = new List<UIVertex>();
vh.GetUIVertexStream(originUIVertices);
TrisToQuads(originUIVertices);
for (int i = 0; i < originUIVertices.Count; i += 4)
{
CreateQuads(originUIVertices, i, cachedVertices, cachedTriangles);
}
BendMeshCylinder(cachedVertices);複製代碼
首先在TrisToQuads(originUIVertices)
裏面是將每六個頂點構成的兩個三角形合併爲四個頂點構成的四邊形,而後在CreateQuads(originUIVertices, i, cachedVertices, cachedTriangles)
裏面對每一個四邊形內部進行線性插值計算新增頂點,每一個四邊形內部點的uv均可以根據座標在四邊形內部的位置計算出來。以下圖所示,紅色點爲新增頂點,當四邊形在X軸方向足夠細分,就不須要再添加頂點,一般狀況下Sliced模式的圖片邊緣是足夠細分的。
vh.AddUIVertexStream(cachedVertices, cachedTriangles)
。
若是改變UI的顏色屬性,會觸發MaterialDirty,咱們能夠經過Graphic.RegisterDirtyMaterialCallback
監聽這個改變,而後在ModifyMesh()
改變頂點的顏色。另外不建議監聽Graphic.RegisterDirtyVerticesCallback
來肯定是否須要從新計算頂點,由於改變頂點顏色,這個回調也會調用。
曲面化的原理如上,若是要真正運用起來,還須要配合一個Editor。須要注意的是,最好不要在曲面化狀態調整UI的座標和角度,不只很難調整到想要的位置,並且會影響總體曲面化的效果。文章裏面主要聚焦圓柱面,若是是球面,主要要修改UI座標的計算方法,和Image三角形細化方法,若是是其餘更復雜的曲面,不太建議用這種方式處理,由於涉及到曲面的數據計算,效果也很難保證。