Unity - 使用 C# 在网格上投影纹理(无着色器)

Unity - Project a texture on a mesh using C# (No Shaders)

我正在尝试仅使用 C# 在简单的立方体 meshFilter 上投影纹理,但我很难理解该怎么做。我几乎让它在 X 轴旋转上工作并且 Y/Z 有很多不良扭曲。基本上,我会在相机的 position/rotation 发生变化时更新 UV,这是我的代码:

[ExecuteInEditMode]
public class ObjectEditor : MonoBehaviour {

    public GameObject Model;

    public void UpdateTexture(Camera camera) {
        MeshFilter[] mesheFilters = Model.GetComponentsInChildren<MeshFilter>();
        foreach (MeshFilter meshFilter in mesheFilters) {
            int size = meshFilter.sharedMesh.vertices.Length;
            Vector2[] uvs = new Vector2[size];
            for (int i = 0; i < size; i++) {
                uvs[i] = vertexToUVPosition(camera, meshFilter, i);
            }
            meshFilter.sharedMesh.SetUVs(0, uvs);
        }
    }

    private Vector2 vertexToUVPosition(Camera camera, MeshFilter meshFilter, int index) {
        Vector3 vertex = meshFilter.sharedMesh.vertices[index];
        Matrix4x4 VP = camera.projectionMatrix * camera.worldToCameraMatrix;
        Vector4 worldPos = new Vector4(vertex.x, vertex.y, vertex.z, 1f);
        Vector4 clipPos = VP * worldPos;
        clipPos *= 1f / clipPos.w;
        return camera.WorldToScreenPoint(clipPos);
    }
}

关于投影的一切都发生在 vertexToUVPosition 中。 这是我现在拥有的(投影纹理是一个简单的 black/white 棋盘格):

有投影经验的人可以向我解释我做错了什么,并提供一个可以正常工作的示例 C# 代码吗?谢谢。

我找到了一个完全解决问题的解决方案,但我猜它在数学上是不正确的,因为我不太了解矩阵和投影。然而,对于任何想在没有任何经验的情况下做类似事情的人来说,这是一个很好的起点。

在显示代码之前,您应该设置一些东西,以便更容易调试潜在问题:

  1. 确保您的纹理处于钳制模式,这样可以更容易地查看投影是否正常工作。
  2. 将您的对象定位在 (0,0,0)。
  3. 将您的相机定位在 (0,0,0) 并确保它处于透视模式。
  4. 在正交模式下使用另一个相机查看投影纹理并验证它是否正确显示。

算法:

// Calculate the VP matrix based on the Perspective Camera
VP = camera.projectionMatrix * camera.worldToCameraMatrix
foreach vertex in mesh
    // Replace the "w" component by 1.
    worldPosition = new Vector4(vertex.x, vertex.y, vertex.z, 1f);
    clipPosition = VP * worldPosition;
    // Small correction on Y axis (maybe someone can explain why I need this?).
    clipPosition.Scale(new Vector3(1, 0.5f, 1));
    // Use the clipPosition as UV coordinates for that vertex.
    ...

我的实现:

[ExecuteInEditMode]
public class ObjectEditor : MonoBehaviour {

    public GameObject Model;

    public void UpdateTexture(Camera camera) {
        Matrix4x4 vp = camera.projectionMatrix * camera.worldToCameraMatrix;
        MeshFilter[] mesheFilters = Model.GetComponentsInChildren<MeshFilter>();
        foreach (MeshFilter meshFilter in mesheFilters) {
            int size = meshFilter.sharedMesh.vertices.Length;
            Vector2[] uvs = new Vector2[size];
            for (int i = 0; i < size; i++) {
                uvs[i] = vertexToUVPosition(vp, meshFilter, i);
            }
            meshFilter.sharedMesh.SetUVs(0, uvs);
        }
    }

    private Vector2 vertexToUVPosition(Matrix4x4 vp, MeshFilter meshFilter, int index) {
        Vector3 vertex = meshFilter.sharedMesh.vertices[index];
        Vector4 worldPos = new Vector4(vertex.x, vertex.y, vertex.z, 1f);
        Vector4 clipPos = vp * worldPos;
        clipPos.Scale(new Vector3(1, 0.5f, 1));
        return clipPos;
    }
}

结果 - 在两个轴 (40,-45,0) 上进行旋转测试:

正交相机视图: