Unity射线检测实现选中三角面及相邻面差异化着色问题排查
问题描述
我在Unity中尝试通过射线检测实现以下功能:为选中的三角面设置一种颜色,为其相邻的三角面设置另一种颜色,但运行时所有三角面都被染成同一种颜色。想知道是代码实现存在问题,还是需要调整Unity的相关设置(如材质等)。以下是我编写的代码,恳请各位提供帮助!

using System.Linq; using UnityEngine; using UnityEngine.UI; using UnityEngine.SceneManagement; using System.Collections.Generic; public class MyRayDraw : MonoBehaviour { public GameObject spherePrefab; // Better to reference those already in the Inspector [SerializeField] private MeshFilter meshFilter; [SerializeField] private MeshRenderer meshRenderer; [SerializeField] private MeshCollider meshCollider; //colors [SerializeField] private Material startMaterial; [SerializeField] private Material newMaterial; Renderer objrenderer; private Mesh mesh; private void Awake() { if (!meshFilter) meshFilter = GetComponent<MeshFilter>(); if (!meshRenderer) meshRenderer = GetComponent<MeshRenderer>(); if (!meshCollider) meshCollider = GetComponent<MeshCollider>(); mesh = meshFilter.mesh; // create new colors array where the colors will be created var colors = new Color[mesh.vertices.Length]; for(var i = 0; i < colors.Length; i++) { colors[i] = Color.black; } // assign the array of colors to the Mesh. mesh.colors = colors; } private void Update() { if (!Input.GetMouseButtonDown(0)) return; var ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit)) { Debug.Log(hit.triangleIndex); //cube.transform.position = hit.point; // Get current vertices, triangles, uvs and colors var vertices = mesh.vertices; var triangles = mesh.triangles; //var uv = _mesh.uv; var colors = mesh.colors; // Get the vert indices for this triangle var vertIndex_1 = triangles[hit.triangleIndex * 3 + 0]; var vertIndex_2 = triangles[hit.triangleIndex * 3 + 1]; var vertIndex_3 = triangles[hit.triangleIndex * 3 + 2]; // Get the positions for the vertices var vertPos_1 = vertices[vertIndex_1]; var vertPos_2 = vertices[vertIndex_2]; var vertPos_3 = vertices[vertIndex_3]; // Now for all three vertices we first check if any other triangle if using it // by simply count how often the indices are used in the triangles list var verticesOccur_1 = 0; var verticesOccur_2 = 0; var verticesOccur_3 = 0; for (var i = 0; i < triangles.Length; i++) { if (triangles[i] == vertIndex_1) verticesOccur_1++; if (triangles[i] == vertIndex_2) verticesOccur_2++; if (triangles[i] == vertIndex_3) verticesOccur_3++; } // Create copied Lists so we can dynamically add entries var newVertices = vertices.ToList(); var newTriangles = triangles.ToList(); //var newUV = uv.ToList(); var newColors = colors.ToList(); // Now we check if the vertices are used by more than one triangle // If so, we need to split the triangle into three new ones // and add the new vertices to the vertices list if (verticesOccur_1 > 1) { // Add the new vertices to the vertices list newVertices.Add(vertPos_1); newVertices.Add(vertPos_2); newVertices.Add(vertPos_3); // Add the new triangles to the triangles list newTriangles.Add(vertIndex_1); newTriangles.Add(newVertices.Count - 3); newTriangles.Add(newVertices.Count - 1); newTriangles.Add(newVertices.Count - 3); newTriangles.Add(vertIndex_2); newTriangles.Add(newVertices.Count - 2); newTriangles.Add(newVertices.Count - 1); newTriangles.Add(newVertices.Count - 2); newTriangles.Add(vertIndex_3); // Add the new colors to the colors list newColors.Add(Color.black); newColors.Add(Color.black); newColors.Add(Color.black); // Update the triangle index to the new triangle triangles[hit.triangleIndex] = newTriangles.Count / 3 - 1; } if (verticesOccur_2 > 1) { // Add the new vertices to the vertices list newVertices.Add(vertPos_1); newVertices.Add(vertPos_2); newVertices.Add(vertPos_3); // Add the new triangles to the triangles list newTriangles.Add(vertIndex_1); newTriangles.Add(newVertices.Count - 3); newTriangles.Add(newVertices.Count - 1); newTriangles.Add(newVertices.Count - 3); newTriangles.Add(vertIndex_2); newTriangles.Add(newVertices.Count - 2); newTriangles.Add(newVertices.Count - 1); newTriangles.Add(newVertices.Count - 2); newTriangles.Add(vertIndex_3); // Add the new colors to the colors list newColors.Add(Color.black); newColors.Add(Color.black); newColors.Add(Color.black); // Update the triangle index to the new triangle triangles[hit.triangleIndex] = newTriangles.Count / 3 - 1; } if (verticesOccur_3 > 1) { // Add the new vertices to the vertices list newVertices.Add(vertPos_1); newVertices.Add(vertPos_2); newVertices.Add(vertPos_3); // Add the new triangles to the triangles list newTriangles.Add(vertIndex_1); newTriangles.Add(newVertices.Count - 3); newTriangles.Add(newVertices.Count - 1); newTriangles.Add(newVertices.Count - 3); newTriangles.Add(vertIndex_2); newTriangles.Add(newVertices.Count - 2); newTriangles.Add(newVertices.Count - 1); newTriangles.Add(newVertices.Count - 2); newTriangles.Add(vertIndex_3); // Add the new colors to the colors list newColors.Add(Color.white); newColors.Add(Color.green); newColors.Add(Color.yellow); // Update the triangle index to the new triangle triangles[hit.triangleIndex] = newTriangles.Count / 3 - 1; } // Update the mesh with the new data mesh.vertices = newVertices.ToArray(); mesh.triangles = newTriangles.ToArray(); //mesh.uv = newUV.ToArray(); mesh.colors = newColors.ToArray(); // Update the indices of the hit triangle to use the (eventually) new // vertices instead triangles[hit.triangleIndex * 3 + 0] = vertIndex_1; triangles[hit.triangleIndex * 3 + 1] = vertIndex_2; triangles[hit.triangleIndex * 3 + 2] = vertIndex_3; // Now we can simply add the new triangle newTriangles.Add(vertIndex_1); newTriangles.Add(vertIndex_2); newTriangles.Add(vertIndex_3); // color these vertices newColors[vertIndex_1] = Color.red; newColors[vertIndex_2] = Color.red; newColors[vertIndex_3] = Color.red; //color the mesh mesh.colors = newColors.ToArray(); // Recalculate the normals mesh.RecalculateNormals(); } else { //reset the colors back to original var colors = new Color[mesh.vertices.Length]; for(var i = 0; i < colors.Length; i++) { colors[i] = Color.black; } mesh.colors = colors; Debug.Log("No hit"); } } }
问题分析与解决方案
一、材质设置问题
首先检查材质是否支持顶点颜色:
- 使用
StandardShader时,需在Inspector面板开启Vertex Colors选项;也可直接使用Unlit/Color这类原生支持顶点颜色的基础Shader; - 自定义Shader必须在片元着色器中采样
COLOR语义的顶点颜色,并将其融入最终颜色输出,示例代码片段:fixed4 frag (v2f i) : SV_Target { fixed4 col = i.color; // 其他颜色计算逻辑 return col; }
二、代码逻辑问题
你的代码存在多处核心逻辑错误,导致颜色无法正确应用:
1. 顶点拆分逻辑完全错误
当顶点被多个三角面共享时,应为选中三角面复制专属顶点,而非添加三个顶点后拆分出三个新三角面。当前代码会产生大量冗余顶点,且未正确替换原三角面的顶点索引,导致颜色修改影响所有共享该顶点的三角面。
2. 颜色赋值顺序混乱
代码先更新mesh.colors,之后修改newColors再赋值,中间对triangles数组的修改基于原数组,未关联到newTriangles,导致颜色应用逻辑混乱。
3. 未实现相邻三角面检测
代码完全没有处理“相邻三角面染色”的逻辑,仅对选中三角面的顶点染色,这是功能未实现的核心原因。
修正后的核心逻辑示例
private void Update() { if (!Input.GetMouseButtonDown(0)) return; var ray = Camera.main.ScreenPointToRay(Input.mousePosition); if (!Physics.Raycast(ray, out RaycastHit hit)) { // 重置所有顶点颜色为黑色 var colors = new Color[mesh.vertices.Length]; System.Array.Fill(colors, Color.black); mesh.colors = colors; return; } // 1. 获取原Mesh数据 var vertices = mesh.vertices.ToList(); var triangles = mesh.triangles.ToList(); var colors = mesh.colors.ToList(); // 2. 处理选中三角面的顶点拆分(避免共享顶点影响其他面) int selectedTriIndex = hit.triangleIndex; int v1 = triangles[selectedTriIndex * 3]; int v2 = triangles[selectedTriIndex * 3 + 1]; int v3 = triangles[selectedTriIndex * 3 + 2]; // 为选中三角面创建专属顶点 int newV1 = vertices.Count; int newV2 = vertices.Count + 1; int newV3 = vertices.Count + 2; vertices.Add(vertices[v1]); vertices.Add(vertices[v2]); vertices.Add(vertices[v3]); colors.Add(Color.red); // 选中面颜色 colors.Add(Color.red); colors.Add(Color.red); // 替换原三角面的顶点索引为新顶点 triangles[selectedTriIndex * 3] = newV1; triangles[selectedTriIndex * 3 + 1] = newV2; triangles[selectedTriIndex * 3 + 2] = newV3; // 3. 检测并标记相邻三角面 HashSet<int> adjacentTriangles = new HashSet<int>(); // 遍历所有三角面,检查是否与选中面共享任意一个原顶点 for (int i = 0; i < triangles.Count; i += 3) { int triIdx = i / 3; if (triIdx == selectedTriIndex) continue; int tv1 = triangles[i]; int tv2 = triangles[i + 1]; int tv3 = triangles[i + 2]; if (tv1 == v1 || tv1 == v2 || tv1 == v3 || tv2 == v1 || tv2 == v2 || tv2 == v3 || tv3 == v1 || tv3 == v2 || tv3 == v3) { adjacentTriangles.Add(triIdx); } } // 为相邻三角面染色 foreach (int triIdx in adjacentTriangles) { colors[triangles[triIdx * 3]] = Color.blue; colors[triangles[triIdx * 3 + 1]] = Color.blue; colors[triangles[triIdx * 3 + 2]] = Color.blue; } // 4. 更新Mesh数据 mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.colors = colors.ToArray(); mesh.RecalculateNormals(); // 同步更新碰撞体Mesh meshCollider.sharedMesh = mesh; }
三、额外注意事项
- 修改Mesh后必须同步更新
MeshCollider的sharedMesh,否则射线检测会基于旧Mesh数据; - 频繁修改Mesh会产生性能开销,建议仅在必要时操作,或使用对象池等优化手段;
- 静态模型可提前烘焙顶点数据,减少运行时计算量。
内容的提问来源于stack exchange,提问作者newCoderMe
相关产品推荐
相关产品推荐

