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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Billboard with single axis rotation

Discussion in 'Shaders' started by imPrgrmr, May 28, 2018.

  1. imPrgrmr

    imPrgrmr

    Joined:
    Oct 19, 2013
    Posts:
    14
    Hello,

    I am trying to make a billboard shader with two conditions:
    1) The angle between the view vector and the local y axis of the model is minumum (i.e. the model always faces the camera)
    2) The model keeps its orientation to z azis when rotated or translated

    The best I have got so far is the folowing:

    Code (CSharp):
    1. Shader "Custom/BillboardZ"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Texture Image", 2D) = "white" {}
    6.         _Color("Tint Color", Color) = (0.5,0.5,0.5,0.5)
    7.     }
    8.     SubShader
    9.     {
    10.         Tags { "Queue" = "Transparent" "IgnoreProjector" = "False" "RenderType" = "Transparent" }
    11.         Cull Off
    12.         ZTest Less
    13.         Blend SrcAlpha OneMinusSrcAlpha
    14.         ColorMask RGB
    15.         Cull Off Lighting Off Fog{ Color(0,0,0,0) }
    16.         ZWrite Off
    17.         Cull Back
    18.         Pass
    19.         {
    20.             CGPROGRAM
    21.             #include "UnityCG.cginc"
    22.             #pragma shader_feature IGNORE_ROTATION_AND_SCALE
    23.             #pragma vertex vert
    24.             #pragma fragment frag
    25.      
    26.             uniform sampler2D _MainTex;
    27.             fixed4 _Color;
    28.          
    29.             float4 _MainTex_ST;
    30.             struct vertexInput
    31.             {
    32.                 float4 vertex : POSITION;
    33.                 float4 tex : TEXCOORD0;
    34.             };
    35.             struct vertexOutput
    36.             {
    37.                 float4 pos : SV_POSITION;
    38.                 float2 tex : TEXCOORD0;
    39.             };
    40.  
    41.             vertexOutput vert(vertexInput input)
    42.             {
    43.                 float3 worldPos = mul(unity_ObjectToWorld, float4(0, 0, 0, 1)).xyz;
    44.                 float3 posCamVector = _WorldSpaceCameraPos - worldPos;
    45.  
    46.                 float w = atan2(posCamVector.y, posCamVector.x);
    47.  
    48.  
    49.                 float cosw = cos(w);
    50.                 float sinw = sin(w);            
    51.  
    52.                 float3x3 rotMatrix;          
    53.                 rotMatrix[0].xyz = float4(cosw, -sinw, 0, 0);
    54.                 rotMatrix[1].xyz = float4(sinw, cosw, 0, 0);
    55.                 rotMatrix[2].xyz = float4(0, 0 ,1, 0);
    56.                 float4 newPos = float4(mul(rotMatrix, input.vertex * float4(0, 1, 1, 0)), 1);
    57.  
    58.                 vertexOutput output;
    59.                 output.pos = mul(UNITY_MATRIX_VP, mul(unity_ObjectToWorld, newPos));
    60.                 output.tex = TRANSFORM_TEX(input.tex, _MainTex);
    61.                 return output;
    62.             }
    63.  
    64.             float4 frag(vertexOutput input) : COLOR
    65.             {
    66.                 return tex2D(_MainTex, input.tex.xy) * _Color;
    67.             }
    68.             ENDCG
    69.         }
    70.     }
    71. }
    This does exactly what I want except when the model is rotated around an axis the 1st condition doesn't hold (it does not face the camera).
    I think the solution would be to apply the rotation to the z axis locally to the model after the model matrix is applied (and not before as in the above code) but I don't know how to do it. Anty ideas?
    The model used with the above shader is a cube.

    I would appreciate any ideas.

    cheers,
    Sotos
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,248
    I'm not sure I follow exactly what you're asking for. I assume you want the mesh to face the camera by rotating around the (world?) Y axis, but also want keep the rotation around the local Z axis so it can spin? If you can guarantee the game object itself is always only ever rotated in world Z then you should be able to apply the objec to world matrix as a rotation matrix to the initial vertex position prior to rotating it towards the camera.

    float3 vertPos = mul((float3x3)unity_ObjectToWorld, v.vertex.xyz);
    float3 newPos = mul(rotMatrix, vertPos) + worldPos;
    output.pos = mul(UNITY_MATRIX_VP, float4(newPos, 1));

    That should apply the gameobject's rotation and scale, then rotate towards the camera. You may also want to use a quad mesh instead of a cube so you're not doing calculations for vertices you don't need.
     
  3. imPrgrmr

    imPrgrmr

    Joined:
    Oct 19, 2013
    Posts:
    14
    Thank you for the reply,

    What I wanted to do is make the billboard face always the camera but with the additional constraint that the billboard is allowed to rotate only around to a local axis say z.

    I solved the problem by decomposing the model matrix in scale rotation and translation therefore T * R * S * v and then multiplying the rotation with another matrix Rz which is a rotation around the local z axis represented in the world coordinates with an angle that depends on the projection of the position to Camera vector to the x local axis.

    therefore the it goes T * Rz * R * S * v

    cheers,
    Sotos
     
    Last edited: Jun 2, 2018