简体   繁体   中英

Create smoothly rounded rectangle with Texture2D

I am trying to draw rounded rectangles which can be used as a Texture for any UI component. My goal is to create this rounded Texture with the Texture2D class by setting the pixels with the SetPixels32 function. I do not want to do this with a shader.

This post did that but with XNA instead of Unity. I ported it into Unity but the Edge is jaggy .

This is what it looks like in Unity:

在此处输入图片说明

Below is the ported code:

public int width = 256;
public int height = 140;
public int borderThickness = 1;  //Cannot be < 1
//Border shadow cannot be more than Border Radius
public int borderRadius = 40; //Cannot be < 1
public int borderShadow = 2;
public List<Color32> backgroundColors = new List<Color32>();
public List<Color32> borderColors = new List<Color32>();
public float initialShadowIntensity = 5f;
public float finalShadowIntensity = 5f;


private Texture2D resultTex;
public RawImage display;

void Start()
{
    backgroundColors.Add(new Color32(171, 0, 0, 255));
    backgroundColors.Add(new Color32(9, 48, 173, 255));

    borderColors.Add(new Color32(111, 8, 99, 255));
    borderColors.Add(new Color32(171, 4, 161, 255));

    resultTex = RectangleCreator.
        CreateRoundedRectangleTexture(width, height, borderThickness,
        borderRadius, borderShadow, backgroundColors, borderColors,
        initialShadowIntensity, finalShadowIntensity);

    display.texture = resultTex;
    display.SetNativeSize();
}



public class RectangleCreator
{
    public static Texture2D CreateRoundedRectangleTexture(int width, int height, int borderThickness, int borderRadius, int borderShadow, List<Color32> backgroundColors, List<Color32> borderColors, float initialShadowIntensity, float finalShadowIntensity)
    {
        if (backgroundColors == null || backgroundColors.Count == 0) throw new ArgumentException("Must define at least one background color (up to four).");
        if (borderColors == null || borderColors.Count == 0) throw new ArgumentException("Must define at least one border color (up to three).");
        if (borderRadius < 1) throw new ArgumentException("Must define a border radius (rounds off edges).");
        if (borderThickness < 1) throw new ArgumentException("Must define border thikness.");
        if (borderThickness + borderRadius > height / 2 || borderThickness + borderRadius > width / 2) throw new ArgumentException("Border will be too thick and/or rounded to fit on the texture.");
        if (borderShadow > borderRadius) throw new ArgumentException("Border shadow must be lesser in magnitude than the border radius (suggeted: shadow <= 0.25 * radius).");

        Texture2D texture = new Texture2D(width, height, TextureFormat.ARGB32, false);
        Color32[] color = new Color32[width * height];

        for (int x = 0; x < texture.width; x++)
        {
            for (int y = 0; y < texture.height; y++)
            {
                switch (backgroundColors.Count)
                {
                    case 4:
                        Color32 leftColor0 = Color32.Lerp(backgroundColors[0], backgroundColors[1], ((float)y / (width - 1)));
                        Color32 rightColor0 = Color32.Lerp(backgroundColors[2], backgroundColors[3], ((float)y / (height - 1)));
                        color[x + width * y] = Color32.Lerp(leftColor0, rightColor0, ((float)x / (width - 1)));
                        break;
                    case 3:
                        Color32 leftColor1 = Color32.Lerp(backgroundColors[0], backgroundColors[1], ((float)y / (width - 1)));
                        Color32 rightColor1 = Color32.Lerp(backgroundColors[1], backgroundColors[2], ((float)y / (height - 1)));
                        color[x + width * y] = Color32.Lerp(leftColor1, rightColor1, ((float)x / (width - 1)));
                        break;
                    case 2:
                        color[x + width * y] = Color32.Lerp(backgroundColors[0], backgroundColors[1], ((float)x / (width - 1)));
                        break;
                    default:
                        color[x + width * y] = backgroundColors[0];
                        break;
                }

                color[x + width * y] = ColorBorder(x, y, width, height, borderThickness, borderRadius, borderShadow, color[x + width * y], borderColors, initialShadowIntensity, finalShadowIntensity);
            }
        }

        texture.SetPixels32(color);
        texture.Apply();
        return texture;
    }

    private static Color32 ColorBorder(int x, int y, int width, int height, int borderThickness, int borderRadius, int borderShadow, Color32 initialColor, List<Color32> borderColors, float initialShadowIntensity, float finalShadowIntensity)
    {
        Rect internalRectangle = new Rect((borderThickness + borderRadius), (borderThickness + borderRadius), width - 2 * (borderThickness + borderRadius), height - 2 * (borderThickness + borderRadius));


        Vector2 point = new Vector2(x, y);
        if (internalRectangle.Contains(point)) return initialColor;

        Vector2 origin = Vector2.zero;

        if (x < borderThickness + borderRadius)
        {
            if (y < borderRadius + borderThickness)
                origin = new Vector2(borderRadius + borderThickness, borderRadius + borderThickness);
            else if (y > height - (borderRadius + borderThickness))
                origin = new Vector2(borderRadius + borderThickness, height - (borderRadius + borderThickness));
            else
                origin = new Vector2(borderRadius + borderThickness, y);
        }
        else if (x > width - (borderRadius + borderThickness))
        {
            if (y < borderRadius + borderThickness)
                origin = new Vector2(width - (borderRadius + borderThickness), borderRadius + borderThickness);
            else if (y > height - (borderRadius + borderThickness))
                origin = new Vector2(width - (borderRadius + borderThickness), height - (borderRadius + borderThickness));
            else
                origin = new Vector2(width - (borderRadius + borderThickness), y);
        }
        else
        {
            if (y < borderRadius + borderThickness)
                origin = new Vector2(x, borderRadius + borderThickness);
            else if (y > height - (borderRadius + borderThickness))
                origin = new Vector2(x, height - (borderRadius + borderThickness));
        }

        if (!origin.Equals(Vector2.zero))
        {
            float distance = Vector2.Distance(point, origin);

            if (distance > borderRadius + borderThickness + 1)
            {
                return Color.clear;
            }
            else if (distance > borderRadius + 1)
            {
                if (borderColors.Count > 2)
                {
                    float modNum = distance - borderRadius;

                    if (modNum < borderThickness / 2)
                    {
                        return Color32.Lerp(borderColors[2], borderColors[1], (float)((modNum) / (borderThickness / 2.0)));
                    }
                    else
                    {
                        return Color32.Lerp(borderColors[1], borderColors[0], (float)((modNum - (borderThickness / 2.0)) / (borderThickness / 2.0)));
                    }
                }


                if (borderColors.Count > 0)
                    return borderColors[0];
            }
            else if (distance > borderRadius - borderShadow + 1)
            {
                float mod = (distance - (borderRadius - borderShadow)) / borderShadow;
                float shadowDiff = initialShadowIntensity - finalShadowIntensity;
                return DarkenColor(initialColor, ((shadowDiff * mod) + finalShadowIntensity));
            }
        }

        return initialColor;
    }

    private static Color32 DarkenColor(Color32 color, float shadowIntensity)
    {
        return Color32.Lerp(color, Color.black, shadowIntensity);
    }
}

I tried fixing the black jagged edge by replacing the

return Color32.Lerp(color, Color.black, shadowIntensity);

with

return Color32.Lerp(color, Color.clear, shadowIntensity);

but that didn't smooth it with a transparent color. It removed the black color but made it more jagged.

Here is what it looks like:

在此处输入图片说明

How can I smooth out the texture edge?

When I take this code and put it on a RawImage in a UICanvas it looks fine. This is default setup you get with Game Object -> UI -> Raw Image

I just attached the component to the rawimage and set the Display property to itself;

我的层次结构

我的结果

用像素网格放大

If you want unity to do some antialiasing, you can increase the resolution of the raw image and remove the call to display.SetNativeSize(); But then you will need to set the actual size of the rectangle using its transform component.

   using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class NewBehaviourScript : MonoBehaviour {


public int width = 256;
public int height = 140;
public int borderThickness = 1;  //Cannot be < 1
//Border shadow cannot be more than Border Radius
public int borderRadius = 40; //Cannot be < 1
public int borderShadow = 2;
public List<Color32> backgroundColors = new List<Color32>();
public List<Color32> borderColors = new List<Color32>();
public float initialShadowIntensity = 5f;
public float finalShadowIntensity = 5f;

public float resolutionmultiplier = 4f;


private Texture2D resultTex;
public RawImage display;

public RectTransform rt;

void Start()
{
    backgroundColors.Add(new Color32(171, 0, 0, 255));
    backgroundColors.Add(new Color32(9, 48, 173, 255));

    borderColors.Add(new Color32(111, 8, 99, 255));
    borderColors.Add(new Color32(171, 4, 161, 255));

    resultTex = RectangleCreator.
        CreateRoundedRectangleTexture(4, width, height, borderThickness,
        borderRadius, borderShadow, backgroundColors, borderColors,
        initialShadowIntensity, finalShadowIntensity);

    display.texture = resultTex;
    rt.sizeDelta = new Vector2(width, height);
}



public class RectangleCreator
{
    public static Texture2D CreateRoundedRectangleTexture(int resolutionmultiplier, int width, int height, int borderThickness, int borderRadius, int borderShadow, List<Color32> backgroundColors, List<Color32> borderColors, float initialShadowIntensity, float finalShadowIntensity)
    {
       // if (backgroundColors == null || backgroundColors.Count == 0) throw new ArgumentException("Must define at least one background color (up to four).");
      //  if (borderColors == null || borderColors.Count == 0) throw new ArgumentException("Must define at least one border color (up to three).");
      //  if (borderRadius < 1) throw new ArgumentException("Must define a border radius (rounds off edges).");
      //  if (borderThickness < 1) throw new ArgumentException("Must define border thikness.");
     //   if (borderThickness + borderRadius > height / 2 || borderThickness + borderRadius > width / 2) throw new ArgumentException("Border will be too thick and/or rounded to fit on the texture.");
     //   if (borderShadow > borderRadius) throw new ArgumentException("Border shadow must be lesser in magnitude than the border radius (suggeted: shadow <= 0.25 * radius).");

        width = width * resolutionmultiplier;
        height = height * resolutionmultiplier;

        Texture2D texture = new Texture2D(width, height, TextureFormat.ARGB32, false);
        Color32[] color = new Color32[width * height];

        for (int x = 0; x < texture.width; x++)
        {
            for (int y = 0; y < texture.height; y++)
            {
                switch (backgroundColors.Count)
                {
                    case 4:
                        Color32 leftColor0 = Color32.Lerp(backgroundColors[0], backgroundColors[1], ((float)y / (width - 1)));
                        Color32 rightColor0 = Color32.Lerp(backgroundColors[2], backgroundColors[3], ((float)y / (height - 1)));
                        color[x + width * y] = Color32.Lerp(leftColor0, rightColor0, ((float)x / (width - 1)));
                        break;
                    case 3:
                        Color32 leftColor1 = Color32.Lerp(backgroundColors[0], backgroundColors[1], ((float)y / (width - 1)));
                        Color32 rightColor1 = Color32.Lerp(backgroundColors[1], backgroundColors[2], ((float)y / (height - 1)));
                        color[x + width * y] = Color32.Lerp(leftColor1, rightColor1, ((float)x / (width - 1)));
                        break;
                    case 2:
                        color[x + width * y] = Color32.Lerp(backgroundColors[0], backgroundColors[1], ((float)x / (width - 1)));
                        break;
                    default:
                        color[x + width * y] = backgroundColors[0];
                        break;
                }

                color[x + width * y] = ColorBorder(x, y, width, height, borderThickness, borderRadius, borderShadow, color[x + width * y], borderColors, initialShadowIntensity, finalShadowIntensity);
            }
        }

        texture.SetPixels32(color);
        texture.Apply();
        return texture;
    }

    private static Color32 ColorBorder(int x, int y, int width, int height, int borderThickness, int borderRadius, int borderShadow, Color32 initialColor, List<Color32> borderColors, float initialShadowIntensity, float finalShadowIntensity)
    {
        Rect internalRectangle = new Rect((borderThickness + borderRadius), (borderThickness + borderRadius), width - 2 * (borderThickness + borderRadius), height - 2 * (borderThickness + borderRadius));


        Vector2 point = new Vector2(x, y);
        if (internalRectangle.Contains(point)) return initialColor;

        Vector2 origin = Vector2.zero;

        if (x < borderThickness + borderRadius)
        {
            if (y < borderRadius + borderThickness)
                origin = new Vector2(borderRadius + borderThickness, borderRadius + borderThickness);
            else if (y > height - (borderRadius + borderThickness))
                origin = new Vector2(borderRadius + borderThickness, height - (borderRadius + borderThickness));
            else
                origin = new Vector2(borderRadius + borderThickness, y);
        }
        else if (x > width - (borderRadius + borderThickness))
        {
            if (y < borderRadius + borderThickness)
                origin = new Vector2(width - (borderRadius + borderThickness), borderRadius + borderThickness);
            else if (y > height - (borderRadius + borderThickness))
                origin = new Vector2(width - (borderRadius + borderThickness), height - (borderRadius + borderThickness));
            else
                origin = new Vector2(width - (borderRadius + borderThickness), y);
        }
        else
        {
            if (y < borderRadius + borderThickness)
                origin = new Vector2(x, borderRadius + borderThickness);
            else if (y > height - (borderRadius + borderThickness))
                origin = new Vector2(x, height - (borderRadius + borderThickness));
        }

        if (!origin.Equals(Vector2.zero))
        {
            float distance = Vector2.Distance(point, origin);

            if (distance > borderRadius + borderThickness + 1)
            {
                return Color.clear;
            }
            else if (distance > borderRadius + 1)
            {
                if (borderColors.Count > 2)
                {
                    float modNum = distance - borderRadius;

                    if (modNum < borderThickness / 2)
                    {
                        return Color32.Lerp(borderColors[2], borderColors[1], (float)((modNum) / (borderThickness / 2.0)));
                    }
                    else
                    {
                        return Color32.Lerp(borderColors[1], borderColors[0], (float)((modNum - (borderThickness / 2.0)) / (borderThickness / 2.0)));
                    }
                }


                if (borderColors.Count > 0)
                    return borderColors[0];
            }
            else if (distance > borderRadius - borderShadow + 1)
            {
                float mod = (distance - (borderRadius - borderShadow)) / borderShadow;
                float shadowDiff = initialShadowIntensity - finalShadowIntensity;
                return DarkenColor(initialColor, ((shadowDiff * mod) + finalShadowIntensity));
            }
        }

        return initialColor;
    }

    private static Color32 DarkenColor(Color32 color, float shadowIntensity)
    {
        return Color32.Lerp(color, Color.black, shadowIntensity);
    }
}
}

抗锯齿版

EDIT:

After increasing the texture resolution width, height to about 2048 , 624 , increase the borderRadius to 300 . Finally make sure that "Low Resolution Aspect Ratios" is not enabled on the Game View. This causes the image to be blurry.

在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM