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
