Unity-单击网格时如何设置单个面的颜色?

Mir*_*Mir 1 unity-game-engine

昨天,Stack Overflow上的其他人帮助我确定了如何通过单击将网格三角形重新着色为红色,效果很好,唯一的问题是,重新着色的3个顶点在三角形之间共享。这导致看起来很模糊的着色。我真的希望有一种方法可以只对一张脸进行着色(如果可以的话,可以正常着色)。

我已将以下脚本附加到我的网格,该脚本使用射线投射确定表面坐标并在那里平移绿色立方体。下面的gif可以更好地说明此问题。

再次感谢您对此的任何帮助或见解。谢谢!

在此处输入图片说明

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MyRayDraw : MonoBehaviour
{
    public GameObject cube;
    private MeshRenderer meshRenderer;
    Mesh mesh;
    Vector3[] vertices;
    Color[] colorArray;

    private void Start()
    {
        mesh = transform.GetComponent<MeshFilter>().mesh;
        vertices = mesh.vertices;

        colorArray = new Color[vertices.Length];
        for (int k = 0; k < vertices.Length; k++)
        {
            colorArray[k] = Color.white;
        }
        mesh.colors = colorArray;
    }

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

            if (Physics.Raycast(ray, out RaycastHit hit))
            {
                Snap(hit.point); // Moves the green cube

                int[] triangles = mesh.triangles;
                var vertIndex1 = triangles[hit.triangleIndex * 3 + 0];
                var vertIndex2 = triangles[hit.triangleIndex * 3 + 1];
                var vertIndex3 = triangles[hit.triangleIndex * 3 + 2];

                colorArray[vertIndex1] = Color.red;
                colorArray[vertIndex2] = Color.red;
                colorArray[vertIndex3] = Color.red;

                mesh.colors = colorArray;                
            }
            else
            {
                Debug.Log("no hit");
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

der*_*ugo 5

正如您所说,问题是顶点在三角形之间共享,但着色始终基于顶点。

解决方案的想法是:

  • 对于命中三角形的每个顶点,检查是否被其他三角形使用
  • 如果是这样,请复制其位置以创建一个新的分离的顶点
  • 更新三角形以使用新创建的顶点索引
  • (evtl.)用于RecalculateNormals使三角形面向外部,而不必关心提供的顶点的顺序
using System.Linq;
using UnityEngine;

public class MyRayDraw : MonoBehaviour
{
    public GameObject cube;

    // Better to reference those already in the Inspector
    [SerializeField] private MeshFilter meshFilter;
    [SerializeField] private MeshRenderer meshRenderer;
    [SerializeField] private MeshCollider meshCollider;

    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 k = 0; k < colors.Length; k++)
        {
            colors[k] = Color.white;
        }
        _mesh.colors = colors;
    }

    private void Update()
    {
        if (!Input.GetMouseButtonDown(0)) return;

        var ray = Camera.main.ScreenPointToRay(Input.mousePosition);

        if (Physics.Raycast(ray, out var hit))
        {
            Debug.Log(hit.triangleIndex);
            //cube.transform.position = hit.point;

            // Get current vertices, triangles and colors
            var vertices = _mesh.vertices;
            var triangles = _mesh.triangles;
            var colors = _mesh.colors;

            // Get the vert indices for this triangle
            var vert1Index = triangles[hit.triangleIndex * 3 + 0];
            var vert2Index = triangles[hit.triangleIndex * 3 + 1];
            var vert3Index = triangles[hit.triangleIndex * 3 + 2];

            // Get the positions for the vertices
            var vert1Pos = vertices[vert1Index];
            var vert2Pos = vertices[vert2Index];
            var vert3Pos = vertices[vert3Index];

            // 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 vert1Occurrences = 0;
            var vert2Occurrences = 0;
            var vert3Occurrences = 0;
            foreach (var index in triangles)
            {
                if (index == vert1Index) vert1Occurrences++;
                else if (index == vert2Index) vert2Occurrences++;
                else if (index == vert3Index) vert3Occurrences++;
            }

            // Create copied Lists so we can dynamically add entries
            var newVertices = vertices.ToList();
            var newColors = colors.ToList();

            // Now if a vertex is shared we need to add a new individual vertex
            // and also an according entry for the color array
            // and update the vertex index
            // otherwise we will simply use the vertex we already have
            if (vert1Occurrences > 1)
            {
                newVertices.Add(vert1Pos);
                newColors.Add(new Color());
                vert1Index = newVertices.Count - 1;
            }

            if (vert2Occurrences > 1)
            {
                newVertices.Add(vert2Pos);
                newColors.Add(new Color());
                vert2Index = newVertices.Count - 1;
            }

            if (vert3Occurrences > 1)
            {
                newVertices.Add(vert3Pos);
                newColors.Add(new Color());
                vert3Index = newVertices.Count - 1;
            }

            // Update the indices of the hit triangle to use the (eventually) new
            // vertices instead
            triangles[hit.triangleIndex * 3 + 0] = vert1Index;
            triangles[hit.triangleIndex * 3 + 1] = vert2Index;
            triangles[hit.triangleIndex * 3 + 2] = vert3Index;

            // color these vertices
            newColors[vert1Index] = Color.red;
            newColors[vert2Index] = Color.red;
            newColors[vert3Index] = Color.red;

            // write everything back
            _mesh.vertices = newVertices.ToArray();
            _mesh.triangles = triangles;
            _mesh.colors = newColors.ToArray();

            _mesh.RecalculateNormals();
        }
        else
        {
            Debug.Log("no hit");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明


但是请注意,这仅适用于简单的着色,但不适用于具有UV贴图的复杂纹理。mesh.uv如果使用UV映射纹理,则还必须更新。