0
\$\begingroup\$

I am placing a 3D game object with a SpriteRenderer + Billboard attached to it in my scene.

However, since it's a distance away from a perspective camera, the sprite gets scaled, losing a lot of the quality the original art had.

How can I resize the sprite to be 1:1 with the original art asset when displayed on a 1920 x 1080 display?

I want it to maintain that world size when viewed on larger or smaller screens, but stay "pixel perfect" at the target screen resolution.

\$\endgroup\$
2
  • \$\begingroup\$ Not super familiar with Unity but it sounds like "Screen Space - Camera" is using the current camera or a camera you assign it to render the UI, so couldn't you just create an orthographic projection camera and assign it to that since ortho projections means it's the same size regardless of Z position? \$\endgroup\$ Commented Feb 12 at 17:49
  • \$\begingroup\$ @Charanor Definitely an interesting idea! But it looks like when you use an orthographic camera that's overlaid on your main camera for the canvas, the UI is always in front of the gameobject regardless of the layer/sorting order or the z-axis \$\endgroup\$ Commented Feb 12 at 20:28

1 Answer 1

1
\$\begingroup\$

Here is a script that should apply the right compensating scale to keep your sprite at 1 image texel : 1 screen pixel at your target resolution.

I'd recommend using a sprite material with "Pixel Snap" enabled when running at exactly that target resolution, to nudge the sprite onto the pixel grid and avoid unnecessary blending between pixels.

using UnityEngine; public class ScaledBillboard : MonoBehaviour { [Tooltip("Vertical screen resolution where this sprite should render 1:1")] public int targetResolution = 1080; [Tooltip("Camera this should project through - defaults to Camera.main")] new public Camera camera; void OnValidate() => ScaleAndBillboard(); void Start() => ScaleAndBillboard(); // If you change the camera angle / position dynamically, you may // want to call this in Update() too, or in a coroutine for the duration // of the movement. void ScaleAndBillboard() { if (!TryGetComponent(out SpriteRenderer renderer)) { Debug.LogError("Must attach to an object with a SpriteRenderer."); return; } if (camera == null) { camera = Camera.main; } //Billboard to align with view direction. var view = camera.transform.forward; transform.rotation = Quaternion.LookRotation(view); // Height in world "meters" that the camera is able to see // at the SpriteRenderer's depth. float worldUnitsSeen; if (camera.orthographic) { worldUnitsSeen = camera.orthographicSize * 2; } else { // Distance in front of the camera. float depth = Vector3.Dot(transform.position - camera.transform.position, view); // How tall the camera's view of the world is at this depth. worldUnitsSeen = depth * 2 * Mathf.Tan(camera.fieldOfView * Mathf.Deg2Rad/2); } // What PPU would make this render 1:1 at our target screen resolution? float desiredPPU = targetResolution / worldUnitsSeen; // Apply a compensating scale. float scale = renderer.sprite.pixelsPerUnit / desiredPPU; transform.localScale = new Vector3(scale, scale, scale); } } 
\$\endgroup\$
1
  • \$\begingroup\$ This worked perfectly! Already thinking of so many other uses for this. Thanks so much! \$\endgroup\$ Commented Feb 15 at 17:47

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.