Hey guys, I’ve been experimenting with building a tactics engine using 3D geometry but 2D character sprites, similar to the Final Fantasy Tactics and Disgaea series. I’m using a simple shader for billboard sprites that always face the camera, and this works quite well in most cases.
Using one of their sprites as a placeholder, you can see they render fine at a shallow angle:
And are properly occluded by objects in front:
However, at any steeper camera angle, if they’re close to a vertical wall, the sprite tilts back to maintain its facing, and is clipped through by the wall.
This is realistic behaviour for a flat plane, but the sprites have their perspective baked in, and I need to avoid this clipping somehow. All of the sprite should render over objects further back than its bottom pixel, but still be occluded by obstacles in front.
Is there some way I can achieve this or avoid the clipping issue another way, short of designing my 3D space around it?
Thanks in advance!
EDIT: Including the shader I used for reference.
Shader "Unlit/Sprite_Billboard_Unlit"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
}
SubShader
{
Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" "DisableBatching" = "True" }
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 pos : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.uv.xy;
// billboard mesh towards camera
float3 vpos = mul((float3x3)unity_ObjectToWorld, v.vertex.xyz);
float4 worldCoord = float4(unity_ObjectToWorld._m03, unity_ObjectToWorld._m13, unity_ObjectToWorld._m23, 1);
float4 viewPos = mul(UNITY_MATRIX_V, worldCoord) + float4(vpos, 0);
float4 outPos = mul(UNITY_MATRIX_P, viewPos);
o.pos = outPos;
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}