Search Unity

Overdraw: SpriteRenderer in UI?

Discussion in 'Unity UI (uGUI) & TextMesh Pro' started by Marrt, Jul 10, 2015.

  1. Marrt

    Marrt

    Joined:
    Feb 7, 2012
    Posts:
    547
    Question:
    Should i start writing UI-scaling-functions for the SpriteRenderer or will the Unity-UI get an update to support non-quad geometry at some point in the future?

    Background:
    I want to use big circles as analog-pad indicators. Obviously this causes overdraw because the UI-System creates only quads (i can see the fps drop on the device while those circle images are active).

    The solution would be to have a mesh-geometry similar to the SpriteRenderer, that cuts out most transparent areas. You can actually use a SpriteRenderer in the UI but there are some restrictions:
    • SpriteRenderer will not be visible in ScreenSpace-Overlay mode, you need to use ScreenSpace-Camera
    • SpriteRenderer completely ignores the RectTransform except for centering in it, so scaling has to be done by yourself, but that can easily be done with some functions
    • At least one additional Drawcall if i use both normal GUI-Image and SpriteRenderers within the UI - but that is much less of a performance hit than the fillrate

    Picture:
    left circle: UI-Image, right circle: SpriteRenderer
    left side: Shaded Wirframe, right side: Overdraw
    upload_2015-7-10_11-57-58.png

    Thank you for reading up to this point.
     
  2. Marrt

    Marrt

    Joined:
    Feb 7, 2012
    Posts:
    547
    I ended up writing a costum scaling function for my UI-Spriterenderers. Now they look exactly like the previous UI-Images. Scaling function is simple:

    Code (CSharp):
    1.  
    2. public static SpriteRenderer AddScaledUISpriteRenderer(RectTransform rectTrans, int? sortingOrder = null){
    3.         //Adds a child GameObject to the UI-GameObject that copies the sprite of the image
    4.         //Needs to be a child because we need to scale it but still anchor its position to the old Object
    5.         //If we would add a SpriteRenderer to the actual Image-Object and scale it accordingly, children of it will be scaled too.
    6.        
    7.         float pxWidth        = rectTrans.rect.width;            //width  of the scaled UI-Object in pixel
    8.         float pxHeight        = rectTrans.rect.height;        //height of the scaled UI-Object in pixel
    9.        
    10.         Image uiImage        = rectTrans.GetComponent<Image>();
    11.        
    12.         float spriteSizeX    = uiImage.sprite.bounds.size.x;    //width  of the unscaled sprite in pixel
    13.         float spriteSizeY    = uiImage.sprite.bounds.size.y;    //height of the unscaled sprite in pixel
    14.        
    15.         //scale that needs to be applied to the SpriteRender-Gameobject to give it the same screen size as the UI-Image
    16.         float scaleX = pxWidth    /spriteSizeX;
    17.         float scaleY = pxHeight    /spriteSizeY;
    18.        
    19.        
    20.         //create new SpriteGameObject in UI
    21.         GameObject spriteGO        = new GameObject();
    22.         spriteGO.name            = rectTrans.name+"Sprite";                    //give it a name
    23.         spriteGO.layer            = LayerMask.NameToLayer("UI");                //culling layer, if needed
    24.         spriteGO.transform.localScale = new Vector3(scaleX, scaleY, 1F);    //scale the SpriteRenderer
    25.        
    26.         RectTransform sRect        = spriteGO.AddComponent<RectTransform>();    //add a rect Transform to replace the normal Transform
    27.         sRect.SetParent(rectTrans, false);                                    //make the image Object the new parent, so this will move with it      
    28.        
    29.         SpriteRenderer spriteR    = spriteGO.AddComponent<SpriteRenderer>();    //Add a sprite Renderer to the new Object
    30.         spriteR.sprite            = uiImage.sprite;                            //copy the Image's sprite
    31.         spriteR.color            = uiImage.color;                            //copy the Image's coloration
    32.         spriteR.enabled            = uiImage.enabled;                            //copy enabled state
    33.         spriteR.sortingOrder    = sortingOrder ?? 1;                        //needed to be at least 1 if the UI is drawn above a render texture
    34.                
    35.         uiImage.enabled            = false;    //disable the old UI-Image
    36.                        
    37.         return spriteR;
    38.     }
    39.  
    Previous overdraw:
    upload_2015-7-13_22-8-1.png
    New overdraw:
    upload_2015-7-13_22-8-28.png
     
    Last edited: Jul 13, 2015
    hungrybelome and IgorAherne like this.
  3. Miguel-Ferreira

    Miguel-Ferreira

    Joined:
    May 8, 2015
    Posts:
    90
    +1, this is one reason why UI is currently so slow, the overdraw is super heavy, specially on Android devices
     
  4. IgorAherne

    IgorAherne

    Joined:
    May 15, 2013
    Posts:
    347
    I would like to contribute.
    As long as we keep the sprite or even a Mesh quad parented to some parent-rect, we can stretch that parent rect and sprite will follow it.

    Just make sure to attach the following script to the child sprite (or child mesh quad)


    Code (CSharp):
    1.  
    2. using UnityEngine;
    3.  
    4. // Attach this script onto a sprite renderer.
    5. // Stretches a sprite to follow corners of parent rect.
    6. // Make sure it's parented to a "dummy" rectTransform and stretch that instead of our tranform.
    7. //
    8. // IgorAherne March 2017
    9. // Marrt  July 2015
    10. // https://forum.unity3d.com/threads/overdraw-spriterenderer-in-ui.339912/#post-3009616
    11. // https://forum.unity3d.com/threads/overdraw-spriterenderer-in-ui.339912/#post-3009616
    12.  
    13. [ExecuteInEditMode, RequireComponent(typeof(RectTransform))]
    14. public class QuadScalerRelativeToAnchors : MonoBehaviour {
    15.  
    16.  
    17.     [SerializeField, HideInInspector] RectTransform _myRectTransform;
    18.     [SerializeField, HideInInspector] RectTransform _parentRectTransf;
    19.     [SerializeField, HideInInspector] SpriteRenderer _mySpriteRenderer;
    20.  
    21.     void Reset() {
    22.         Start();
    23.     }
    24.  
    25.  
    26.     void Start() {
    27.         _parentRectTransf = transform.parent as RectTransform;
    28.         _mySpriteRenderer = GetComponent(typeof(SpriteRenderer)) as SpriteRenderer;
    29.         _myRectTransform = transform as RectTransform;
    30.     }
    31.  
    32.  
    33.  
    34.     void Update() {
    35.  
    36. #if UNITY_EDITOR
    37.         //if we are in execute in edit mode, in editor, we might not have some references yet.
    38.         //so establish them if needed:
    39.         if(_mySpriteRenderer == null  ||  _parentRectTransf == null  || _myRectTransform == null) {
    40.             Start();
    41.         }
    42. #endif
    43.  
    44.         keepScaleRelativeToParent();
    45.     }
    46.  
    47.  
    48.  
    49.     public void keepScaleRelativeToParent() {
    50.  
    51.         float pxWidth = _parentRectTransf.rect.width;            //width  of the scaled UI-Object in pixel
    52.         float pxHeight = _parentRectTransf.rect.height;        //height of the scaled UI-Object in pixel
    53.  
    54.         if (float.IsNaN(pxHeight)  ||  float.IsNaN(pxWidth)) {
    55.             //unity hasn't not yet initialized (usually happens during start of the game)
    56.             return;
    57.         }
    58.  
    59.         float spriteSizeX = 1;
    60.         float spriteSizeY = 1;
    61.  
    62.         if (_mySpriteRenderer) {
    63.             spriteSizeX = _mySpriteRenderer.sprite.bounds.size.x;  //width  of the unscaled sprite in pixel
    64.             spriteSizeY = _mySpriteRenderer.sprite.bounds.size.y;  //height of the unscaled sprite in pixel
    65.         }
    66.  
    67.         //scale that needs to be applied to the SpriteRender-Gameobject to give it the same screen size as the UI-Image
    68.         float scaleX = pxWidth / spriteSizeX;
    69.         float scaleY = pxHeight / spriteSizeY;
    70.  
    71.        
    72.         //create new SpriteGameObject in UI
    73.         this.gameObject.layer = LayerMask.NameToLayer("UI");            //culling layer, if needed
    74.         _myRectTransform.localScale = new Vector3(scaleX, scaleY, 1F);    //scale our SpriteRenderer
    75.  
    76.  
    77.         //position self in the middle of parent
    78.         _myRectTransform.anchoredPosition3D = new Vector3(0,0, _myRectTransform.anchoredPosition3D.z);
    79.         _myRectTransform.anchorMin = Vector2.one * 0.5f;
    80.         _myRectTransform.anchorMax = Vector2.one * 0.5f;
    81.  
    82.         float myLocalScale_x = Mathf.Max(0.0001f, _myRectTransform.localScale.x);
    83.         float myLocalScale_y = Mathf.Max(0.0001f, _myRectTransform.localScale.y);
    84.  
    85.         _myRectTransform.sizeDelta = new Vector2( Mathf.Max(0.0001f, _parentRectTransf.rect.width / myLocalScale_x),
    86.                                                   Mathf.Max(0.0001f, _parentRectTransf.rect.height / myLocalScale_y) );
    87.     }
    88.  
    89. }
    90.  
    91.  
     
    Last edited: Mar 30, 2017
    glenneroo and hungrybelome like this.
unityunity