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

Possible to get objects rotation value in unity_ObjectToWorld?

Discussion in 'Shaders' started by Xenxe, Apr 25, 2018.

  1. Xenxe

    Xenxe

    Joined:
    Jul 8, 2014
    Posts:
    11
    I am starting to learn shader programming so Iv'e been looking to make a billboard shader that does what I wan't. It should be no problem however I cannot seem to wrap my head around accessing the rotation data of the object the shader currently lives on.

    I can get its location and from everything i have read says its in the 4x4 model matrix returned by unity_ObjectToWorld however I have no idea how to access the rotation data in that model matrix.

    Ultimately I want the billboarding sprite to change the uv position based on the models rotation so its always "facing correctly" when an object moves about the game world but i'm so new to shader coding this seems to be going over my head..
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,230
    You should read up on transform matrices. The rotation and scale are stored in the 3x3 part of the matrix, with the position in the last column. Extracting the rotation into something user friendly like Euler angles is doable, but more trouble than it’s worth. You only want to find the angle between the view direction and the sprite’s forward direction.

    @mgear has example of how to go about this here:
    https://github.com/unitycoder/DoomStyleBillboardTest
     
    Xenxe likes this.
  3. Xenxe

    Xenxe

    Joined:
    Jul 8, 2014
    Posts:
    11
    I actually already saw that example and ive been working off of it and I have a buggy shader that sort of half works.

    Basically, I want it to change the UV position of the current texture based on the camera rotation AND the objects rotation. I think I've got it to a degree (heh) but it seems like part of the math here is going over my head because it only displays properly at certain angles.

    Here's the full code.

    Code (CSharp):
    1.  
    2. //based on unity coders doom billboard shader
    3. //https://github.com/unitycoder/DoomStyleBillboardTest
    4.  
    5. Shader "Advanced Billboard"
    6. {
    7.  
    8.     Properties
    9.     {
    10.         _MainTex ("Base (RGB)", 2D) = "white" {}
    11.         _Frames ("Frames", Float) = 8
    12.     }
    13.  
    14.     SubShader
    15.     {
    16.         Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True"}
    17.  
    18.         ZWrite Off
    19.         Blend SrcAlpha OneMinusSrcAlpha
    20.  
    21.         Pass
    22.         {
    23.             CGPROGRAM
    24.  
    25.             #pragma vertex vert
    26.             #pragma fragment frag
    27.             #define PI 3.1415926535897932384626433832795
    28.             #define RAD2DEG 57.2957795131
    29.             #define SINGLEFRAMEANGLE (360/_Frames)
    30.             #define UVOFFSETX (1/_Frames)
    31.             #include "UnityCG.cginc"
    32.  
    33.             uniform sampler2D _MainTex;
    34.             uniform float4 _MainTex_ST;
    35.  
    36.             struct appdata {
    37.                 float4 vertex : POSITION;
    38.                 float4 texcoord : TEXCOORD0;
    39.             };
    40.  
    41.             struct v2f {
    42.                 float4 pos : SV_POSITION;
    43.                 half2 uv : TEXCOORD0;
    44.             };
    45.  
    46.             float _Frames;
    47.  
    48.  
    49.             v2f vert (appdata v)
    50.             {
    51.                 v2f o;
    52.        
    53.                 o.pos = UnityObjectToClipPos (v.vertex);
    54.  
    55.                 // object world position
    56.                    float3 objWorldPos=float3(unity_ObjectToWorld._m03,unity_ObjectToWorld._m13,unity_ObjectToWorld._m23);
    57.  
    58.                 //get objects current Y rotation from its rotation matrix in radians
    59.                 float heading = atan2(unity_ObjectToWorld._m20,unity_ObjectToWorld._m00)*RAD2DEG+180;
    60.  
    61.                 // get angle between object and camera
    62.                 float3 fromCameraToObject = normalize(objWorldPos - _WorldSpaceCameraPos.xyz);
    63.                 float angle = atan2(fromCameraToObject.z, fromCameraToObject.x)*RAD2DEG+180;
    64.  
    65.                 // get current tilesheet frame and feed it to UV also subtracts objects Y rotation frames from the current index
    66.                 int index = (angle/SINGLEFRAMEANGLE)-(heading/SINGLEFRAMEANGLE);
    67.  
    68.                 o.uv = float2(v.texcoord.x*UVOFFSETX+UVOFFSETX*index,v.texcoord.y);
    69.  
    70.        
    71.    
    72.             o.pos = mul(UNITY_MATRIX_P,
    73.               mul(UNITY_MATRIX_MV, float4(0.0, 0.0, 0.0, 1.0))
    74.               + float4(v.vertex.x, v.vertex.y, 0.0, 0.0)
    75.               * float4(1, 1, 1, 1.0));
    76.  
    77.  
    78.                 return o;
    79.             }
    80.  
    81.             fixed4 frag(v2f i) : SV_Target
    82.             {
    83.                 return tex2D(_MainTex,i.uv);
    84.             }
    85.             ENDCG
    86.         }
    87.     }
    88. }
    Here's my sprite sheet if you want to test it. Just put this in a new material with 8 frames


    I'm aware this will go negative and will technically be "out of index" by not displaying but I'm stuck here I can't simply make it just spit out the absolute value or min it out at 0. none of that displays properly.

    I'll give it another go some other day but a little help would be appreciated :)
     
    Last edited: May 9, 2018
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,230
    I rewrote the shader a bit for fun. @mgear , maybe you'd like this too.

    The main things that changed:
    • The original used +X axis for forward heading calculation, but Unity uses +Z axis for forward. Minor, but useful for consistency. This does make selecting it in the editor annoying since the default Quad mesh is oriented -Z forward and Unity's selection code doesn't take into account any shader manipulations, so trying to select the sprite while looking at the front doesn't work.. Setting Cull Off makes this a little better as it'll at least select the backface.
    • The frame index was such that it would switch between frame 0 and frame -1 at 0 degrees. But really you want everything between -22.5 and 22.5 degrees to be frame 0 when you have 8 frames. To solve this I use round() instead of relying on the implicit floor() of converting from float to int. This is kind of the big one. Otherwise the sprites always feel like they're slightly off of the correct rotation.
    Obviously there were a few more changes too. I skip the adjustments to bring the angle ranges from [-180, 180] to [0, 360] degrees since it's it makes no difference in the resulting angle diff and just adds some extra math. I removed unnecessary conversions from radians to degrees. Mostly for clutter reduction. Flipped around the cameraToObject vector to be objectToCamera so that both the forward vector and camera vectors are in the same "space". Before they were flipped so that "forward" ended up being the -X axis.

    Code (CSharp):
    1. Shader "Advanced Billboard"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Base (RGB)", 2D) = "white" {}
    6.         _Frames ("Frames", Float) = 8
    7.     }
    8.  
    9.     SubShader
    10.     {
    11.         Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True"}
    12.  
    13.         ZWrite Off
    14.         Blend SrcAlpha OneMinusSrcAlpha
    15.         Cull Off
    16.  
    17.         Pass
    18.         {
    19.             CGPROGRAM
    20.  
    21.             #pragma vertex vert
    22.             #pragma fragment frag
    23.  
    24.             #include "UnityCG.cginc"
    25.  
    26.             #define TAU (UNITY_PI * 2.0)
    27.             #define SINGLEFRAMEANGLE (TAU / _Frames)
    28.             #define UVOFFSETX (1.0 / _Frames)
    29.  
    30.             uniform sampler2D _MainTex;
    31.             uniform float4 _MainTex_ST;
    32.  
    33.             struct appdata {
    34.                 float4 vertex : POSITION;
    35.                 float4 texcoord : TEXCOORD0;
    36.             };
    37.  
    38.             struct v2f {
    39.                 float4 pos : SV_POSITION;
    40.                 float2 uv : TEXCOORD0;
    41.             };
    42.  
    43.             float _Frames;
    44.  
    45.             v2f vert (appdata v)
    46.             {
    47.                 v2f o;
    48.  
    49.                 // object's forward vector (local +Z direction)
    50.                 float3 objWorldForward = unity_ObjectToWorld._m02_m12_m22;
    51.                 //get objects current Y rotation from its rotation matrix in radians
    52.                 float objWorldHeading = atan2(objWorldForward.z, objWorldForward.x);
    53.  
    54.                 // object's world position
    55.                 float3 objWorldPos = unity_ObjectToWorld._m03_m13_m23;
    56.                 // get angle between object and camera in radians
    57.                 float3 objToCam = _WorldSpaceCameraPos.xyz - objWorldPos;
    58.                 float objToCamAngle = atan2(objToCam.z, objToCam.x);
    59.  
    60.                 // get angle difference between heading and camera relative position
    61.                 float angleDiff = objToCamAngle - objWorldHeading;
    62.  
    63.                 // get current tilesheet frame and feed it to UV also subtracts objects Y rotation frames from the current index
    64.                 float index = round(angleDiff / SINGLEFRAMEANGLE);
    65.  
    66.                 o.uv = float2(v.texcoord.x * UVOFFSETX + UVOFFSETX * index, v.texcoord.y);
    67.  
    68.                 o.pos = mul(UNITY_MATRIX_P,
    69.                   mul(UNITY_MATRIX_V, float4(objWorldPos, 1.0))
    70.                   + float4(v.vertex.x, v.vertex.y, 0.0, 0.0));
    71.  
    72.                 return o;
    73.             }
    74.  
    75.             fixed4 frag(v2f i) : SV_Target
    76.             {
    77.                 return tex2D(_MainTex,i.uv);
    78.             }
    79.             ENDCG
    80.         }
    81.     }
    82. }
     
    Xenxe and mgear like this.
  5. Xenxe

    Xenxe

    Joined:
    Jul 8, 2014
    Posts:
    11
    I might have skipped too much trig in highschool.

    Its nice to know the conversions to degrees were redundant. Thank you so much though this shader is a huge step in the right direction!

    Only question I have though is it rotates and displays mostly perfectly but only for the first 90 degrees or so while looking at it. When viewing it outside that 90 degrees it displays nothing. There also appears to be a brief out of index when going to frames 0 to 8 and vice versa.

    I might just be doing it wrong though so I'm trying some new things out with it.

    EDIT: oh fun it works almost perfectly (still briefly disappearing between frames 0 and 8) when the object is rotated at certain angles (206 degrees on the y is one). I fear somehow the angleDiff calculation is a bit inaccurate.


    My project is using the Light Weight Render Pipeline if that makes any functional difference.
    IT DOES

    works mostly fine using the standard render settings. Well it sort of mirrors its display weirdly

    like this but I think i can figure that out. maybe


    Ahh I see setting cull off seems to display the reverse side as well but its rotated 180 degrees so its rightside up because by default it displays upside down...this is strange since this wasnt how it displayed in the LWRP but in the regular pipeline its always been like this even in my old shader lol.




    I needed to reverse a few values but it works great thanks!

    The only problem is the weird selection halo of the combined faces and that's honestly just a visual bug I can get over.
     
    Last edited: May 14, 2018
  6. Xenxe

    Xenxe

    Joined:
    Jul 8, 2014
    Posts:
    11
    Hey it's me again. Every time I work on this thing I learn so much more but I still don't manage to figure it out fully lol.

    I wanted to try working on a sort of x rotation for the objects but only calculated via the camera angle and I figured it would be easy given everything I already had and I was almost right.

    My problem here is I'm not bothering to do a whole range of motion for this rotation. only the positive 180 degrees. here's my spritesheet to illustrate this.


    I have this functionality almost working in my shader but only problem is for the life of me I can't get it to display the top row properly (directly above).

    I have tried all sorts of poking and prodding all of which have their own failures and successes but this is the current most functional version of the shader which does most of what I need it to do.

    please let me know if I am being silly I am still very new to this. Also ignore the bits of extra lighting code in there I was halfway implementing that.

    Code (CSharp):
    1. // Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
    2.  
    3. // Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
    4.  
    5. Shader "Advanced Billboard"
    6. {
    7.     Properties
    8.     {
    9.         _MainTex ("Albedo", 2D) = "white" {}
    10.         _Color ("Diffuse Color", Color) = (1,1,1,1)
    11.         _Frames ("Frames", Float) = 8
    12.         _Yframes ("Yframes", Float) = 5
    13.         _Smoothness ("Smoothness", Range(0, 1)) = 0.5
    14.     }
    15.     SubShader
    16.     {
    17.         Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True"}
    18.         ZWrite Off
    19.         Blend SrcAlpha OneMinusSrcAlpha
    20.         //Cull Off
    21.         Pass
    22.         {
    23.             CGPROGRAM
    24.             #pragma vertex vert
    25.             #pragma fragment frag
    26.             #include "UnityCG.cginc"
    27.             #define TAU (UNITY_PI * 2.0)
    28.             #define SINGLEFRAMEANGLE (TAU / _Frames)
    29.             #define SINGLEYFRAMEANGLE (TAU / _Yframes)
    30.             #define UVOFFSETX (1.0 / _Frames)
    31.             #define UVOFFSETY (1.0 / _Yframes)
    32.  
    33.             uniform float4 _Color;
    34.             uniform sampler2D _MainTex;
    35.             uniform float4 _MainTex_ST;
    36.             uniform fixed4 _LightColor0;
    37.             struct vertexInput {
    38.                 float4 vertex : POSITION;
    39.                 float3 normal : NORMAL;
    40.                 float4 texcoord : TEXCOORD0;
    41.             };
    42.             struct vertex2Frag {
    43.                 float4 pos : SV_POSITION;
    44.                 float4 color : COLOR;
    45.                 float2 uv : TEXCOORD0;
    46.                 float3 normal : TEXCOORD1;
    47.             };
    48.             float _Frames;
    49.             int _Yframes;
    50.  
    51.             vertex2Frag vert (vertexInput v)
    52.             {
    53.                 vertex2Frag o;
    54.                 // object's forward vector (local +Z direction)
    55.                 float3 objWorldForward = unity_ObjectToWorld._m02_m12_m22;
    56.  
    57.                 //get objects current Y rotation from its rotation matrix in radians
    58.                 float objWorldHeading = atan2(-objWorldForward.z, objWorldForward.x);
    59.                 // object's world position
    60.                 float3 objWorldPos = unity_ObjectToWorld._m03_m13_m23;
    61.  
    62.                 // get angle between object and camera in radians
    63.                 float3 objToCam = _WorldSpaceCameraPos.xyz - objWorldPos;
    64.                 float objToCamAngle = atan2(-objToCam.z, objToCam.x);
    65.  
    66.                 //calculates specific angles between camera and object in radians
    67.                 float objToCamYAngle =  atan2(-objToCam.y, min(min(objToCam.z,-objToCam.z),min(objToCam.x,-objToCam.x)));
    68.  
    69.  
    70.                 // get angle difference between heading and camera relative position
    71.                 float angleDiff = objToCamAngle - objWorldHeading;
    72.              
    73.                 // get current tilesheet frame on the x and y axis of the spritesheet
    74.                 float index = round(angleDiff / SINGLEFRAMEANGLE);
    75.                 float yindex = round(objToCamYAngle / SINGLEYFRAMEANGLE);
    76.                 o.uv = float2(v.texcoord.x * UVOFFSETX + UVOFFSETX * index, -v.texcoord.y * UVOFFSETY + UVOFFSETY * yindex);
    77.                 float3 normalDir = normalize(_WorldSpaceCameraPos - objWorldPos);
    78.                 o.normal = v.normal;
    79.                 o.pos = mul(UNITY_MATRIX_P,
    80.                   mul(UNITY_MATRIX_V, float4(objWorldPos, 1.0))
    81.                   + float4(v.vertex.x, v.vertex.y, 0.0, 0.0));
    82.  
    83.                 float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz);
    84.                 float3 diffuse =  _Color.rgb; //* _LightColor0.rgb * max(0.0,dot(normalDir, lightDirection));
    85.  
    86.                 o.color = half4(diffuse, 1.0);
    87.  
    88.  
    89.                 return o;
    90.             }
    91.             fixed4 frag(vertex2Frag i) : COLOR
    92.             {
    93.                 return tex2D(_MainTex,i.uv) * i.color;
    94.             }
    95.             ENDCG
    96.         }
    97.     }
    98. }
     
  7. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,230
    To get the vertical angle you just need:

    asin(normalize(objToCam).y)

    edit: asin, not atan
     
    Last edited: May 16, 2018
    Xenxe likes this.
  8. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,230
    For the vertically offset you have to think about the fact your yaw rotation starts at 0 degrees at the 0 column index. Your row 0 (the bottom row) is -90 degrees.
     
    Xenxe likes this.
  9. Xenxe

    Xenxe

    Joined:
    Jul 8, 2014
    Posts:
    11
    It's beautiful!

    Thanks!



    Code (CSharp):
    1. // Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
    2.  
    3. // Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
    4.  
    5. Shader "Advanced Billboard"
    6. {
    7.     Properties
    8.     {
    9.         _MainTex ("Albedo", 2D) = "white" {}
    10.         _Color ("Diffuse Color", Color) = (1,1,1,1)
    11.         _Frames ("Frames", Float) = 8
    12.         _Yframes ("Yframes", Float) = 5
    13.         _Smoothness ("Smoothness", Range(0, 1)) = 0.5
    14.     }
    15.     SubShader
    16.     {
    17.         Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True"}
    18.         ZWrite Off
    19.         Blend SrcAlpha OneMinusSrcAlpha
    20.         //Cull Off
    21.         Pass
    22.         {
    23.             CGPROGRAM
    24.             #pragma vertex vert
    25.             #pragma fragment frag
    26.             #include "UnityCG.cginc"
    27.             #define TAU (UNITY_PI * 2.0)
    28.             #define SINGLEFRAMEANGLE (TAU / _Frames)
    29.             #define SINGLEYFRAMEANGLE (UNITY_PI / _Yframes)
    30.             #define UVOFFSETX (1.0 / _Frames)
    31.             #define UVOFFSETY (1.0 / _Yframes)
    32.  
    33.             uniform float4 _Color;
    34.             uniform sampler2D _MainTex;
    35.             uniform float4 _MainTex_ST;
    36.             uniform fixed4 _LightColor0;
    37.             struct vertexInput {
    38.                 float4 vertex : POSITION;
    39.                 float3 normal : NORMAL;
    40.                 float4 texcoord : TEXCOORD0;
    41.             };
    42.             struct vertex2Frag {
    43.                 float4 pos : SV_POSITION;
    44.                 float4 color : COLOR;
    45.                 float2 uv : TEXCOORD0;
    46.                 float3 normal : TEXCOORD1;
    47.             };
    48.             float _Frames;
    49.             int _Yframes;
    50.  
    51.             vertex2Frag vert (vertexInput v)
    52.             {
    53.                 vertex2Frag o;
    54.                 // object's forward vector (local +Z direction)
    55.                 float3 objWorldForward = unity_ObjectToWorld._m02_m12_m22;
    56.  
    57.                 //get objects current Y rotation from its rotation matrix in radians
    58.                 float objWorldHeading = atan2(-objWorldForward.z, objWorldForward.x);
    59.                 // object's world position
    60.                 float3 objWorldPos = unity_ObjectToWorld._m03_m13_m23;
    61.  
    62.                 // get angle between object and camera in radians
    63.                 float3 objToCam = _WorldSpaceCameraPos.xyz - objWorldPos;
    64.                 float objToCamAngle = atan2(-objToCam.z, objToCam.x);
    65.  
    66.                 //calculates specific angles between camera and object in radians
    67.                 float objToCamYAngle =  max(asin(normalize(objToCam).y)-30,asin(normalize(objToCam).y)+30);
    68.              
    69.  
    70.                 // get angle difference between heading and camera relative position
    71.                 float angleDiff = objToCamAngle - objWorldHeading;
    72.              
    73.                 // get current tilesheet frame on the x and y axis of the spritesheet
    74.                 float index = round(angleDiff / SINGLEFRAMEANGLE);
    75.                 float yindex = round(objToCamYAngle / SINGLEYFRAMEANGLE);
    76.                 o.uv = float2(v.texcoord.x * UVOFFSETX + UVOFFSETX * index, -v.texcoord.y * UVOFFSETY + UVOFFSETY * yindex);
    77.                 float3 normalDir = normalize(_WorldSpaceCameraPos - objWorldPos);
    78.                 o.normal = v.normal;
    79.                 o.pos = mul(UNITY_MATRIX_P,
    80.                   mul(UNITY_MATRIX_V, float4(objWorldPos, 1.0))
    81.                   + float4(v.vertex.x, v.vertex.y, 0.0, 0.0));
    82.  
    83.                 float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz);
    84.                 float3 diffuse =  _Color.rgb; //* _LightColor0.rgb * max(0.0,dot(normalDir, lightDirection));
    85.  
    86.                 o.color = half4(diffuse, 1.0);
    87.  
    88.  
    89.                 return o;
    90.             }
    91.             fixed4 frag(vertex2Frag i) : COLOR
    92.             {
    93.                 return tex2D(_MainTex,i.uv) * i.color;
    94.             }
    95.             ENDCG
    96.         }
    97.     }
    98. }
     
  10. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,230
    Xenxe likes this.