在uGUI正交相機中實現旋轉透視效果
正常uGUI使用正交相機的話,旋轉是沒有透視效果的,但如果能實現較簡單的透視,
對一些效果表現來說還是不錯的;見下圖(左為透視效果):

正常思路感覺各種麻煩。
因為uGUI使用unity的x和y方向表示寬高,z方向自然就是縱深,我們可以直接拿z值作為系數進行縮放處理,
達到偽透視的效果(美中不足,細看會有弧度感)。
但是,直接對z軸進行縮放會出現扭曲問題:

這種情況可以通過頂點細分來解決,一般細分一次即可。頂點越多,UV插值產生的扭曲影響就越小:

再擴展一下可支持Text組件:

最后上代碼:
namespace Hont { using UnityEngine; using UnityEngine.UI; [ExecuteInEditMode] public class UiPerspectiveFx : BaseMeshEffect { [Header("Only Image")] public int subdivision = 2; //有的材質細分2次,有的需3-4次。 public float perspectiveScale = 1.0f; public Transform referencePoint;//z軸參考對象,賦予自身即可 public bool alwaysRefresh = true; private void Update() { if (alwaysRefresh) graphic.SetVerticesDirty(); } private void CalcPerspectiveScale(ref Vector3 point) { if (referencePoint) { var vertexWS = transform.TransformPoint(point); vertexWS.z -= referencePoint.position.z; var vertexZ = vertexWS.z * perspectiveScale; point *= 1f + vertexZ; } } public override void ModifyMesh(VertexHelper vh) { RectTransform rectTransform = transform as RectTransform; if (graphic is Image image) { vh.Clear(); var sizeDelta = rectTransform.sizeDelta; Vector2 begin = -sizeDelta * 0.5f; Vector2 cell = sizeDelta / subdivision; float uvCell = 1f / subdivision; // Get the sprite from the Image component Sprite sprite = image.sprite; if (sprite == null) return; // Calculate the texture rect in normalized coordinates Rect textureRect = sprite.textureRect; textureRect.x /= sprite.texture.width; textureRect.y /= sprite.texture.height; textureRect.width /= sprite.texture.width; textureRect.height /= sprite.texture.height; for (int x = 0; x < subdivision; ++x) //TODO:可進一步做緩存優化 { for (int y = 0; y < subdivision; ++y) { Vector3 p0 = new Vector3(begin.x + cell.x * x, begin.y + cell.y * y); Vector3 p1 = new Vector3(begin.x + cell.x * x, begin.y + cell.y * (y + 1)); Vector3 p2 = new Vector3(begin.x + cell.x * (x + 1), begin.y + cell.y * (y + 1)); Vector3 p3 = new Vector3(begin.x + cell.x * (x + 1), begin.y + cell.y * y); Vector2 uv0 = new Vector2(textureRect.x + x * uvCell * textureRect.width, textureRect.y + y * uvCell * textureRect.height); Vector2 uv1 = new Vector2(textureRect.x + x * uvCell * textureRect.width, textureRect.y + (y + 1) * uvCell * textureRect.height); Vector2 uv2 = new Vector2(textureRect.x + (x + 1) * uvCell * textureRect.width, textureRect.y + (y + 1) * uvCell * textureRect.height); Vector2 uv3 = new Vector2(textureRect.x + (x + 1) * uvCell * textureRect.width, textureRect.y + y * uvCell * textureRect.height); CalcPerspectiveScale(ref p0); CalcPerspectiveScale(ref p1); CalcPerspectiveScale(ref p2); CalcPerspectiveScale(ref p3); vh.AddUIVertexQuad(new UIVertex[] { new() {position = p0, color = graphic.color, uv0 = uv0}, new() {position = p1, color = graphic.color, uv0 = uv1}, new() {position = p2, color = graphic.color, uv0 = uv2}, new() {position = p3, color = graphic.color, uv0 = uv3} }); } } } else if (graphic is Text) { for (int i = 0, iMax = vh.currentVertCount; i < iMax; i++) { UIVertex vertex = default; vh.PopulateUIVertex(ref vertex, i); CalcPerspectiveScale(ref vertex.position); vh.SetUIVertex(vertex, i); } } } } }
掛載到Image/Text組件下即可:

浙公網安備 33010602011771號