Advertisement

How to get scaling of a texture when drawn on screen?

Started by September 29, 2018 11:30 PM
2 comments, last by milessmith 6 years, 3 months ago

How can I get scaling of a texture when drawn on screen? In the other words: how can I get the amount of texture elements (texel) a pixel on the screen takes up? i.e. if a texture has 100x100 pixels in size and it only takes up 20x20 pixels on the monitor screen then I want to calculate 5.0 as value. I don't need anything complex since it's a 2D scene with ortographic camera setup.
I'm trying to do manual texture sampling in my fragment shader. It's a Cg prorgam inside a Unity project so if there is a built-in way to get/calculate this let me know.
I feel like there are two ways:

  1. Calculate using viewport and camera information
  2. Calculate using world to screen space transformations

Is there a better way? Which one should I implement and how?

Ultimately, my constant dissatisfaction with the way things are becomes the driving force behind everything I do.

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.

Ultimately, my constant dissatisfaction with the way things are becomes the driving force behind everything I do.

Advertisement

Good article. I think it would be worth mentioning Unity has Frame Debugger tool better than the mentioned general-purpose Profiler tool for GPU-related debugging

 

 

This topic is closed to new replies.

Advertisement