You can reproject your camera image onto a mesh either by setting the mesh’s texture to the camera feed and setting the vertex UVs using a script or by reprojecting inside of the pixel shader.
The former technique can be extended towards the construction of a persistent texture atlas.
The latter technique has pixel-perfect accuracy, but can only ever wok for what is currently visible to the user’s webcam.
MeshReproject.cs
Attach this to the mesh you’d like to have reprojected UVs:
using UnityEngine;
public class MeshReproject : MonoBehaviour {
public Camera ProjectionPerspective;
public float width = 0.2f;
public float height = 0.2f;
Mesh mesh;
Vector3[] vertices;
Vector2[] uvs;
void Start() {
ProjectionPerspective.enabled = false;
mesh = GetComponent<MeshFilter>().mesh;
mesh.MarkDynamic();
}
void Update() {
ProjectionPerspective.projectionMatrix = ManualProjectionMatrix(-width, width, -height, height, ProjectionPerspective.nearClipPlane, ProjectionPerspective.farClipPlane);
vertices = mesh.vertices;
uvs = new Vector2[vertices.Length];
Vector2 ScreenPosition;
for (int i = 0; i < uvs.Length; i++) {
ScreenPosition = ProjectionPerspective.WorldToViewportPoint(transform.TransformPoint(vertices[i]));
uvs[i].Set(ScreenPosition.x, ScreenPosition.y);
}
mesh.uv = uvs;
}
static Matrix4x4 ManualProjectionMatrix(float left, float right, float bottom, float top, float near, float far) {
float x = 2.0F * near / (right - left);
float y = 2.0F * near / (top - bottom);
float a = (right + left) / (right - left);
float b = (top + bottom) / (top - bottom);
float c = -(far + near) / (far - near);
float d = -(2.0F * far * near) / (far - near);
float e = -1.0F;
Matrix4x4 m = new Matrix4x4();
m[0, 0] = x;
m[0, 1] = 0;
m[0, 2] = a;
m[0, 3] = 0;
m[1, 0] = 0;
m[1, 1] = y;
m[1, 2] = b;
m[1, 3] = 0;
m[2, 0] = 0;
m[2, 1] = 0;
m[2, 2] = c;
m[2, 3] = d;
m[3, 0] = 0;
m[3, 1] = 0;
m[3, 2] = e;
m[3, 3] = 0;
return m;
}
}
GPU Reprojection
First set the Camera Projection Matrix variable in C#:
void LateUpdate() {
Shader.SetGlobalMatrix("_camProjection", FrontCam.projectionMatrix * FrontCam.worldToCameraMatrix);
}
Then calculate the UVs inside of the mesh’s material’s shader:
uniform float4x4 _camProjection;
float2 WorldToViewport(float4x4 camVP, float3 worldPoint)
{
float2 result;
result.x = camVP[0][0] * worldPoint.x + camVP[0][1] * worldPoint.y + camVP[0][2] * worldPoint.z + camVP[0][3];
result.y = camVP[1][0] * worldPoint.x + camVP[1][1] * worldPoint.y + camVP[1][2] * worldPoint.z + camVP[1][3];
float num = camVP[3][0] * worldPoint.x + camVP[3][1] * worldPoint.y + camVP[3][2] * worldPoint.z + camVP[3][3];
num = 1.0 / num;
result.x *= num;
result.y *= num;
result.x = (result.x * 0.5 + 0.5);
result.y = (result.y * 0.5 + 0.5);
return result;
}
Once you have this, you can sample your texture using:
uv = WorldToViewport(_camProjection, i.worldPos);
(Make sure worldPos is being passed in through i)