You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity射线检测实现选中三角面及相邻面差异化着色问题排查

问题描述

我在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");
        }
    }
}
问题分析与解决方案

一、材质设置问题

首先检查材质是否支持顶点颜色:

  • 使用Standard Shader时,需在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 21:35:14