Search Unity

How are the minimum and maximum coordinates of ComputeScreenPos determined

Discussion in 'Shaders' started by TryToDreamBig, Jul 4, 2017.

  1. TryToDreamBig

    TryToDreamBig

    Joined:
    Jul 4, 2017
    Posts:
    2
    Hello everyone!

    My assumption was that ComputeScreenPos(o.vertex).x would be a value between 0 and 1, 0 being the left most pixel of the screen and 1 being the right most pixel of the screen.

    It appears that this is incorrect, as the following shader appears to only put a gradient on about one-tenth of the left side of the screen (in perspective camera mode). In orthographic camera mode this shader works as expected, with a gradient from left to right.

    What are the minimum and maximum XYZW coords of ComputeScreenPos and what parameters can we use to normalize at least the XY coords?

    Thanks for reading!

    Code (CSharp):
    1. struct appdata
    2.             {
    3.                 float4 vertex : POSITION;
    4.                 float2 uv : TEXCOORD0;
    5.             };
    6.  
    7.             struct v2f
    8.             {
    9.                 float2 uv : TEXCOORD0;
    10.                 float4 vertex : SV_POSITION;
    11.                 float4 screenPos : TEXCOORD1;
    12.             };
    13.          
    14.             v2f vert (appdata v)
    15.             {
    16.                 v2f o;
    17.                 o.vertex = UnityObjectToClipPos(v.vertex);
    18.                 o.uv = v.uv;
    19.                 o.screenPos = ComputeScreenPos(o.vertex);
    20.                 return o;
    21.             }
    22.          
    23.             fixed4 frag (v2f i) : SV_Target
    24.             {
    25.                 fixed4 col = float4(i.screenPos.x,i.screenPos.x, i.screenPos.x, 1);
    26.  
    27.                 return col;
    28.             }
     
    Last edited: Jul 5, 2017
  2. TryToDreamBig

    TryToDreamBig

    Joined:
    Jul 4, 2017
    Posts:
    2
    While I cannot explain exactly why at this stage, it appears that the fourth coordinate in the float4 returned by ComputeScreenPos can be used to make o.screenPos.xy return valuable results from 0 to 1.

    //Updated code to make coordinates between 0 and 1
    o.screenPos = ComputeScreenPos(o.vertex);
    o.screenPos.xyz /= o.screenPos.w;
     
    bgolus likes this.
  3. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    You should only do this part in the fragment shader, and not in the vertex shader. The reason is because of the non-linear nature of projection (aka clip) space. If you do the divide in the vertex shader it might look right in some cases, specifically on quads that are parallel with the camera plane, but anything more complicated will end up with the screenPos looking distorted.

    Code (CSharp):
    1.             struct appdata
    2.             {
    3.                 float4 vertex : POSITION;
    4.                 float2 uv : TEXCOORD0;
    5.             };
    6.             struct v2f
    7.             {
    8.                 float2 uv : TEXCOORD0;
    9.                 float4 vertex : SV_POSITION;
    10.                 float4 screenPos : TEXCOORD1;
    11.             };
    12.        
    13.             v2f vert (appdata v)
    14.             {
    15.                 v2f o;
    16.                 o.vertex = UnityObjectToClipPos(v.vertex);
    17.                 o.uv = v.uv;
    18.                 o.screenPos = ComputeScreenPos(o.vertex);
    19.                 return o;
    20.             }
    21.        
    22.             fixed4 frag (v2f i) : SV_Target
    23.             {
    24.                 float2 screenPos = i.screenPos.xy / i.screenPos.w;
    25.                 fixed4 col = fixed4(screenPos.xxx, 1);
    26.                 return col;
    27.             }
    In projection space, what the UnityObjecToClipPos() function returns, the x and y components go from -w to +w. The ComputeScreenPos() basically just does "xy = (xy + w) * 0.5" to move them from that -w to +w range to a 0.0 to +w range, plus some extra code to handle flipped projection space (it's a "fun" topic, but sometimes rendering happens upside down, don't worry about it), and deal with single pass stereo.
     
    TryToDreamBig likes this.