Search Unity

Get camera screen position from a different Camera

Discussion in 'Shaders' started by danybittel, Jan 23, 2014.

  1. danybittel

    danybittel

    Joined:
    Aug 1, 2013
    Posts:
    68
    Hi

    I'm tryng to do faked reflections (rendering with a second camera)

    I would need to know where to sample in the renderTexture inside my reflection shader is.

    I don't want to just map it (mirror reflection) as I need normal maps.

    So basically, I need to know where my shaded fragment is on the screen of my reflection camera. (X and Y coordinate viewed from the reflection camera)

    I can write the "worldToCameraMatrix" into a float4x4 and use that in the shader. But this gives me just the transformations, it ignores the Field of View of the camera.

    I found "projectionMatrix".. is this what I need? How do I combine these Matrices?
     
  2. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    Unity actually already supplies the screen space coordinates (if you want them.) There is an example at the surface shader example page:

    Code (csharp):
    1.  
    2. Shader "Example/ScreenPos" {
    3.     Properties {
    4.       _MainTex ("Texture", 2D) = "white" {}
    5.       _Detail ("Detail", 2D) = "gray" {}
    6.     }
    7.     SubShader {
    8.       Tags { "RenderType" = "Opaque" }
    9.       CGPROGRAM
    10.       #pragma surface surf Lambert
    11.       struct Input {
    12.           float2 uv_MainTex;
    13.           float4 screenPos;
    14.       };
    15.       sampler2D _MainTex;
    16.       sampler2D _Detail;
    17.       void surf (Input IN, inout SurfaceOutput o) {
    18.           o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
    19.           float2 screenUV = IN.screenPos.xy / IN.screenPos.w;
    20.           screenUV *= float2(8,6);
    21.           o.Albedo *= tex2D (_Detail, screenUV).rgb * 2;
    22.       }
    23.       ENDCG
    24.     }
    25.     Fallback "Diffuse"
    26.   }
    27.  
    For fragment shaders there are also many examples out there. You take a copy from the final position in clip space and pass it to the pixel shader. There you do the projective divide. (Same as above, screenPos.xy / screenPos.w) Then you need to make a final adjustment, because the clip space position ranges from -1 to 1, while texture coordinates range from 0 to 1.
     
  3. Slin

    Slin

    Joined:
    Jun 27, 2010
    Posts:
    85
    To add to the previous answer:
    You need the model view projection matrix of the reflection camera and do the perspective divide with the result, something like
    float3 texcoord = mul(modelviewproj, inpos).xyw; //can be done in vertex or fragment shader
    texcoord.xy /= texcoord.w; //projective divide, should be done in fragment shader
    texcoord.xy = texcoord.xy*0.5+0.5; //adjust from -1 - 1 range to 0 - 1 range

    The last transformation can also be done before the divide, ideally as part of the matrix, calculated on the CPU.
    Also, there is a tex2Dproj function that does the projective divide for you.
     
  4. danybittel

    danybittel

    Joined:
    Aug 1, 2013
    Posts:
    68
    Hi.. thanks for the answers.
    jvo3dc. I need the screen space coordinates from the camera that is not rendering. (Camera projection)

    Slin. Excellent, I got it to work. Had some troubles getting the right matrix from the camera (needed to multiply projectionMatrix * worldToCameraMatrix). Now it works, thanks a lot. (And learned something along the way)
     
  5. sewy

    sewy

    Joined:
    Oct 11, 2015
    Posts:
    150
    For others trying to make it work, "inpos" in Slin's answer is in world space
    material.SetMatrix("_RenderCameraMVP", cam.projectionMatrix * cam.worldToCameraMatrix); //in script
    o.screenPos = mul(_RenderCameraMVP, mul(unity_ObjectToWorld, v.vertex)); //in vertex shader

    float2 screenPos = i.screenPos / i.screenPos.w; //in fragment shader
    screenPos = screenPos * 0.5 + 0.5; //adjust from -1 - 1 range to 0 - 1 range //in fragment shader
     
    John_Leorid likes this.
  6. John_Leorid

    John_Leorid

    Joined:
    Nov 5, 2012
    Posts:
    650
    Absolute life saver, was struggling with this for 72h - switching matrices like underwear, trying out everything and nothing worked.
    After countless reading I found this post, added the division in shadergraph and .. it just works, OMG just wanted to give you a big THANK YOU.

    I think that's the "division you can't solve by just multiplying the matrix" and maybe the reason why Matrix4x4.MultiplyPoint() isn't the same as Matrix4x4.MultiplyPoint3x4(), as in the C# documentation it is noted, that MultiplyPoint3x4 is unable to calculate a projection matrix.
     
    sewy likes this.