pixel-shaders

2

The sprites clipping is an issue I’ve been meaning to tackle for a while now. I’ve gone through a couple different iterations of strategies for displaying the sprites, but they’ve all relied on the sprite being at a constant angle facing the camera, like so:

Of course at an angle, it’s very easy for the sprite to just go right through walls. Some games avoid this issue altogether by having the collision detection extend out far enough, or having the sprites small enough to fit in a smaller collision area. This was not a design choice I had made, and a collision area large enough would seriously impact mobility with such a steep angle on such a large sprite. I had been trying to make use of the sprite shader’s offset property to force the sprite to render after walls, but if I took the offset far enough so as to draw the whole head above the wall, I’d get diminishing returns and the feet would start rendering above foreground elements(as seen in the first photo), and if I lowered it to a point where that didn’t happen, a portion of the head would still clip at direct angles (as seen in photo 2).

I wondered if I could figure out a way I could have the sprite render perfectly vertically, but with a transformation applied to it to create a false sense of perspective, so that the sprite would not appear squashed.

I decided to scrap everything I had in place for billboarding, which up until now was an attached C# component which just rotated towards the camera, and instead look into rotating from the shader code. I managed to hack together a sort of frankenstein formula for resizing the sprite relative to the model view matrix, but if anyone here is good with geometry, they should hit me up because it’s definitely imperfect (the effect stops working at too steep of an angle), but it works for my use here.

I added this little bit of code to unity’s default sprite diffuse shader:

 void vert (inout appdata_full v, out Input o)
        {
            UNITY_INITIALIZE_OUTPUT(Input, o);
      
              float3 forward = normalize(UNITY_MATRIX_V._m20_m21_m22);
            float3 up = normalize(UNITY_MATRIX_MVP._m10_m11_m12);
            float3 upXZ = up;
            upXZ.y = 0;
              float3 right = normalize(UNITY_MATRIX_V._m00_m01_m02);

            float4x4 rotationMatrix = float4x4(right,   0,
                                         //use the magnitude of the normalized Model View Projection matrix, plus the length between the magnitude between the X and Z values in the MVP, divided by pi
                                         float3(0, length(up) + length(upXZ)/3.14159265359, 0), 0,
                                         forward, 0,
                                         0, 0, 0, 1);

            v.vertex = mul(v.vertex, rotationMatrix);
              v.normal = mul(v.normal, rotationMatrix);

            #if defined(PIXELSNAP_ON)
            v.vertex = UnityPixelSnap (v.vertex);
            #endif
            
            o.color = v.color * _Color;
        } 

And it works! No more clipping, and no squashed sprite! And ideally, this is a more efficient solution for billboarding anyway, as it doesn’t require all those C# scripts

As an added bonus, it makes the mirrors work so much better, since the sprites are just now rotated through the render pipeline, they’ll never appear with a warped perspective, and support the same for multiple mirrors

Woah who’s that creepin behind you