Search Unity

UI Outline Effect - Still only multiplied outline colors possible?

Discussion in 'UGUI & TextMesh Pro' started by Cherno, Mar 13, 2017.

  1. Cherno

    Cherno

    Joined:
    Apr 7, 2013
    Posts:
    515
    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?
     
  2. Hosnkobf

    Hosnkobf

    Joined:
    Aug 23, 2016
    Posts:
    1,096
    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!?
     
  3. Cherno

    Cherno

    Joined:
    Apr 7, 2013
    Posts:
    515
    @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)?
     
  4. dadude123

    dadude123

    Joined:
    Feb 26, 2014
    Posts:
    789
    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...
     
  5. Hosnkobf

    Hosnkobf

    Joined:
    Aug 23, 2016
    Posts:
    1,096
    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.
     
  6. Cherno

    Cherno

    Joined:
    Apr 7, 2013
    Posts:
    515
    @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.
     
  7. dadude123

    dadude123

    Joined:
    Feb 26, 2014
    Posts:
    789
    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.
     
  8. Hosnkobf

    Hosnkobf

    Joined:
    Aug 23, 2016
    Posts:
    1,096
    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.
     
    Last edited: Mar 16, 2017
  9. Cherno

    Cherno

    Joined:
    Apr 7, 2013
    Posts:
    515
    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.
     
  10. dadude123

    dadude123

    Joined:
    Feb 26, 2014
    Posts:
    789
    Pretty sure its in the unity ui code on bitbucket
     
  11. Hosnkobf

    Hosnkobf

    Joined:
    Aug 23, 2016
    Posts:
    1,096
    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.
     
  12. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,689
    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)
     
    karl_jones likes this.
  13. SimRuJ

    SimRuJ

    Joined:
    Apr 7, 2016
    Posts:
    247
    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?
     
  14. Hosnkobf

    Hosnkobf

    Joined:
    Aug 23, 2016
    Posts:
    1,096
    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.
     
  15. SimRuJ

    SimRuJ

    Joined:
    Apr 7, 2016
    Posts:
    247
    @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<Outline>()" 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.
     
  16. Hosnkobf

    Hosnkobf

    Joined:
    Aug 23, 2016
    Posts:
    1,096
    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...
     
  17. SimRuJ

    SimRuJ

    Joined:
    Apr 7, 2016
    Posts:
    247
    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?)?
     
  18. Cherno

    Cherno

    Joined:
    Apr 7, 2013
    Posts:
    515
    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.
     
  19. Hosnkobf

    Hosnkobf

    Joined:
    Aug 23, 2016
    Posts:
    1,096
    here is the source code: https://bitbucket.org/Unity-Technol...ine.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.
     
  20. SimRuJ

    SimRuJ

    Joined:
    Apr 7, 2016
    Posts:
    247
    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. :/
     
  21. Cherno

    Cherno

    Joined:
    Apr 7, 2013
    Posts:
    515
    I wrote my own, but you'd need only the most basic one that just displays a flat color.
     
  22. SimRuJ

    SimRuJ

    Joined:
    Apr 7, 2016
    Posts:
    247
    @Cherno
    Oh, that's a pity.

    Something I just thought about (haven't tested it yet): Can't you just create an unclickable button that is a child of your normal button, lies behind it and uses the color you want your border to be, then just set it to be displayed whenever the original button is clicked and hide it when the option is disabled?
     
  23. wiztensai

    wiztensai

    Joined:
    Dec 6, 2022
    Posts:
    1
    hey, how to solve this? why dont just give the solid outline color unity!
     
    kiddvmn likes this.