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 acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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