【问题标题】:Unity - How to set the color of an individual face when clicking a mesh?Unity - 单击网格时如何设置单个面的颜色?
【发布时间】:2019-08-09 15:42:28
【问题描述】:

昨天 Stack Overflow 上的其他人帮我确定了how to recolor a mesh triangle to red by clicking on it,它工作得很好,唯一的问题是重新着色的 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");
            }
        }
    }
}

【问题讨论】:

  • 正如所说的着色是由顶点完成的,而不是三角形或面。唯一的解决方案是确保三角形不共享顶点......它可以工作,例如使用立方体图元,因为它使用网格,每个三角形都有单独的顶点..其他然后例如我们示例中的球体;)
  • 您需要检查其他面(在这种情况下为 tris)是否引用相同的顶点。如果是这样,复制顶点以创建一个独立的面。否则相邻面将插入顶点颜色。

标签: unity3d


【解决方案1】:

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

解决方案的想法是:

  • 为命中三角形的每个顶点检查它是否被其他三角形使用
  • 如果是这样,则复制其位置以创建新的分离顶点
  • 更新三角形以使用新创建的顶点索引
  • (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");
        }
    }
}


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

【讨论】:

  • 完美!非常感谢您清楚地说明如何复制共享顶点。因此,我对网格数据的理解大大增加。再次感谢!
  • 抱歉,还有一个问题 - 如果我想为 UV 着色,我是否只需要遵循与顶点相同的模式并在像这样添加它们时复制这些坐标? ... if (vert1Occurrences &gt; 1) { newVertices.Add(vert1Pos); newColors.Add(new Color()); newUVs.Add(new Vector2(vert1Pos.x, vert1Pos.y)); vert1Index = newVertices.Count - 1; } ... 我想我很接近了,但可能缺少一些东西。
  • 啊,没关系,我想通了!只需使用相同的顶点索引从原始的 mesh.uv 数组复制位置,然后以与使用原始位置的顶点演示的完全相同的方式将它们添加到新的 UV 列表中。最后,将其分配回网格。谢谢一百万!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-08
相关资源
最近更新 更多