using RK_Runtime; using RK_Runtime.RK_Space_UI; using System; public class MC_RectTransformChangeListerner : RK_UIMonoViewComponent { private Action m_Listener; public void SetListener(Action action) { m_Listener = action; } private void OnRectTransformDimensionsChange() { if (m_Listener != null) { try { m_Listener(); } catch (Exception e) { RK.Log.LogError(e); } } } }
原因可能是:Canvas 的Piexel Perfect,在旋转UI内容动画时会发生抖动,去选勾选即可
可以发现下拉组件打开的内容是复制Template的,所以可以利用这个,创建两个脚本,一个用来接受,一个用来监听是不是打开了。
Dropdown挂载接受脚本,Template挂载监听脚本
using System; public class ReceiveDropDownStatus : UIMonoViewComponent { public Action<bool> StatusChange; }
using Frame.Runtime.UI; public class ListenDropDownStatus : UIMonoViewComponent { private ReceiveDropDownStatus m_Receive; // Start is called before the first frame update void Start() { if (m_Receive == null) { m_Receive = this.transform.parent.GetComponent<ReceiveDropDownStatus>(); } if (this.gameObject.name == "Dropdown List") { if (m_Receive.StatusChange != null) { m_Receive.StatusChange(true); } } } private void OnDisable() { if (this.gameObject.name == "Dropdown List") { if (m_Receive.StatusChange != null) { m_Receive.StatusChange(false); } } } }
ui可以添加canvas并设置overrideSorting和sortingOrder,如果需要交互需要添加GraphicRaycaster组件
3d物体需要修改MeshRenderer的sortingOrder属性。
再利用好深度可以很好的处理好这种叠层的问题,规划好sortingOrder
using UnityEngine; using UnityEngine.UI; using Frame.Runtime.UI; public class SortOrderSet : UIMonoViewComponent { [SerializeField] private int SortOrder = 0; [SerializeField] private int m_UISortOrder = 0; [SerializeField] private int m_RenderSortOrder = 0; public void SetUISortOrder(int order, bool raycast = true ) { Canvas canvas = GetComponent<Canvas>(); if (canvas == null) { canvas = gameObject.AddComponent<Canvas>(); } GraphicRaycaster gr = GetComponent<GraphicRaycaster>(); if (raycast) { if (gr == null) { gameObject.AddComponent<GraphicRaycaster>(); } } else { if (gr != null) { DestroyImmediate(gr); } } m_UISortOrder = order; canvas.overrideSorting = true; canvas.sortingOrder = m_UISortOrder; } public void SetRenderSortOrder(int order) { m_RenderSortOrder = order; foreach (var render in transform.GetComponentsInChildren<MeshRenderer>(true)) { render.sortingOrder = m_RenderSortOrder; } } }
一、网格重建优化 Rebuild
UI层级关系尽量简单
动静分离,动态元素单独放到特定的Canvas中
active操作会触发耗时的rebuild
二、合批优化
同一图集内容尽量放在同一个层级中
少用Mask,会打断合批且自身额外加2DC
不要用空的Image,打断合批
必要时候可以将公用的ui加到需要的图集中
三、OverDraw优化
禁用看不到的ui的canvas
实现一个响应Raycast但不参与渲染的EmptyGraphic,用于扩大点击范围等
四、字体
使用TeshmeshPro替代原来Text
使用艺术字体
五、其他
扩展工具禁用Raycast,需要的手动点开
一、创建一个用来控制效果开启和关闭的开关,获取处理过的贴图的管理类
using UnityEngine; public class GlassCtrl : MonoBehaviour { public static int Iterations = 2;//高斯模糊处理次数 public static int takeShot = 0;//1只处理一次获取贴图,-1一直处理更新贴图 public static RenderTexture renderTexture = null;//保存处理过的贴图 public void StartTakeShot(int width, int height, int takeShot) { ReleaseRenderTexture(); renderTexture = new RenderTexture(width, height, 32); GlassCtrl.takeShot = takeShot; } public void StopTakeShot() { takeShot = 0; } public void SetIterations(int time) { Iterations = time; } public void ReleaseRenderTexture() { if (renderTexture != null) { Destroy(renderTexture); renderTexture = null; } } public RenderTexture GetShotTexture() { return renderTexture; } }
二、创建一个ScriptableRenderPass
using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; public class GlassBlurRenderPass : ScriptableRenderPass { private CommandBuffer cmd; private string cmdName; private RenderTargetHandle dest; private Material m_blurMat; private RenderTargetIdentifier source { get; set; } RenderTargetHandle m_temporaryColorTexture; RenderTargetHandle blurredID; RenderTargetHandle blurredID2; public GlassBlurRenderPass(GlassBlurRenderPassFeature.Settings param) { renderPassEvent = param.renderEvent; cmdName = param.cmdName; m_blurMat = param.blurMat; blurredID.Init("blurredID"); blurredID2.Init("blurredID2"); } public void Setup(RenderTargetIdentifier src, RenderTargetHandle _dest) { this.source = src; this.dest = _dest; } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { if (GlassCtrl.takeShot == 0) return; if (renderingData.cameraData.isSceneViewCamera) return; //如果cullingMask非UI层的camera,返回 // if (!((renderingData.cameraData.camera.cullingMask & 1<<LayerMask.NameToLayer("UI")) > 0)) // return; cmd = CommandBufferPool.Get(cmdName); Vector2[] sizes = { new Vector2(Screen.width, Screen.height), new Vector2(Screen.width / 2, Screen.height / 2), new Vector2(Screen.width / 4, Screen.height / 4), new Vector2(Screen.width / 8, Screen.height / 8), }; RenderTextureDescriptor opaqueDesc = renderingData.cameraData.cameraTargetDescriptor; opaqueDesc.depthBufferBits = 0; cmd.GetTemporaryRT(m_temporaryColorTexture.id, opaqueDesc, FilterMode.Bilinear); cmd.Blit(source, m_temporaryColorTexture.Identifier()); for (int i = 0; i < GlassCtrl.Iterations; ++i) { cmd.GetTemporaryRT(blurredID.id, opaqueDesc, FilterMode.Bilinear); cmd.GetTemporaryRT(blurredID2.id, opaqueDesc, FilterMode.Bilinear); cmd.Blit(m_temporaryColorTexture.Identifier(), blurredID.Identifier()); cmd.SetGlobalVector("offsets", new Vector4(2.0f / sizes[i].x, 0, 0, 0)); cmd.Blit(blurredID.Identifier(), blurredID2.Identifier(), m_blurMat); cmd.SetGlobalVector("offsets", new Vector4(0, 2.0f / sizes[i].y, 0, 0)); cmd.Blit(blurredID2.Identifier(), blurredID.Identifier(), m_blurMat); cmd.Blit(blurredID.Identifier(), m_temporaryColorTexture.Identifier()); } //把最终内容Blit到一个RenderTexture上。 cmd.Blit(blurredID.Identifier(), GlassCtrl.renderTexture); if (GlassCtrl.takeShot == 1) GlassCtrl.takeShot = 0; context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); //Debug.LogError("glass pass render"); } }
使用的shader为:
Shader "PostEffect/SeparableGlassBlur" { Properties { _MainTex ("Base (RGB)", 2D) = "" {} } HLSLINCLUDE #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" struct appdata_img { float4 vertex:POSITION; float2 texcoord:TEXCOORD0; }; struct v2f { float4 pos : POSITION; float2 uv : TEXCOORD0; float4 uv01 : TEXCOORD1; float4 uv23 : TEXCOORD2; float4 uv45 : TEXCOORD3; }; float4 offsets; sampler2D _MainTex; v2f vert (appdata_img v) { v2f o; VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz); o.pos = vertexInput.positionCS; o.uv.xy = v.texcoord.xy; o.uv01 = v.texcoord.xyxy + offsets.xyxy * float4(1,1, -1,-1); o.uv23 = v.texcoord.xyxy + offsets.xyxy * float4(1,1, -1,-1) * 2.0; o.uv45 = v.texcoord.xyxy + offsets.xyxy * float4(1,1, -1,-1) * 3.0; return o; } half4 frag (v2f i) : COLOR { half4 color = float4 (0,0,0,0); color += 0.40 * tex2D (_MainTex, i.uv); color += 0.15 * tex2D (_MainTex, i.uv01.xy); color += 0.15 * tex2D (_MainTex, i.uv01.zw); color += 0.10 * tex2D (_MainTex, i.uv23.xy); color += 0.10 * tex2D (_MainTex, i.uv23.zw); color += 0.05 * tex2D (_MainTex, i.uv45.xy); color += 0.05 * tex2D (_MainTex, i.uv45.zw); return color; } ENDHLSL Subshader { Pass { ZTest Always Cull Off ZWrite Off HLSLPROGRAM #pragma vertex vert #pragma fragment frag ENDHLSL } } }
三、创建一个ScriptableRendererFeature
using UnityEngine; using UnityEngine.Rendering.Universal; public class GlassBlurRenderPassFeature : ScriptableRendererFeature { //设置一个RendererFeature的配置,写上要用的参数 [System.Serializable] public class Settings { public RenderPassEvent renderEvent = RenderPassEvent.BeforeRenderingPostProcessing; public LayerMask layerMask = -1; public Material blurMat; public string textureName = ""; public string cmdName = ""; public string passName = ""; } GlassBlurRenderPass m_ScriptablePass; private RenderTargetHandle dest; public Settings settings; //这个在可视化中设置 public override void Create() { m_ScriptablePass = new GlassBlurRenderPass(settings); m_ScriptablePass.renderPassEvent = settings.renderEvent; } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { var src = renderer.cameraColorTarget; dest = RenderTargetHandle.CameraTarget; //把camera渲染到的画面src 传入GlassBlurRenderPass里。 m_ScriptablePass.Setup(src,this.dest); //注入队列 renderer.EnqueuePass(m_ScriptablePass); } }
四、将ScriptableRendererFeature添加到Render Feature,填写自己定义的参数内容
五、业务控制和注意
通过逻辑控制GlassCtrl然后获取贴图就可以了。注意的是URP这个处理只对主相机渲染的内容进行处理(Base Camera),对添加到主相机下面的Overlay相机不会影响,所以必要时刻需要调整需要玻璃化的相机为主相机。一般都是背景做毛玻璃效果,内容还是在上面的,所以这应该不会有问题。
using UnityEngine; using TMPro; public class DynamicTMP : MonoBehaviour { public TMP_Text tmp; public float Strength = 3.6f; public float Space = 0.03f; public float Frequently = 2f; void Update() { tmp.ForceMeshUpdate(); var textInfo = tmp.textInfo; for (int i = 0; i < textInfo.characterCount; i++) { var charInfo = textInfo.characterInfo[i]; if(!charInfo.isVisible) continue; var verts = textInfo.meshInfo[charInfo.materialReferenceIndex].vertices; for (int j = 0; j < 4; j++) { var orig = verts[charInfo.vertexIndex + j]; verts[charInfo.vertexIndex + j] = orig + new Vector3(0, Mathf.Sin(Time.time * Frequently + orig.x * Space) * Strength, 0); } } for (int i = 0; i < textInfo.meshInfo.Length; i++) { var meshInfo = textInfo.meshInfo[i]; meshInfo.mesh.vertices = meshInfo.vertices; tmp.UpdateGeometry(meshInfo.mesh, i); } } }
public static bool IsEmoji(Char c) { var _unicodeCategory = char.GetUnicodeCategory(c); switch (_unicodeCategory) { case UnicodeCategory.OtherSymbol: case UnicodeCategory.Surrogate: case UnicodeCategory.ModifierSymbol: case UnicodeCategory.NonSpacingMark: { return true; } default: { return false; } } }
选中TextmeshPro创建的Font Asset下的Material
在Inspector中点击设置按钮-Create Material Preset
调整新创建出来的材质属性,在需要的TextMesh组件中设置Material Preset