赞
踩
在使用UGUI Image组件显示不规则图片、镂空图片时,Image总是会创建一个四边形网格来显示图形,渲染过程中,GPU需要对完全透明的区域进行计算,这不利于性能的优化,一个解决办法是采用多边形网格显示图形,来减少这种不必要的消耗。
下面是Image组件和多边形显示组件的网格对比
下面是Image和多边形的Overdraw对比
整个方案的实现过程包括以下几个步骤:
如果需要根据图形做射线检测,在 UIPolyImage/Image 组件上添加PolyRaycastFilter 组件。因为该组件需要读取贴图的像素,所以需要将贴图的 readAndWrite 属性勾选。
示例项目:这里
using System.Collections; using System.Collections.Generic; using UnityEngine; namespace UnityEngine.UI { public class UIPolyImage : Image { protected override void OnPopulateMesh(VertexHelper vh) { if (overrideSprite == null) { base.OnPopulateMesh(vh); return; } switch (type) { case Type.Simple: case Type.Filled: case Type.Sliced: case Type.Tiled: GenerateSimpleSprite(vh, preserveAspect); break; } } void GenerateSimpleSprite(VertexHelper vh, bool lPreserveAspect) { Vector4 v = GetDrawingDimensions(lPreserveAspect); var color32 = color; float width = v.z - v.x; float height = v.w - v.y; //将sprite.pivot进行归一化 Vector2 spritePivot = new Vector2(sprite.pivot.x / sprite.rect.width, sprite.pivot.y / sprite.rect.height); Vector2 pivotOffset = Vector2.Scale(spritePivot - rectTransform.pivot, new Vector2(width, height)); Vector3 scale = new Vector3(1, 1, 1); scale.x = width / sprite.bounds.size.x; scale.y = height / sprite.bounds.size.y; Matrix4x4 trsMT = Matrix4x4.TRS(pivotOffset, Quaternion.identity, scale); vh.Clear(); for (int i = 0; i < sprite.vertices.Length; i++) { vh.AddVert(trsMT.MultiplyPoint3x4(sprite.vertices[i]), color32, sprite.uv[i]); } for (int i = 0; i < sprite.triangles.Length; i += 3) { vh.AddTriangle(sprite.triangles[i], sprite.triangles[i + 1], sprite.triangles[i + 2]); } } public Rect GetDrawingRect() { Vector4 v = GetDrawingDimensions(preserveAspect); return Rect.MinMaxRect(v.x, v.y, v.z, v.w); } /// Image's dimensions used for drawing. X = left, Y = bottom, Z = right, W = top. private Vector4 GetDrawingDimensions(bool shouldPreserveAspect) { var padding = overrideSprite == null ? Vector4.zero : UnityEngine.Sprites.DataUtility.GetPadding(overrideSprite); var size = overrideSprite == null ? Vector2.zero : new Vector2(overrideSprite.rect.width, overrideSprite.rect.height); Rect r = GetPixelAdjustedRect(); // Debug.Log(string.Format("r:{2}, size:{0}, padding:{1}", size, padding, r)); int spriteW = Mathf.RoundToInt(size.x); int spriteH = Mathf.RoundToInt(size.y); var v = new Vector4( padding.x / spriteW, padding.y / spriteH, (spriteW - padding.z) / spriteW, (spriteH - padding.w) / spriteH); if (shouldPreserveAspect && size.sqrMagnitude > 0.0f) { var spriteRatio = size.x / size.y; var rectRatio = r.width / r.height; if (spriteRatio > rectRatio) { var oldHeight = r.height; r.height = r.width * (1.0f / spriteRatio); r.y += (oldHeight - r.height) * rectTransform.pivot.y; } else { var oldWidth = r.width; r.width = r.height * spriteRatio; r.x += (oldWidth - r.width) * rectTransform.pivot.x; } } v = new Vector4( r.x + r.width * v.x, r.y + r.height * v.y, r.x + r.width * v.z, r.y + r.height * v.w ); return v; } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
这里实现方式是通过获取像素的 Alpha 值来进行判断的,所以一个明显的缺点就是需要将贴图的 readable 设置为true。当然还有一些其他的方法也可以实现功能,比如设置多边形碰撞、多边形检测等等,后续如果有机会的话,会补充其他的方法。
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; namespace UnityEngine.UI { /// <summary> /// 用于检测不规则图形的射线检测方法,由于需要进行多边形的射线检测, /// 效率相对较低,所以,确认自己必须进行不规则射线检测时,才使用 /// 该组件。 /// </summary> [RequireComponent(typeof(UIPolyImage))] public class PolyRaycastFilter : MonoBehaviour, ICanvasRaycastFilter { private UIPolyImage image { get { if (m_Image == null) m_Image = GetComponent<UIPolyImage>(); return m_Image; } } private RectTransform rectTransform { get { if (m_RectTransform == null) m_RectTransform = GetComponent<RectTransform>(); return m_RectTransform; } } private UIPolyImage m_Image; private RectTransform m_RectTransform; public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera) { if (image.sprite == null) return false; //首先转换到本地坐标系中,方便下面的计算 Vector2 localP = rectTransform.InverseTransformPoint(sp); Rect pixelRect = image.GetDrawingRect(); if (!pixelRect.Contains(localP, false)) return false; Vector2 delta = localP - pixelRect.min; Vector2 normalizedDelta = new Vector2(delta.x / pixelRect.width, delta.y / pixelRect.height); int x = Mathf.CeilToInt(normalizedDelta.x * image.sprite.rect.width+image.sprite.rect.xMin); int y = Mathf.CeilToInt(normalizedDelta.y * image.sprite.rect.height+image.sprite.rect.yMin); Color pixel = image.sprite.texture.GetPixel(x, y); return !Mathf.Approximately(pixel.a, 0f); } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。