0
\$\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\$
  • 1
    \$\begingroup\$ There is no such thing as "camera 1" and "camera 2" in a shader. There is only the current camera. \$\endgroup\$ – Draco18s Jan 3 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 Jan 4 at 2:22
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\$
  • \$\begingroup\$ Thanks DMGregory. Thant's exactly what i'm looking for. May god grant you good health, knowledge, guidance, wisdom and success. \$\endgroup\$ – The Oathman Jan 4 at 10:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.