I figured out 2 ways of doing this:
1- Calculating it in your code using SpriteRenderer properties and world to screen transformations:
[ExecuteInEditMode]
public class Sharpener : MonoBehaviour
{
private SpriteRenderer spriteRenderer;
private MaterialPropertyBlock propertyBlock;
// Use this for initialization
void Start()
{
spriteRenderer = GetComponent<SpriteRenderer>();
}
void OnEnable()
{
propertyBlock = new MaterialPropertyBlock();
}
void OnWillRenderObject()
{
spriteRenderer.GetPropertyBlock(propertyBlock);
{
Camera cam = Camera.current;
if (cam == null || float.IsNaN(cam.orthographicSize) || cam.orthographicSize <= 0)
{
propertyBlock.SetVector("_TexScale", Vector2.one);
}
else
{
Vector3 c = spriteRenderer.transform.TransformPoint(spriteRenderer.sprite.bounds.min);
Vector3 v = Vector3.Scale(spriteRenderer.transform.lossyScale, spriteRenderer.sprite.bounds.size);
v = cam.WorldToScreenPoint(c + v);
c = cam.WorldToScreenPoint(c);
v -= c;
Vector2 texScale = spriteRenderer.sprite.rect.size / v;
propertyBlock.SetVector("_TexScale", texScale);
}
}
spriteRenderer.SetPropertyBlock(propertyBlock);
}
}
And then inside your Unity shader Properties:
[PerRendererData] _TexScale ("Texture Scale", Vector) = (1,1,1,1)
Don't forget to define it in the actual shader source too:
float2 _TexScale;
This obviously only works in a orthographic camera setup (2D).
2- Calculating it inside pixel/fragment shader using derivative functions:
float4 dv = float4(
abs(ddx(texCoord.x * _MainTex_TexelSize.z)),
abs(ddx(texCoord.y * _MainTex_TexelSize.w)),
abs(ddy(texCoord.x * _MainTex_TexelSize.z)),
abs(ddy(texCoord.y * _MainTex_TexelSize.w))
);
float2 texScale = float2(sqrt(dv.x * dv.x + dv.y * dv.y), sqrt(dv.z * dv.z + dv.w * dv.w));
When it comes to performance I haven't tested them (never guess, always profile your code) but the second one works in 3D as well and can be easily converted to GLSL.