想在Unity3D里为一个图片或者文字添加阴影效果,只需要在该对象上在添加一个Shadow组件。Shadow通过为图像或者文字的Mesh添加顶点来实现阴影效果,而Outline是在对象四角上各添加了一个Shadow。由此我们可以得知,Outline的(额外)消耗是Shadow的四倍,所以还是需要谨慎使用。
Outline继承自Shadow,Shadow继承自BaseMeshEffect,BaseMeshEffect继承自UIBehaviour和IMeshModifier。
Graphic在生成完Mesh数据之后,会查找对象上IMeshModifier类型的组件,调用它们的ModifyMesh方法,之后再正式生成Mesh。
BaseMeshEffect是一个抽象类,而ModifyMesh是一个抽象方法(以Mesh为参数的ModifyMesh方法是过期的不再讨论),具体在其子类里实现。它的OnEnable、OnDisable和OnDidApplyAnimationProperties(当应用动画属性后),会调用graphic属性的SetVerticesDirty方法(设置顶点数据为脏的,重建图像时重新生成顶点数据)。graphic是一个只读属性,获取的是本对象的Graphic组件(Image、RawImage或Text)。
Shadow的ModifyMesh方法:
public override void ModifyMesh(VertexHelper vh)
{
if (!IsActive())
return;
var output = ListPool.Get();
vh.GetUIVertexStream(output);
ApplyShadow(output, effectColor, 0, output.Count, effectDistance.x, effectDistance.y);
vh.Clear();
vh.AddUIVertexTriangleStream(output);
ListPool.Release(output);
}
获取VertexHelper(暂存顶点数据,用于辅助生成Mesh)里的顶点数据output。然后根据effectColor和effectDistance调整顶点。ApplyShadow会将顶点数乘以2,然后调用ApplyShadowZeroAlloc方法。遍历output的顶点,根据顶点生成一个新的顶点vt,添加到顶点列表里,并将vt的位置加上偏移量,并设置颜色。最后将output重新添加到VertexHelper里面(AddUIVertexTriangleStream会自行生成三角形)。
Outline的ModifyMesh方法:
public override void ModifyMesh(VertexHelper vh)
{
if (!IsActive())
return;
var verts = ListPool.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.Release(verts);
}
Outline将顶点数乘以了5,并在四角上各添加了一组(阴影)顶点。
补充:
除了Shadow和Outline以外,UGUI还内置了一个效果PositionAsUV1。
代码十分简单:
public class PositionAsUV1 : BaseMeshEffect
{
protected PositionAsUV1()
{}
public override void ModifyMesh(VertexHelper vh)
{
UIVertex vert = new UIVertex();
for (int i = 0; i < vh.currentVertCount; i++)
{
vh.PopulateUIVertex(ref vert, i);
vert.uv1 = new Vector2(vert.position.x, vert.position.y);
vh.SetUIVertex(vert, i);
}
}
}
就是根据坐标点设置uv1坐标(法线贴图坐标),为图片或者文字添加法线贴图效果。
原理我们知道了,那么效果是怎么样的呢?
导入一张贴图:
导入设置Texture Type为Normal map(法线贴图)。
我们新建一个Material,设置shader为UI/Unlit/Detail。将刚刚导入的法线贴图设置为Detail(RGB)。
在场景里新建一个Image,为它设置Material。我们会发现图片的颜色变深了一点,但是除此之外并没有变化。
接着为这个Image对象添加PositionAsUV1组件。如图: