Skip to content

Instantly share code, notes, and snippets.

@Chaosed0
Created May 3, 2024 19:02
Show Gist options
  • Save Chaosed0/ecf85881be5dcb375373ccf48e776e11 to your computer and use it in GitHub Desktop.
Save Chaosed0/ecf85881be5dcb375373ccf48e776e11 to your computer and use it in GitHub Desktop.
Unity Shader helper for sorting 2D billboarded sprites against 3D objects
// The functions in this file are used to transform camera-facing billboards into vertical billboards
// such that the way the sprite on the billboard is rendered remains the same.
// This is done to avoid sorting issues with 3D objects.
// This code taken from here: https://forum.unity.com/threads/problem-solving-2d-billboard-sprites-clipping-into-3d-environment.680374/
// Calculates intersection point of ray and plane.
float RayPlaneIntersection(float3 rayDir, float3 rayPos, float3 planeNormal, float3 planePos)
{
float denom = dot(planeNormal, rayDir);
denom = max(denom, 0.000001); // avoid divide by zero
float3 diff = planePos - rayPos;
return dot(diff, planeNormal) / denom;
}
// Given the object position of a vertex on a billboarded plane, returns the object position
// of that vertex if the billboard were vertically upright.
float4 ObjectToUprightObjectPos(float4 pos, float3 objectWorldOrigin)
{
float3 viewPos = UnityObjectToViewPos(pos);
// calculate distance to vertical billboard plane seen at this vertex's screen position
float3 planeNormal = normalize(float3(UNITY_MATRIX_V._m20, 0.0, UNITY_MATRIX_V._m22));
float3 planePoint = objectWorldOrigin;
float3 rayStart = _WorldSpaceCameraPos.xyz;
float3 rayDir = -normalize(mul(UNITY_MATRIX_I_V, float4(viewPos.xyz, 1.0)).xyz - rayStart); // convert view to world, minus camera pos
float dist = RayPlaneIntersection(rayDir, rayStart, planeNormal, planePoint);
// calculate the vertical plane world position
return float4(rayStart + rayDir * dist, 1.0);
}
// Given the vertical-billboard-corrected object position of a vertex, returns the clip-space
// position of that vertex.
float4 UprightObjectToClipPos(float4 pos, float4 uprightObjectPos)
{
float4 clipPos = UnityObjectToClipPos(pos);
// calculate the clip space z for vertical plane
float4 planeOutPos = mul(UNITY_MATRIX_VP, uprightObjectPos);
float newPosZ = planeOutPos.z / planeOutPos.w * clipPos.w;
// use the closest clip space z
#if defined(UNITY_REVERSED_Z)
clipPos.z = max(clipPos.z, newPosZ);
#else
clipPos.z = min(clipPos.z, newPosZ);
#endif
return clipPos;
}
// Given the object position of a vertex on a billboarded plane, returns the clip-space position
// of that vertex if the billboard were vertically upright.
// This function allows you to specify the world origin of the object, which is necessary for
// things like particle systems where unity_ObjectToWorld.m03_m13_m23 returns the world-space
// origin of the entire particle system rather than the individual particle.
float4 ObjectToUprightClipPos(float4 pos, float3 objectWorldOrigin)
{
return UprightObjectToClipPos(pos, ObjectToUprightObjectPos(pos, objectWorldOrigin));
}
// Given the object position of a vertex on a billboarded plane, returns the clip-space position
// of that vertex if the billboard were vertically upright.
float4 ObjectToUprightClipPos(float4 pos)
{
float3 objectWorldOrigin = unity_ObjectToWorld._m03_m13_m23;
return ObjectToUprightClipPos(pos, objectWorldOrigin);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment