Unity中为同一物体的内外网格分配不同材质
解决Unity中同一物体双Mesh分别分配材质的问题
嘿,先给你指出代码里的一个关键问题:你现在是用GetComponent<MeshFilter>()获取同一个MeshFilter来赋值轮廓Mesh,这会直接把之前的内部Mesh覆盖掉,等于白忙活啦!要实现两个Mesh共存并分别用不同材质,有两种简单可行的方案,我给你详细说:
方案一:用两个MeshFilter组件(最直接)
这个方法完全适配你已经在Mesh Renderer里准备好两个材质的场景,步骤很简单:
- 给物体新增一个MeshFilter组件(别复用原来的那个)
- 分别给两个MeshFilter赋值内部Mesh和轮廓Mesh
- Mesh Renderer会自动按MeshFilter的组件顺序分配材质——第一个MeshFilter用第一个材质,第二个用第二个
修改后的完整代码:
void BoxMesh(float width, float height, float OLwidth) { // 内部Mesh的MeshFilter(用物体原本的) MeshFilter innerMeshFilter = GetComponent<MeshFilter>(); Mesh innerMesh = new Mesh(); innerMeshFilter.mesh = innerMesh; // 轮廓Mesh的MeshFilter:新增一个组件 MeshFilter outlineMeshFilter = gameObject.AddComponent<MeshFilter>(); Mesh outlineMesh = new Mesh(); outlineMeshFilter.mesh = outlineMesh; // -------------------------- // 内部Mesh的顶点、三角形等设置(和你原来的逻辑一致) // -------------------------- Vector3[] innerVertices = new Vector3[4] { new Vector3(0, 0, 0), new Vector3(0, height, 0), new Vector3(width, height, 0), new Vector3(width, 0, 0) }; int[] innerTriangles = new int[6] { 0, 1, 3, 1, 2, 3 }; Vector3[] innerNormals = new Vector3[4]; Vector2[] innerUVs = new Vector2[4] { new Vector2(0, 0), new Vector2(0, 1), new Vector2(1, 1), new Vector2(1, 0) }; for (int i = 0; i < 4; i++) innerNormals[i] = -Vector3.forward; innerMesh.vertices = innerVertices; innerMesh.triangles = innerTriangles; innerMesh.normals = innerNormals; innerMesh.uv = innerUVs; // -------------------------- // 轮廓Mesh的顶点、三角形等设置 // -------------------------- Vector3[] outlineVertices = new Vector3[4] { new Vector3(-OLwidth, -OLwidth, 0), new Vector3(-OLwidth, height + OLwidth, 0), new Vector3(width + OLwidth, height + OLwidth, 0), new Vector3(width + OLwidth, -OLwidth, 0) }; // 注意:这里调整了三角形顺序,避免背面剔除导致轮廓看不到 int[] outlineTriangles = new int[6] { 0, 3, 1, 1, 3, 2 }; Vector3[] outlineNormals = new Vector3[4]; Vector2[] outlineUVs = new Vector2[4] { new Vector2(0, 0), new Vector2(0, 1), new Vector2(1, 1), new Vector2(1, 0) }; for (int i = 0; i < 4; i++) outlineNormals[i] = -Vector3.forward; outlineMesh.vertices = outlineVertices; outlineMesh.triangles = outlineTriangles; outlineMesh.normals = outlineNormals; outlineMesh.uv = outlineUVs; // -------------------------- // 碰撞体设置(用轮廓的范围) // -------------------------- PolygonCollider2D polyCollider = GetComponent<PolygonCollider2D>(); polyCollider.pathCount = 1; Vector2[] colliderPath = new Vector2[4] { new Vector2(-OLwidth, -OLwidth), new Vector2(-OLwidth, height + OLwidth), new Vector2(width + OLwidth, height + OLwidth), new Vector2(width + OLwidth, -OLwidth) }; polyCollider.SetPath(0, colliderPath); // -------------------------- // 手动指定材质顺序(可选,如果你想代码控制) // -------------------------- MeshRenderer meshRenderer = GetComponent<MeshRenderer>(); // 假设你已经把内部材质和轮廓材质拖到了public变量里 // meshRenderer.materials = new Material[] { yourInnerMaterial, yourOutlineMaterial }; }
材质分配说明:
打开Inspector面板,找到你的Mesh Renderer组件,确保Materials数组里有两个材质。默认情况下,第一个材质会对应第一个MeshFilter(内部Mesh),第二个材质对应第二个MeshFilter(轮廓Mesh)。如果顺序不对,直接在Inspector里拖拽材质调整顺序就行。
方案二:合并为带SubMesh的单个Mesh(更高效)
如果不想用两个MeshFilter,也可以把内部和轮廓的Mesh合并成一个,用SubMesh来区分,这样一个MeshFilter就够了,性能也更好。
修改后的代码:
void BoxMesh(float width, float height, float OLwidth) { MeshFilter meshFilter = GetComponent<MeshFilter>(); Mesh combinedMesh = new Mesh(); meshFilter.mesh = combinedMesh; // -------------------------- // 合并所有顶点:内部4个 + 轮廓4个 = 8个顶点 // -------------------------- Vector3[] allVertices = new Vector3[8]; // 内部顶点(索引0-3) allVertices[0] = new Vector3(0, 0, 0); allVertices[1] = new Vector3(0, height, 0); allVertices[2] = new Vector3(width, height, 0); allVertices[3] = new Vector3(width, 0, 0); // 轮廓顶点(索引4-7) allVertices[4] = new Vector3(-OLwidth, -OLwidth, 0); allVertices[5] = new Vector3(-OLwidth, height + OLwidth, 0); allVertices[6] = new Vector3(width + OLwidth, height + OLwidth, 0); allVertices[7] = new Vector3(width + OLwidth, -OLwidth, 0); // -------------------------- // 设置两个SubMesh的三角形 // -------------------------- combinedMesh.subMeshCount = 2; // SubMesh 0:内部Mesh的三角形 combinedMesh.SetTriangles(new int[6] { 0, 1, 3, 1, 2, 3 }, 0); // SubMesh 1:轮廓Mesh的三角形(调整顺序避免背面剔除) combinedMesh.SetTriangles(new int[6] { 4, 7, 5, 5, 7, 6 }, 1); // -------------------------- // 设置法线和UV(这里简化复用,也可以分别自定义) // -------------------------- Vector3[] allNormals = new Vector3[8]; Vector2[] allUVs = new Vector2[8]; for (int i = 0; i < 8; i++) { allNormals[i] = -Vector3.forward; if (i < 4) { allUVs[i] = new Vector2(i % 2, i / 2); } else { allUVs[i] = new Vector2((i - 4) % 2, (i - 4) / 2); } } combinedMesh.normals = allNormals; combinedMesh.uv = allUVs; // -------------------------- // 碰撞体设置 // -------------------------- PolygonCollider2D polyCollider = GetComponent<PolygonCollider2D>(); polyCollider.pathCount = 1; Vector2[] colliderPath = new Vector2[4] { new Vector2(-OLwidth, -OLwidth), new Vector2(-OLwidth, height + OLwidth), new Vector2(width + OLwidth, height + OLwidth), new Vector2(width + OLwidth, -OLwidth) }; polyCollider.SetPath(0, colliderPath); // -------------------------- // 分配材质:SubMesh 0对应第一个材质,SubMesh1对应第二个 // -------------------------- MeshRenderer meshRenderer = GetComponent<MeshRenderer>(); // meshRenderer.materials = new Material[] { yourInnerMaterial, yourOutlineMaterial }; }
注意事项:
- 方案一的好处是两个Mesh完全独立,后续想单独调整位置、缩放都很方便;方案二更适合性能要求高的场景,因为减少了Draw Call。
- 轮廓Mesh的三角形顺序一定要注意,Unity默认会剔除背面(逆时针的面),所以我调整了三角形的顶点顺序,确保轮廓Mesh的正面朝向相机,不然可能会看不到轮廓。
内容的提问来源于stack exchange,提问作者Tom Ryan
相关产品推荐
相关产品推荐

