First, we can make a shader that exposes a variable for the view-projection matrix of the camera we want to project "into"...
struct appdata
{
float4 vertex : POSITION;
};
struct v2f
{
float3 worldPosition : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
// Here we've defined a new variable we can put
// our second camera's projection info into.
float4x4 _CameraMatrix;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.worldPosition = mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// Map world position into camera's projected coordinates.
float4 projected = mul(_CameraMatrix, float4(i.worldPosition, 1.0f));
// Perform perspective divide and adjust range
// from NDC's -1...1 to UV space's 0...1
float2 uv = (projected.xy / projected.w) * 0.5f + 0.5f;
// Sample a texture with this UV to prove it works.
fixed4 col = tex2D(_MainTex, uv);
return col;
}
Next, we attach a script to that camera to tell it to upload its matrix into that variable each frame before rendering time:
[RequireComponent(typeof(Camera))]
public class PerspectiveToShader : MonoBehaviour {
public Material materialToModify;
public string matrixVariableName;
new Camera camera;
int matrixVariable;
void OnEnable () {
matrixVariable = Shader.PropertyToID(matrixVariableName);
camera = GetComponent<Camera>();
}
void LateUpdate () {
var viewProjection = camera.nonJitteredProjectionMatrix * transform.worldToLocalMatrix;
materialToModify.SetMatrix(matrixVariable, viewProjection);
}
}
If you need this across many materials, you can instead set it with Shader.SetGlobalMatrix instead of piping it into each material individually.