如何在World Space模式的Canvas上直接绘制自定义Mesh?
解决World Space Canvas中CanvasRenderer.SetMesh()无显示的问题
你遇到的核心问题是CanvasRenderer与普通MeshRenderer的坐标空间、依赖组件要求完全不同,直接复用MeshFilter的Mesh逻辑会因为坐标不匹配、缺少必要设置而无法显示。以下是具体修复方案:
关键问题分析
- 坐标空间差异:World Space模式的Canvas虽处于世界空间,但CanvasRenderer渲染的Mesh使用的是RectTransform的本地坐标(原点默认在RectTransform中心),你之前的顶点是基于Canvas Rect左下角(0,0)计算的,两者坐标系不匹配。
- 缺少必要配置:CanvasRenderer需要绑定UI专用材质,且Mesh必须设置UV坐标才能让材质正确显示。
- 性能浪费:Update中每次创建新Mesh会导致大量GC,应该复用Mesh对象。
修复步骤
1. 调整组件配置
移除物体上的MeshFilter和MeshRenderer,添加CanvasRenderer组件;确保物体是World Space Canvas的子物体,且自身带有RectTransform。
2. 修改代码逻辑
以下是修复后的完整代码,包含坐标转换、材质绑定、Mesh复用等优化:
using UnityEngine; public class MeshCanvas : MonoBehaviour { private Vector3[] vertices; private int[] triangles; private Vector2[] uv; private Mesh mesh; private RectTransform canvasRectTransform; private CanvasRenderer canvasRenderer; public float offset = 10f; public Material uiMaterial; // 拖拽UI材质(如内置的UI/Default)到这里 private void Awake() { // 缓存组件,避免重复查找 canvasRectTransform = FindObjectOfType<Canvas>().GetComponent<RectTransform>(); canvasRenderer = GetComponent<CanvasRenderer>(); mesh = new Mesh(); // 初始化三角形数组(固定不变,无需重复创建) triangles = new int[] { 0, 1, 2, 2, 3, 0 }; } private void Update() { UpdateMesh(); } private void UpdateMesh() { var canvasRect = canvasRectTransform.rect; float canvasWidth = canvasRect.width; float canvasHeight = canvasRect.height; // 将基于左下角的坐标转换为RectTransform本地坐标(原点在中心) vertices = new Vector3[] { new Vector3(-canvasWidth/2 + offset, -canvasHeight/2 + offset), new Vector3(-canvasWidth/2 + offset, canvasHeight/2 - offset), new Vector3(canvasWidth/2 - offset, canvasHeight/2 - offset), new Vector3(canvasWidth/2 - offset, -canvasHeight/2 + offset) }; // 设置UV坐标,确保材质纹理正确映射 uv = new Vector2[] { new Vector2(0, 0), new Vector2(0, 1), new Vector2(1, 1), new Vector2(1, 0) }; // 更新Mesh数据 mesh.Clear(); mesh.vertices = vertices; mesh.triangles = triangles; mesh.uv = uv; mesh.RecalculateBounds(); // 必须重新计算包围盒,否则CanvasRenderer可能不渲染 // 绑定Mesh和材质到CanvasRenderer canvasRenderer.SetMesh(mesh); if (uiMaterial != null) { canvasRenderer.SetMaterial(uiMaterial, null); } } }
3. 材质设置
在Inspector面板中,将Unity内置的UI/Default材质拖拽到脚本的uiMaterial字段上(如果没有该材质,可以在Project窗口右键创建UI -> Default Material)。
额外注意事项
- 如果Canvas的尺寸动态变化,建议监听
RectTransform.sizeDelta的变化,只在尺寸改变时更新Mesh,避免Update中频繁计算。 - World Space Canvas的缩放会影响Mesh的显示大小,若需要保持Mesh相对Canvas的比例,可在计算顶点时考虑Canvas的缩放值。
内容的提问来源于stack exchange,提问作者ZoKi Mo
相关产品推荐
相关产品推荐

