UGUI 文字效果实现(Shadow\Gradient\Outline)
2017-03-17 12:56
288 查看
NGUI直接在UILabel组件中接入了Shadow、Gradient和outline选项,但在UGUI中是通过另外的组件单独提供,比如outline、shader等。这篇文章主要记录这几个文字效果实现的思路和流程。
1. 实现思路
1. UGUI源码分析
UGUI 的 Text 渲染的过程是由 TextGenerator 产生顶点数据,配合字体产生的贴图最终显示在屏幕上 . 下图为Text组件的继承树。UGUI中很多渲染相关的组件都是继承自Graphics,而Graphics还在Canvas绘制之前进行重建。Graphics中定义了渲染框架,核心代码如下。Text、Image组件的需要自己实现 protected virtual void OnPopulateMesh(VertexHelper vh) 方法来填充需要的数据。Unity提供了IMeshModifier接口供外部使用,如果在Text组件所在物体中存在IMeshModifier类型的组件,则会调用ModifyMesh方法允许你获得渲染数据。也就是说可以通过这种方式进行mesh、贴图等数据的修改。
protected virtual void UpdateGeometry() { if (useLegacyMeshGeneration) DoLegacyMeshGeneration(); else DoMeshGeneration(); } private void DoMeshGeneration() { if (rectTransform != null && rectTransform.rect.width >= 0 && rectTransform.rect.height >= 0) //获取数据 OnPopulateMesh(s_VertexHelper); else s_VertexHelper.Clear(); // clear the vertex helper so invalid graphics dont draw. var components = ListPool<Component>.Get(); GetComponents(typeof(IMeshModifier), components); //检测是否存在IMeshModifier接口类型组件 for (var i = 0; i < components.Count; i++) ((IMeshModifier)components[i]).ModifyMesh(s_VertexHelper); ListPool<Component>.Release(components); //填充渲染数据 s_VertexHelper.FillMesh(workerMesh); canvasRenderer.SetMesh(workerMesh); }
2. 实现接口
Unity提供了BaseMeshEffect类型,继承自IMeshModifier,提供 public abstract void ModifyMesh(VertexHelper vh); 接口。不过从4.7到现在这个接口修改了很多次,5.3版本是这个,5.5版本似乎又做了修改。文字的各种特效就可以通过这个接口获得渲染数据并进行修改即可。2. 颜色渐变(Gradient)
渐变其实就是根据需要进行渐变的方向和颜色修改顶点的颜色值。 如果只考虑上下方向的渐变,可以计算字符上下最大和最小值,然后进行插值即可计算出需要的颜色。对于多方向的渐变实现稍稍麻烦点,原理类似。核心代码如下:public override void ModifyMesh(VertexHelper vh) { var vertexList = new List<UIVertex>(); vh.GetUIVertexStream(vertexList); int count = vertexList.Count; ApplyGradient(vertexList, 0, count); vh.Clear(); vh.AddUIVertexTriangleStream(vertexList); } private void ApplyGradient(List<UIVertex> vertexList, int start, int end) { float bottomY = vertexList[0].position.y; float topY = vertexList[0].position.y; for (int i = start; i < end; ++i) { float y = vertexList[i].position.y; if (y > topY) { topY = y; } else if (y < bottomY) { bottomY = y; } } float uiElementHeight = topY - bottomY; for (int i = start; i < end; ++i) { UIVertex uiVertex = vertexList[i]; uiVertex.color = Color32.Lerp(bottomColor, topColor, (uiVertex.position.y - bottomY)/uiElementHeight); vertexList[i] = uiVertex; } }
3. 阴影(Shadow)
UGUI 中默认带有Shadow的组件,也是对ModifyMesh进行重载。然后将网格数据复制一份并向指定方向移动指定像素,然后填充到顶点数据中。也就是说,Shadow实现是通过增加顶点数据实现的。// X y 为 shadow大小 protected void ApplyShadowZeroAlloc(List<UIVertex> verts, Color32 color, int start, int end, float x, float y) { UIVertex vt; var neededCapacity = verts.Count + end - start; if (verts.Capacity < neededCapacity) verts.Capacity = neededCapacity; for (int i = start; i < end; ++i) { vt = verts[i]; verts.Add(vt); Vector3 v = vt.position; v.x += x; v.y += y; vt.position = v; var newColor = color; if (m_UseGraphicAlpha) newColor.a = (byte)((newColor.a * verts[i].color.a) / 255); vt.color = newColor; verts[i] = vt; } }
4. 勾边 (Outline)
1. 基于Shadow
outline的实现传统的做法是在4个方向或者8个方向进行Shodow操作。换句话说顶点数需要增加8倍,大量使用请谨慎。public override void ModifyMesh(VertexHelper vh) { var verts = ListPool<UIVertex>.Get(); vh.GetUIVertexStream(verts); var neededCpacity = verts.Count * 5; if (verts.Capacity < neededCpacity) verts.Capacity = neededCpacity; var start = 0; var end = verts.Count; ApplyShadowZeroAlloc(verts, effectColor, start, verts.Count, effectDistance.x, effectDistance.y); start = end; end = verts.Count; ApplyShadowZeroAlloc(verts, effectColor, start, verts.Count, effectDistance.x, -effectDistance.y); start = end; end = verts.Count; ApplyShadowZeroAlloc(verts, effectColor, start, verts.Count, -effectDistance.x, effectDistance.y); start = end; end = verts.Count; ApplyShadowZeroAlloc(verts, effectColor, start, verts.Count, -effectDistance.x, -effectDistance.y) vh.Clear(); vh.AddUIVertexTriangleStream(verts); ListPool<UIVertex>.Release(verts); }
2 . 效果扩展
4方向Outlin美术经常不满足的其效果,所以有时候会需要8方向描边的outline8, 甚至更多。基于shadow方式的outline实现可以参考开源代码:https://github.com/n-yoda/unity-vertex-effects 。换汤不换药吧,越好的效果顶点增加的越多。几种效果的图示,中文字边缘比较明显。Circle的边缘更圆滑一些,Shadow操作的次数和8方向相同。
3. 基于Mesh实现
基于Shadow的实现方式内存占用比较高,当然还有别的思路,可以参考网页中描述的方式。其详细流程:提取文字UV区域
扩大文字绘图区域并记录增长量
在pixel处理阶段,在每个像素点,对周围区域(受增长量以及原有uv区域控制)进行采样并作为这个点的颜色和alpha
对原始纹理、alpha以及扩大后的区域进行融合
5.结论
outline的几种实现方式根据具体需求使用,云风在博客中也给出一种优化策略可参考。总之,UGUI对字体效果的支持不算很好,像图文混排等等都需要自己做扩展,据说收购了TexmeshPro对自身text系统进行扩展,期待。
相关文章推荐
- UGUI 文字效果实现(Shadow\Gradient\Outline)
- Unity5的uGUI中实现文字渐变效果(Gradient)
- 利用css3的text-shadow属性实现文字阴影乳白效果
- android 使用LinearGradient实现手机开机文字闪烁效果
- css3新功能之(text-shadow实现)CSS3文字阴影效果
- CSS3利用text-shadow属性实现多种效果的文字样式展现方法
- 利用css3的text-shadow属性实现文字阴影乳白效果
- IE如何实现text-shadow文字阴影效果呢?
- linear-gradient实现纯CSS文字淡入效果
- LinearGradient和Matrix实现动态的文字闪烁效果
- CSS3 利用 text-shadow 实现文字描边效果
- 纯CSS文字阴影效果实现
- CSS实现文字压在边线上的效果,fieldset与legend
- 一个js实现任务栏文字滚动效果
- 两种方式实现的文字竖排效果
- Visual C++实现各种文字特殊效果
- CSharp Tips:模拟MSN Message发送文字效果的实现
- CSS实现的文字竖排效果
- 用css滤镜实现的文字描边效果的代码
- 用css滤镜实现的文字描边效果的代码