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# 代码吗?谢谢。
我找到了一个完全解决问题的解决方案,但我猜它在数学上是不正确的,因为我不太了解矩阵和投影。然而,对于任何想在没有任何经验的情况下做类似事情的人来说,这是一个很好的起点。
在显示代码之前,您应该设置一些东西,以便更容易调试潜在问题:
- 确保您的纹理处于钳制模式,这样可以更容易地查看投影是否正常工作。
- 将您的对象定位在 (0,0,0)。
- 将您的相机定位在 (0,0,0) 并确保它处于透视模式。
- 在正交模式下使用另一个相机查看投影纹理并验证它是否正确显示。
算法:
// 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) 上进行旋转测试:
正交相机视图:
我正在尝试仅使用 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# 代码吗?谢谢。
我找到了一个完全解决问题的解决方案,但我猜它在数学上是不正确的,因为我不太了解矩阵和投影。然而,对于任何想在没有任何经验的情况下做类似事情的人来说,这是一个很好的起点。
在显示代码之前,您应该设置一些东西,以便更容易调试潜在问题:
- 确保您的纹理处于钳制模式,这样可以更容易地查看投影是否正常工作。
- 将您的对象定位在 (0,0,0)。
- 将您的相机定位在 (0,0,0) 并确保它处于透视模式。
- 在正交模式下使用另一个相机查看投影纹理并验证它是否正确显示。
算法:
// 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) 上进行旋转测试:
正交相机视图: