在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三角形细化方法,若是是其余更复杂的曲面,不太建议用这种方式处理,由于涉及到曲面的数据计算,效果也很难保证。