Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Mapping dot product to 0-1, going 360 degrees around an object

Discussion in 'Shaders' started by Benjamin_Overgaard, Oct 31, 2019.

  1. Benjamin_Overgaard

    Benjamin_Overgaard

    Joined:
    Jul 20, 2015
    Posts:
    17
    Hi, I'm trying to map a dot product to go between 0 and 1 instead of -1 and 1. The two vectors used are a vector pointing to the camera and a forward vector for the object, both in object space.

    Code (CSharp):
    1. float4 localCamDir = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1.0));
    2. float4 objForwardDir = float4(0,0,1,1);
    3. float dotProduct = dot(objForwardDir , normalize(localCamDir));
    When the camera vector and the forward vector are pointing in the same direction, I need a value of 1. When they are pointing in opposite directions, I need a value of 0.5 (not -1). The value should go to 0 as the camera direction starts to align with the forward again, but it should jump back to 1 when they align completely.

    Anyone have an idea how to do that?
    Thanks!
     
  2. cnheider

    cnheider

    Joined:
    Apr 5, 2017
    Posts:
    13
    Something like this?
    Code (Boo):
    1.  
    2. float3 forward = mul((float3x3)unity_CameraToWorld, float3(0,0,1));
    3. float4 objForwardDir = float4(0,0,1,1);
    4. float dotProduct = dot(objForwardDir , normalize(forward));
    5. float remap = lerp(0.5, 1.0, (dotProduct + 1.0) * 0.5);
    6. float jump = 1.0 - remap;
    7. if(jump <= 0.01f) jump = 1.0;
    8.  
     
  3. Benjamin_Overgaard

    Benjamin_Overgaard

    Joined:
    Jul 20, 2015
    Posts:
    17
    Thanks, but from 0-360 degrees it needs to fade from 0-1. With the code you posted, it makes a jump to 0 at around 45 degrees where it should actually just fade from white. It should only jump from 360 degrees to 0 degrees.
     
  4. cnheider

    cnheider

    Joined:
    Apr 5, 2017
    Posts:
    13
    Try this
    Code (Boo):
    1. float3 camForwardDir = mul((float3x3)unity_CameraToWorld, float3(0,0,1));
    2. float4 localCamDir = normalize(mul(unity_WorldToObject, float4(camForwardDir, 1.0)));
    3. float4 objForwardDir = float4(0,0,1,1);
    4. float4 objForwardUp = float4(0,1,0,1);
    5. float3 objForwardRight = cross(objForwardUp, objForwardDir);        // right vector
    6.  
    7. float a = dot(objForwardDir, localCamDir);
    8. float b = dot(objForwardRight, localCamDir);
    9.  
    10. const float PI = 3.14159;
    11. float l =  atan2(a,b)/PI*0.5+0.5;
    12. col = float4(l,(1.0-l).xx,1.0);
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    A cross, two dot products, and an atan2? Why not a single acos?
    Also that example is using the camera’s forward vector, @Benjamin_Overgaard did you want it to be the forward vector, or the camera to object position vector like you had before?

    Really your first attempt was pretty close. Really the problem was you were doing a dot product between two float4 values when really you should have been comparing only the normalized .xyz of the localCamDir with a float3(0,0,1). That would get you a nice -1 to 1 dot product. A dot product is the cosine of the angle between two (normalized) vectors. So acos(dot()) gives you the angle in radians. Divide by pi and you’ve got a 1.0 to 0.0.
     
  6. cnheider

    cnheider

    Joined:
    Apr 5, 2017
    Posts:
    13
    @bgolus, ah did not realise it was the local position of the camera and not the direction but pretty sure he wants the sign such that it makes a hard transition at 0 to 360.

    Here is another proposal then ;)
    Code (Boo):
    1.                
    2. float3 localCamDir = normalize(mul(unity_WorldToObject, _WorldSpaceCameraPos));
    3. float3 objForwardDir = float3(0,0,1);
    4. float3 objForwardRight = float3(1,0,0);
    5. const float PI = 3.14159;
    6. float a = dot(objForwardDir, localCamDir);
    7. float b = dot(objForwardRight, localCamDir);
    8. float l = acos(a)/PI/2;
    9. if(b<0) l = 1.0-l;
    10.  
     
  7. Benjamin_Overgaard

    Benjamin_Overgaard

    Joined:
    Jul 20, 2015
    Posts:
    17
    Thanks, it's pretty close now.
    But I can only get it to work with float4, using the w of 1.

    I can't really get it to be linear though. Even at an angle of 45 degrees, it's outputting 0. And at around 225 degrees, there's a bit of a jump. Any ideas?
     
    Last edited: Nov 3, 2019
  8. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    Then you're doing something else wrong, because that is absolutely wrong. You do not want to be doing a dot product in 4 dimensional space, which is what you're doing if you use a float4. That also likely explains the issues you're having with 45 and 225 degrees as you're doing a dot product with an un-normalized vector in 4d space (float4(0,0,1,1) has a length greater than 1).

    Code (csharp):
    1. Shader "Unlit/LinearAlignmentGradient"
    2. {
    3.     Properties
    4.     {
    5.     }
    6.     SubShader
    7.     {
    8.         Tags { "RenderType"="Opaque" }
    9.         LOD 100
    10.  
    11.         Pass
    12.         {
    13.             CGPROGRAM
    14.             #pragma vertex vert
    15.             #pragma fragment frag
    16.  
    17.             #include "UnityCG.cginc"
    18.  
    19.             struct appdata
    20.             {
    21.                 float4 vertex : POSITION;
    22.             };
    23.  
    24.             struct v2f
    25.             {
    26.                 float4 vertex : SV_POSITION;
    27.             };
    28.  
    29.             v2f vert (appdata v)
    30.             {
    31.                 v2f o;
    32.                 o.vertex = UnityObjectToClipPos(v.vertex);
    33.                 return o;
    34.             }
    35.  
    36.             fixed4 frag (v2f i) : SV_Target
    37.             {
    38.                 float3 localCamDir = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1.0)).xyz;
    39.                 float3 objForwardDir = float3(0,0,1);
    40.                 float dotProduct = dot(objForwardDir , normalize(localCamDir));
    41.                 float radianAngle = acos(dotProduct);
    42.                 float linearGradient = radianAngle / UNITY_PI;
    43.              
    44.                 return linearGradient;
    45.             }
    46.             ENDCG
    47.         }
    48.     }
    49. }
     
    Last edited: Nov 3, 2019
    Invertex likes this.
  9. Benjamin_Overgaard

    Benjamin_Overgaard

    Joined:
    Jul 20, 2015
    Posts:
    17
    Ah, you're right.

    When using float3s, I accidentally left out the 4th component of the matrix multiplication. The gradient is now going linearly from 0-1 around 360 degrees.

    Thanks!