Search Unity

Question "Rotating" a Billboard Shader

Discussion in 'Shaders' started by equal, Jul 31, 2022.

  1. equal

    equal

    Joined:
    Dec 14, 2012
    Posts:
    77
    Hi,

    i have this Shader from https://forum.unity.com/threads/pro...-sprites-clipping-into-3d-environment.680374/
    It was made by bgolus .
    This Shader rotates a Quad making it billboard towards the Camera.
    The Problem in the original Thread was that certain clipping apears.

    I got to the point where Clipping apears in my Project as well, but
    my Problem is that i am working in the "2D" workspace. My "UP" is the negative Z Axis.
    (The game is effectivly 2D with 3D Graphics)
    I would have to "just" change the Axis of this Shader to work properly for me.
    Sadly i am currently guessing what _m22 to change to _m32 etc and hoping that i get it right by mere chance.
    I am trying now for a while and i thought why dont i ask some of you shader guys to either:
    Suggest a change i could make to this Shader
    or
    Point me to a good Documentation, so i can learn whats going on.

    Any help is welcome!


    Code (CSharp):
    1. Shader "Custom/NewSurfaceShader"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex("Texture", 2D) = "white" {}
    6.     }
    7.     SubShader
    8.     {
    9.         Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" "DisableBatching" = "True" }
    10.         ZWrite Off
    11.         Blend SrcAlpha OneMinusSrcAlpha
    12.         Pass
    13.         {
    14.             CGPROGRAM
    15.             #pragma vertex vert
    16.             #pragma fragment frag
    17.             // make fog work
    18.             #pragma multi_compile_fog
    19.             #include "UnityCG.cginc"
    20.             struct appdata
    21.             {
    22.                 float4 vertex : POSITION;
    23.                 float2 uv : TEXCOORD0;
    24.             };
    25.             struct v2f
    26.             {
    27.                 float4 pos : SV_POSITION;
    28.                 float2 uv : TEXCOORD0;
    29.                 UNITY_FOG_COORDS(1)
    30.             };
    31.             sampler2D _MainTex;
    32.             float4 _MainTex_ST;
    33.             float rayPlaneIntersection( float3 rayDir, float3 rayPos, float3 planeNormal, float3 planePos)
    34.             {
    35.                 float denom = dot(planeNormal, rayDir);
    36.                 denom = max(denom, 0.000001); // avoid divide by zero
    37.                 float3 diff = planePos - rayPos;
    38.                 return dot(diff, planeNormal) / denom;
    39.             }
    40.             v2f vert(appdata v)
    41.             {
    42.                 v2f o;
    43.                 o.pos = UnityObjectToClipPos(v.vertex);
    44.                 o.uv = v.uv.xy;
    45.                 // billboard mesh towards camera
    46.                 float3 vpos = mul((float3x3)unity_ObjectToWorld, v.vertex.xyz);
    47.                 float4 worldCoord = float4(unity_ObjectToWorld._m03, unity_ObjectToWorld._m13, unity_ObjectToWorld._m23, 1);
    48.                 float4 viewPos = mul(UNITY_MATRIX_V, worldCoord) + float4(vpos, 0);
    49.                 o.pos = mul(UNITY_MATRIX_P, viewPos);
    50.                 // calculate distance to vertical billboard plane seen at this vertex's screen position
    51.                 float3 planeNormal = normalize(float3(UNITY_MATRIX_V._m20, 0.0, UNITY_MATRIX_V._m22));
    52.                 float3 planePoint = unity_ObjectToWorld._m03_m13_m23;
    53.                 float3 rayStart = _WorldSpaceCameraPos.xyz;
    54.                 float3 rayDir = -normalize(mul(UNITY_MATRIX_I_V, float4(viewPos.xyz, 1.0)).xyz - rayStart); // convert view to world, minus camera pos
    55.                 // same as the original example shader
    56.                 float dist = rayPlaneIntersection(rayDir, rayStart, planeNormal, planePoint);
    57.    
    58.                 // added check to get distance to an arbitrary ground plane
    59.                 float groundDist = rayPlaneIntersection(rayDir, rayStart, float3(0,1,0), planePoint);
    60.                 // use "min" distance to either plane (I think the distances are actually negative)
    61.                 dist = max(dist, groundDist);
    62.    
    63.                 // then do the rest of the shader normally
    64.                 // calculate the clip space z for vertical plane
    65.                 float4 planeOutPos = mul(UNITY_MATRIX_VP, float4(rayStart + rayDir * dist, 1.0));
    66.                 float newPosZ = planeOutPos.z / planeOutPos.w * o.pos.w;
    67.                 // use the closest clip space z
    68.                 #if defined(UNITY_REVERSED_Z)
    69.                 o.pos.z = max(o.pos.z, newPosZ);
    70.                 #else
    71.                 o.pos.z = min(o.pos.z, newPosZ);
    72.                 #endif
    73.                 UNITY_TRANSFER_FOG(o,o.pos);
    74.                 return o;
    75.             }
    76.             fixed4 frag(v2f i) : SV_Target
    77.             {
    78.                 fixed4 col = tex2D(_MainTex, i.uv);
    79.                 UNITY_APPLY_FOG(i.fogCoord, col);
    80.                 return col;
    81.             }
    82.             ENDCG
    83.         }
    84.     }
    85. }
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Presumably the bit of code you're trying to change is this one at line 64 in the original example shader, 51 in yours.
    Code (csharp):
    1. float3 planeNormal = normalize(float3(UNITY_MATRIX_V._m20, 0.0, UNITY_MATRIX_V._m22));
    You did correctly identify which line you need to modify, but not how to modify it.

    That's the normal of the billboarded plane, i.e.: the direction the surface of that plane should be facing, not the axis of rotation. Though they are directly related.

    The assumption in that shader is the billboard should be rotating on the world Y axis. A Y axis spinning billboard will have a normal vector with only values in the X and Z components, as the Y will always be 0.0, which is what that
    float3()
    is setting. It could be rewritten as:
    Code (csharp):
    1. normalize(UNITY_MATRIX_V._m20_m21_m22 * float3(1,0,1))
    or
    Code (csharp):
    1. normalize(UNITY_MATRIX_V._m20_m21_m22 * (float3(1,1,1) - float3(0,1,0))
    That last
    float3(0,1,0)
    being the "up" vector.

    So if you need Z to be the up vector, you'd want to modify that line to be:
    Code (csharp):
    1. float3 planeNormal = normalize(float3(UNITY_MATRIX_V._m20, UNITY_MATRIX_V._m21, 0.0));
    You'll also need to change the
    float3(0,1,0)
    being used for the
    rayPlaneIntersection()
    function to be
    float3(0,0,1)
    since the ground is now not on the y plane.
     
  3. equal

    equal

    Joined:
    Dec 14, 2012
    Posts:
    77
    Thank you very VERY much for your reply, im not sure how i missed it yesterday!
    Im currently throu the first Shader vid of Miss Holmér (https://www.youtube.com/c/Acegikmo) so i can pick up whats actually going on more precicly.

    My latest guess was that maybe the "model" of the Quad needed some change(a rotation of 90 degree, to align with the proper Plane ).

    I will try what you have suggested and report back.
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    It's not a bad guess.

    It's wrong in this case, but it's not a bad guess. If you did that here it just means you'd have a quad that was spinning on the wrong axis.
     
  5. equal

    equal

    Joined:
    Dec 14, 2012
    Posts:
    77
    Alright,

    i fully learned how to write shaders and matrix stuff.

    It apears that i can not have what i want to have.
    Here is a Picture of a Solution i thought should work.
    upload_2022-8-13_19-39-43.jpeg
    (Blue=OrthoCam, Green=InitialBillboarding, Brown=ChangedBillboard, RedDotArrow= VertexMoveDistance)

    I have an Orthographic Camera, and the Camera can not watch directly down nor forward but can rotate around the -Z Axis freely.
    The Solution i thought was simple, move the top 2 Vertices towards the Cam when the ViewAngle increases.
    This Solution is very similar to your inital Shader Bgolus. I thought i can avoid the Clipping of stuff in front of a Billboard, if your normal plane was set to close.

    But with this Solution , if i rotate the Camera around the -Z, the bottom 2 (edit: should affect more ) Vertices clip into the Walls (in the Picture Black Lines)
    If i combine this solution with the Normal Plane one, i could approch something that looks good for a while but it will bite me later. Because currently i have very simple geometry going on, couple of Walls, a floor and a Billboard. If there would be any complex 3d Model with Holes and Tubes, the clipping would come back, in one form or another.
    @bgolus Is this a Limit i reached here?

    How did Dungeon Keeper did this over 20 Years ago?
    (noted i found clippings, but in general they are very rare, and sometimes i wasnt sure if the clipping didnt come from missing vSync buffering)
     
    Last edited: Aug 13, 2022
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    The original Dungeon Keeper is at its heart a 2D game. Yes it has 3D elements, but it can choose to render objects on top of 3D elements on a whim by modifying the draw order of the sprites and individual world triangles.

    However all is not lost. You still need to pretend your sprites have some volume to them. Don’t let the objects get that close to walls!
     
  7. equal

    equal

    Joined:
    Dec 14, 2012
    Posts:
    77
    True and with your tip to act like they have volume, i can imagine it would work.
    But with futher work into the project i realized that a Perspective View and actual 3d Models are far more benefitial for this game. So sadly no more 2d Planes to worry about. The Final nail was the Skybox in Orthographic, while i am sure there is a good solution to that too, turning everything 3d and perspective removed the need to find/implement those solutions.

    On the Brightside i can write shaders now :) and thats hella usefull.

    Bgolus thank you very much for your help!


    Note: If there is egnouth interest in the Shader Code i will try to find time to clean it up and post here.
     
    mgear likes this.