1
\$\begingroup\$

I'm trying to transform a point in fragment shader from world space to screen coordinate of a second camera. Not the main one.

I think I know how to achieve that to the main camera, but I'm struggling to find an easy way to transform a point in world space to screen coordinate of camera2.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ There is no such thing as "camera 1" and "camera 2" in a shader. There is only the current camera. \$\endgroup\$ Commented Jan 3, 2019 at 23:12
  • \$\begingroup\$ @Draco18s that's not necessarily a barrier, it just means it's up to us to define what we mean by "camera 2" in how we write our shaders and C# scripts. ;) \$\endgroup\$
    – DMGregory
    Commented Jan 4, 2019 at 2:22

1 Answer 1

1
\$\begingroup\$

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.

\$\endgroup\$
1
  • \$\begingroup\$ Thanks DMGregory. Thant's exactly what i'm looking for. May god grant you good health, knowledge, guidance, wisdom and success. \$\endgroup\$ Commented Jan 4, 2019 at 10:02

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .