UI Outline Effect - Still only multiplied outline colors possible?

I have researched this topic for months now and it seems like there is still no solution for having a colored outline around a UI element that doesn’t just multiply from the border pixels, but rather just draws a solid, specified color.

Since this topic has popped up then and again over the years since the ew UI system was released, I have to ask: Does anybody have a solution to this, or do I have to write my own based on the nativ code?

When I add an outline (Add Component > UI > Effects > Outline) I get an outline which has a solid color which I can specify. No multiplied colors at my side. Not sure if we talk about the same thing!?

@Hosnkobf

So if you take any UI image, add the Outline component, set the color to white and the effect distance to 1,1, you do NOT get a result like the right apple:

, but instead solid white pixels instead of the border pixel color multiplied by white (which results in no change)?

Why not make a simple shader with a mask and a tint color that does what you want?
Should be pretty easy with shaderforge or so…

Now I know what you mean.
yeah, it seems the outline copies the sprite, keeps its colors and colorizes the vertices which leads to wrong results.
Seems like you need to write a new shader yourself. But this is a good idea to add to my Better UI asset. I will investigate this a little bit when I have time.

@dadude123 @Hosnkobf
The problem with a pure shader-based solution is that to my knowledge, you can’t draw pixels beyond the sprite’s mesh edges. That means that if the actual image has at least one transparent pixel around it, it works fine because you can change that pixel to your outline color, but of the image pixel is right at the edge of the bitmap, you can’t access any pixels that would lie beyond the bitmap’s borders (which the mesh is based on).
I already wrote a shader that draws an outline with the aforementioned limitation, and I also found a shader which doesn’t draw an outline but rather an “inline”. So, I think the only solution is an image effect that Unity’s Outline script uses to actually draw multiple meshes around the original one.

That makes sense, I see the problem.

After thinking about it there’s only one possible solution: simple “extending”.
You would expand (just scale) the geometry of the sprite by some factor, maybe 1.5x
And then you re-map the UV coordinates in the pixel shader (or vertex shader for better performance).

For that your sprites texture either needs to be set to clamp-mode, or you need to do some tricks in the shader, or your sprite simply needs some padding around it.
Thats how text-mesh pro does its rendering by the way.

There’s no other way to do this because your sprites “edges” are only determined by its pixel values.
For example if you had a big circle with a hole in the middle as a sprite, then your outline should be at the inside of the hole as well, right? So obviously there’s no trickery possible with only geometry or copying the sprite (as copy + scale would move the inner circle further out, so it would be occluded, which is exactly the opposite of what you want).

Unless you change the problem definition, that’s the only solution I can see. Maybe elaborate some more if that isn’t what you’re aiming for.

I think it is possible with a custom shader applied to the outline.
I believe this could be achieved by extending Outline and implement IMaterialModifier. However, I have no experience (yet) with IMaterialModifier.

EDIT: Nevermind, from my further research I think the material would be changed also for the originial image… so this wouldn’t work. Not sure if it is possible to change the material in the extended Outline class at another place.

The thing is that the Outline component already does exactly what I want, except that it multiplies the color instead of straight-up using the defined outline color. All I would have to do is create a modifed copy of the Outline script and and change the way it draws the color. However, I can’t seem to find the Outline script, it’s not listed in the Unity Downloads section (where hidden things like the Standard Shader are included) and it’s also not possible to edit the script directly from inside Unity by clicking the edit script… button. If anyone knows where to find the actual script file, I’d be very happy to know.

Pretty sure its in the unity ui code on bitbucket

I always use a decompiler (e.g. Telerik JustDecompile) to view the source code.

The problem of the effects is that they don’t have a custom material. they always use the same material of the graphic component. The color just changes the vertex colors. It has the same effect as if you change the color of the image: it multiplies.
It is possible to work around I guess if you use a material for your image which uses a vertex information as variable (e.g. tangent.z) which can make the color of the whole sprite white. then you would need to extent the outline component and set that variable in the “ModifyMesh” method to have it white. then the vertex colors should apply as expected.

No need to decompile the UI system since it’s on bitbucket.
You might also want to check out the other “outline” components in the UI Extensions project, we have a few there I believe (link in signature)

1 Like

I’ve just run into the same problem with Unity 2017.3.1f1:
I want to add a red border to the buttons (I’m using the default UISprite) of enabled settings in my settings menu but no matter if I do it through the UI or code it, the bright red border around my dark gray button is always dark red. I even created a white blob sprite with a transparent area around it but it did’t change noticeably with it.

Is there a workaround for that directly in Unity now or how did everyone here fix it?

Do you have the border in the same sprite? if so, then the sprite color must be set to white to see the original color.
If not: please describe your setup.

@Hosnkobf
No, I added it through the effect “Outline” (like Cherno probably did).
All of my buttons use Unity’s UISprite (the one with the rounded corners, transparent background, dark border and white inside) and the transition “Color Tint”. The color of the sprite is set to white btw, which is the default setting. I have e.g. a couple of buttons to set the background color, which range from black to white (normal color) but all of them use red+0.5 alpha as the highlighted color and red as the pressed color.

I added the border through “Component - UI - Effects - Outline” (“myButton.AddComponent()” in code), set the “Effect Color” to red and the distance to (3,-3), which gives the white button a red outline, the dark gray button a dark red one and the black button a black one.

I also created my own sprite (square with rounded corners, white inside, transparent background) and while the “border” of the button (so just inside the actual outline) is slightly brighter with it (because UISprite has an already dark border while mine hasn’t), it still gives the black button a black outline, the dark gray button a dark red one,… - so the supposed color of the outline still gets multiplied by the color of the button.

Sorry, that I didn’t got it in the first place… Now I understand your problem and I also did a quick research how the outline is actually implemented.
It simply takes the original sprite with the assigned material and renders it behind the original sprite with an offset.

That is why only a black outline works (unless the original sprite is white).

I have a solution in mind but that is very complicated. I describe it anyways:

  1. write an own shader for the original sprite where you can adjust the contrast and gamma. The color for the sprite should be applied after contrast and gamma calculation. The contrast and gamma variables must be set through some vertex data (for example TEXCOORD1 which is actually not used by sprites).
  2. write your own Outline class which does the same as the normal one but also sets the TEXCOORD1 values in a way that the sprite becomes white (contrast: 0; gamma: 2 - I guess)
  3. assign the material to the sprite and use your custom outline component

This is a very complicated solution, but a good Idea for my Asset Better UI where I already implemented a system for Materials which use vertex data for variables…

From what I’ve seen the outline effect is simply a shadow that is shown on all 4 sides instead of just 1 or 2.
I haven’t tested it with sprites that aren’t white yet but I guess they’d make the outline even darker.

You are right, it’s very complicated and really not worth the trouble of writing an extra shader (don’t know how to do it anyway) to fix something that small that doesn’t work properly. Is there no other way? Someone mentioned that the original “Outline” script is on bitbucket, would it not be possible to change some stuff in it (so it doesn’t multiply but only uses the set color) and use it instead of the original one?

How did you manage to get it to work in your project, @Cherno (did you?)?

I used the most straight forward solution, not elegant but works:
Make four copies of the Image element, give them a material which uses a shader that displays only the outline color for all non-transparent pixels, and offset them one pixel in each direction.

here is the source code: https://bitbucket.org/Unity-Technologies/ui/src/a3f89d5f7d145e4b6fa11cf9f2de768fea2c500f/UnityEngine.UI/UI/Core/VertexModifiers/Outline.cs?at=2017.3&fileviewer=file-view-default

but as I told you: it uses the same image and material as the Graphic-Component. That’s why you would need a material which you can modify through the vertex data.

Thanks! Did you use an existing shader or write your own?

Thanks! Yes, that really sucks.
I guess unless someone else has already created a shader that works for this, I’ll either have to live with it or create a bunch of new sprites, then switch the original one with one that already has the right color and an outline. :confused: