Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Voting for the Unity Awards are OPEN! We’re looking to celebrate creators across games, industry, film, and many more categories. Cast your vote now for all categories
    Dismiss Notice
  3. Dismiss Notice

How can I get the center of screen but not normalized?

Discussion in 'Shaders' started by AndreiMarian, Jan 3, 2018.

  1. AndreiMarian

    AndreiMarian

    Joined:
    Jun 9, 2015
    Posts:
    77
    I mean the distance from the center. I'm working in 2D with custom shaded sprites and I'm trying to make calculations based on distance from screen center and I want if possible to take advantage of auto interpolation but at the same time I want the distance to be independent of screen ratio which in spite of all the efforts, I couldn't achieve.
    I have tried all sorts of things, here's the closest I could get to what I'm after:
    Code (CSharp):
    1. Shader "A/Frq"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Main Texture", 2D) = "white" {}
    6.     }
    7.  
    8.     SubShader
    9.     {
    10.         Tags { "RenderType"="Opaque" }
    11.         LOD 100
    12.         ZWrite Off
    13.         Blend SrcAlpha OneMinusSrcAlpha
    14.         Pass
    15.         {
    16.             CGPROGRAM
    17.             #pragma vertex vert
    18.             #pragma fragment frag
    19.        
    20.             #include "UnityCG.cginc"
    21.             struct appdata
    22.             {
    23.                 float4 vertex : POSITION;
    24.                 float2 uv : TEXCOORD0;
    25.             };
    26.             struct v2f
    27.             {
    28.                 float2 uv : TEXCOORD0;
    29.                 float4 vertex : SV_POSITION;
    30.                 float4 scrCen : TEXCOORD1;
    31.                 float4 scrTest : COLOR; // alternative for scrCen, not really a color
    32.             };
    33.             sampler2D _MainTex;
    34.             float4 _MainTex_ST;
    35.        
    36.             v2f vert (appdata v)
    37.             {
    38.                 v2f o;
    39.                 o.vertex = UnityObjectToClipPos(v.vertex);
    40.                 o.uv = v.uv;
    41.              
    42.                 // option 1 is to use o.vertex directly
    43.                 o.scrCen = o.vertex / o.vertex.w ; // option 2
    44.                 //o.scrCen = o.vertex; // option 3
    45.                 //o.scrTest = o.vertex / _ScreenParams; // option 4, intention of counter the later normalization
    46.              
    47.                 return o;
    48.             }
    49.        
    50.             fixed4 frag (v2f i) : SV_Target
    51.             {
    52.                 //return float4(i.vertex.x / 1280, i.vertex.y/720 , 0, 1); // option A
    53.                 return float4(i.scrCen.x / 10, i.scrCen.y / 10, 0, 1); // option B
    54.                 //return float4(i.scrTest.x * 10, i.scrTest.y * 10, 0, 1); // option C
    55.             }
    56.             ENDCG
    57.         }
    58.     }
    59. }
    I've tried all sorts of combinations of those number and letter options but it's like only firing ricochets :)
    I'm trying to get 0 values in the center and same positives for even distance for x and y.
    PS:
    Shading is a science of its own and although I've done quite some research it's nearly impossible for me to crack :)
     
    Last edited: Jan 3, 2018
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,230
    Distance from center of the screen in what unit? Pixels? Screen half widths? Degrees?

    Lets go with screen half widths for now. That is that it'll be 0.0 at the center of the screen, 1.0 at the left or right edge. Top and bottom edge.

    The easiest way to do this is pass the full o.vertex from the vertex shader to the fragment shader as another semantic, like you're kind of doing in your shader already. However do not divide by w in the vertex shader, only do that in the fragment shader. (For sprites and / or orthographic cameras it likely doesn't matter, but it's not really good practice.)

    So, in the vertex shader:
    o.screenPos = o.vertex;

    In the fragment shader:
    float2 screenPos = i.screenPos.xy / i.screenPos.w;

    That will get you a value that has a range of -1 to 1 for both the x and y. Now you need to correct for the aspect ratio. You kind of had the right idea with your "Option A", however there's no need to use hard coded numbers for the resolution as Unity passes that information to the shaders already in the form of the _ScreenParams variable. Also dividing by the resolution is just going to skew the aspect ratio even worse.

    float2 screenPos = i.screenPos.xy / i.screenPos.w;
    screenPos.y *= _ScreenParams.y / _ScreenParams.x;


    That'll scale the y so that it's the "same" as the x in terms of how it looks. After that you can just do length(screenPos) to get a distance to the center of the screen. Note, at the corners of the screen that distance is > 1.
     
  3. AndreiMarian

    AndreiMarian

    Joined:
    Jun 9, 2015
    Posts:
    77
    Thank you very much, it all makes sense to me and it works (of course :)).
    I haven't mentioned in what units because I can work with any. The effect is supposed to increase with distance from center, in an "isotropic" way. But what you present is perfect.

    Funny and totally unexpected for me how just a "clone" of the vertex yields different results, maybe it's the semantic... o_O
    Just for the record, I may be wrong but in glsl you have uniforms and varyings so you can see which vars get interpolated plus you can have your own non-semantic vars whereas here I can't have non-semantics in struct cause it breaks dx11.

    Now that you mentioned _ScreenParams and reading about built-in vars, I wonder if it were more straightforward to just use _WorldSpaceCameraPos and merely calculate effectStrength = o.vertex - _WorldSpaceCameraPos but I wouldn't know how to make effectStrength interpolated.
    EDIT:
    This only works if moving the camera but not when moving the object relative to the camera.
     
    Last edited: Jan 4, 2018
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,230
    Uniforms are the same in glsl and hlsl. Almost any value you define outside of the function bodies, like float4 _MainTex_ST in your shader, is a uniform in hlsl. You can add uniform in front of that line, but it's implied by where you defined it and how it's used so it's not necessary. As you noted hlsl uses semantics to pass data from the vertex to the fragment shader stages, where as glsl uses the varying keyword on variables defined outside of the function bodies. A float 4 myVal : TEXCOORD0; works exactly the same as varying vec4 myVal; apart from hlsl being (semi) explicit about what registers to use, where as glsl determines them for you automatically.

    However the SV_POSITION semantic is special, it's the equivalent of glsl's gl_Position and gl_FragCoord in that it's the vertex shader's output clip space position for rastorization, and the fragment shader input pixel position and z depth post rastorization. So even though it's the SV_POSITION for both the vertex output and fragment input the data is not the same. Passing the o.vertex value in a TEXCOORD semantic means it doesn't get modified at all, only interpolated.

    Because o.vertex is in clip space and _WorldSpaceCameraPos is in world space. The fact you get anything sensible out of that at all, ever, is a fluke because they are in extremely different spaces. If you wanted to do compare anything against _WorldSpaceCameraPos you'd want to use the world space vertex position instead.

    mul(unity_ObjectToWorld, float4(v.vertex.xyz, 1)).xyz - _WorldSpaceCameraPos

    That'll be the 3d position in world space relative to the camera. Depending on where you camera and how it's rotated that may or may not be useful information. More likely you want this:

    mul(unity_ObjectToWorld, float4(v.vertex.xyz, 1)).xy - _WorldSpaceCameraPos.xy

    That'll give you the 2d world space position relative to the camera, ignoring how far the camera is from the 2d plane, assuming your camera is looking down the z axis and is never rotated.
     
    AndreiMarian likes this.
  5. AndreiMarian

    AndreiMarian

    Joined:
    Jun 9, 2015
    Posts:
    77
    Thanks for the info and your time. You know your way around with shaders. I wish I were like that but personally I find it difficult to cut through the shaders because there's stuff going on automatically, without explicitly showing in them and this happens at different points and with different semantics.