Open AAAYaKo opened 1 month ago
Closest Analog: Outside OutlineColor = FillColor OutlineWidth = 1
It's important for small icons and elements in the form of dots and lines. Need to somehow shift the radius by 1 pixel for the fill and outline.
Also, when the outline is off or the width of the outline is 0, use fillColor instead of (0,0,0,0).
Maybe this is due to I have antialiasing in the UI shaders. Unity’s built-in image components are merely quads, but these sdf-quads render rounded corners. Therefore, this feature might be more preferable than a shader without antialiasing.
// generate UI shape by crop by alpha
float delta = fwidth(dist);
float graphicAlpha = 1 - smoothstep(-_OutlineWidth - delta, -_OutlineWidth, dist);
float graphicAlpha = 1 - smoothstep(-_OutlineWidth, -_OutlineWidth, dist);
But as you say, it is inconvenient in some cases (like dot rendered game view). Maybe I should seperate shader with antialiasing and non-antialiasing.
I suggest adding a keyword to the shaders to disable AA for now. And a parameter in the SDFUI. Also, maybe inside/outside can be made via keywords too.
But still for antialiasing mode it is necessary to add some correction offset, for example 0.5, so that the result would be closer to the display without antialiasing.
It might be worth trying several antialiasing methods, maybe SubPixel or SuperSampled antialiasing will be more accurate and there will be no need to add an offset. If you find one, keep the current one, because I think it's probably faster and would give you the option to switch between 3 modes: No antialiasing Fast (or if you find a more accurate but faster one) Quality (But it needs to be tested in practice)
Might be useful https://github.com/etienne-p/UnitySDFAntialiasing https://drewcassidy.me/2020/06/26/sdf-antialiasing/
Thanks for the suggestion. I will implement both subsampling and supersampling for quality mode. Also I'm not well understand to shader keyword but according to the sample you attached, maybe I can replace if else statement in my sdf shader to shader keyword (for INSIDE
/OUTSIDE
SUB_SAMPLING
/SUPER_SAMPLING
). So I will implement that too.
I added the Antialiasing method option. It is difficult to compare the effect of antialiasing by eye, but for subpixel antialiasing, I can see a color gradient at the edge of the shape when scaling up the game view in the editor, so I believe my implementation is correct. This color gradient seems uncomfortable in this attached picture. But it should be effective for antialiasing for the actual display.
Previous method (faster method, 1px expanded) | Super-sampling-antialiasing | Subpixel-antialiasing |
---|
I take the above picture from here
Yes, it's hard for me to evaluate the effect of these changes yet, but I think we need to look into the anti-aliasing some more.
Maybe problem can be solved by hinting. https://en.wikipedia.org/wiki/Font_rasterization
I had certain graphical problems since I'm not a graphical programmer, but I think this direction is right. If you add floor in this place for small rectangles, the results are correct. Perhaps if you find the right place for rounding (or find another way to snap to the pixel grid.) you can get a pixel perfect vector as a result without artifacts.
For information during the test I commented out expand code for faster mode, screenshot from super sampling mode. Faster mode with my rounding looks messy.
I have look agein my sdf shader and I think my shader is little wong. My previouse implementation, I effect antialiasing from inside direction and maybe this implementation is not standard because most of sample of antialiasing seems bigger than non-antialiased graphic (my antialiased graphic smaller than non-antialiased shape).
I compared antialiasing from inside and outside (Blue is sdf-quad).
From Inside (My previouse implementation) | From Outside |
---|
If I effect antialiasing from outside, your suggestion to use hinting seems like it will have effect because I can prevent 1px edge expand for sdf shape's edge that does not need antialiasing by that (like the edge part that is not rounded).
If I use antialiasing from the outside, I need to expand the ui mesh a bit (to prevent shape clipping by rect). But I do not need to use offset on shader sampling like in my previous implementation.
In the latest update I changed the antialiasing direction from inside to outside. And I removed the shader sampling offset to replace it with hinting in the future (hinting is not implemented yet. I will look for a hinting enabled antialiasing implementation).
There is now an anti-aliasing artifact.
I think this line is redundant.
Before After If we use antialiasing on outside of the edge rectangle looks bigger than expected. I think it's more correct to make antialiasing in the middle of the edge. Code for sdf quad
Maybe we should move the antialiasing code to SDFUtils? It looks like copy-paste and maybe it would be better to make a shared function.
Maybe we should move the antialiasing code to SDFUtils? It looks like copy-paste and maybe it would be better to make a shared function.
Sure it is. we can commonize the process of determine sampling position and process after determined distance from shape. Maybe it is preferable to move this process to other hlsl file and include it to original code.
Also, as you say, it seems preferable to antialiase from the both sides of the edge rather than from the outside.
As written in this article fwidth is an expensive operation that you should not call too much. Do we need to calculate sdelta in case the shadow is off? http://www.numb3r23.net/2015/08/17/using-fwidth-for-distance-based-anti-aliasing/
Maybe calculations related with shadow also should be hidden behind a keyword?
I see, seen the article. Certnly, it is overhead that culclate shadow's delta in case of no used. Need to add keyword to shader's to switch shadow process enable/disable. Currently I am doing to remove tiny black color from shape edge. I will push update once this and move shadow related process to keyword's section is done.
I suggest changing sample positions for SSAA. https://www.beyond3d.com/content/articles/122/8 According to this article sparse grid anti-aliasing position of the samples is the most efficient one.
I accidentally referenced a different issue id in the commit message. So I created this message to announce my update. 4f68e7f
In the previous update, there is a problem in shader that runtime attached sdf-ui is not rendered in build-app. Maybe this is because I use shader keyword as shader_feature instead of multi_compile, my shader's keyword branch is too complex for compiler, so there are some critical part for shader has dropped.
In the latest update, I replace all shader keyword from shader_feature to multi_compile.
Multi-compile keyword increases build time and build app size (but build time will not increase if build cache remains after first build). I may need to change some keywords back from multi_compile to shader_feature that is not seem to cause the above problem.
I have made a notice of this as it is a critical issue. 330555a
PS: I have misunderstood cause of problem.
shader_feature
and multi_compile
mostly behave the same, but shader_feature
are ignoring compile for keyword branch that is not used when building. I put shader in resources folder to always have refarence of it, but it is not enough to enable shader_feature
in compile.
If all shader_feature
branch is compiled as same as multi_compile
, build time and app size is same.
So my latest update to replace shader_feature
with multi_compile
seems not wrong, but in order to decrease build time and app size, I have to optimize my shader code (not keyword branch).
As I understand it with 2 passes in Build-In RP, antialiasing doesn't work. We need to find out how antialiasing works in TMP. Maybe their implementation doesn't involve 2 passes, but I could be wrong.
(I deleted this message becuse of my miss understanding)
For antialiasing problem, I remaked sdf shader (not pushed yet because add shadow in single pass rendering still remaine) with reference to TMPro's shader (TMP_SDF.shader and TMP_SDF-Mobile.shader). I just changed bellow.
// Unity-SDF-UI-Toolkit\Runtime\Resources\Shader\SDF-UI\{Shape-Name}.shader
// Befor
Blend SrcAlpha OneMinusSrcAlpha, One OneMinusSrcAlpha
// After
Blend One OneMinusSrcAlpha
// Unity-SDF-UI-Toolkit\Runtime\Resources\Shader\SDF-UI\ClipByDistance.hlsl
// Befor
#ifdef SDF_UI_AA_SUBPIXEL
half4 layer0 = lerp(half4(1, 1, 1, 0), _OutlineColor, outlineAlpha);
half4 layer1 = color * graphicAlpha;
#else
half4 layer0 = half4(_OutlineColor.rgb, _OutlineColor.a * outlineAlpha);
half4 layer1 = half4(color.rgb, color.a);
#endif
half4 layer2 = blend(layer0, layer1, _OutlineColor, color, graphicAlpha);
half4 effects = layer2;
// After
half4 layer0 = _OutlineColor; layer0.rgb *= layer0.a;
half4 layer1 = color; layer1.rgb *= layer1.a;
half4 layer2 = lerp(layer0, layer1, graphicAlpha);
half4 effects = layer2 * outlineAlpha;
Here is the result (blue color is the background)
Due to the screen capture problem, the color gradation between red and blue appears whitish, but the actual display works fine.
I have not seen the whole code, so maybe there are some of my missed.
I also added the shadow in the same pass of the shape and outline. The shader is now rendered in a single pass. Currently, it should be fixed that antialiasing doesn't work when the outline width is zero. Currently I only referenced the implementation from TMPro's shader that related to this plugin's issue about antialiasing and multipass. So maybe there are others that are useful to reference in other parts of TMPro's shader.
If so, I think this section can be removed from the readme
Yes, it is. This section is no longer needed.
I also added the shadow in the same pass of the shape and outline. The shader is now rendered in a single pass. Currently, it should be fixed that antialiasing doesn't work when the outline width is zero. Currently I only referenced the implementation from TMPro's shader that related to this plugin's issue about antialiasing and multipass. So maybe there are others that are useful to reference in other parts of TMPro's shader.
I think we need to copy the shadow settings. Current Power, Width, Blur are not very clear and do not look like other programs. In opposition to this, TMP gives quite typical settings. I would also like to be able to use HDR colors like in TMP.
Replaced shadow parameters to similar for TMPro's it and supported HDR in color properties. Currently, I left the previous soft shadow implementation in the shader (not similar to TMPro). TMPro uses only distance from shape and saturate()
, so soft shadow seems blur linearly. This plugin's previouse blur implementation is based on this (smoothstep()
based) and I think this is looks great than TMPro's linerly soft shadow (but it is problem that soft shadow result is smaller as much as to increase softness value than actual size).
fixed newtonsoft miss reference
Maybe it's worth placing shaders in the Hidden group, like the default and tmp shaders?
I found that when this setting is disabled, shaders don't compile. It is worth adding this to the readme.
Also I think 6000+ shader variants is too much. It would be nice to find a solution to this issue. Maybe we should give up keyword in certain places and return if logic. Maybe we should look at how disabling properties in TMP works.
#pragma multi_compile_local _ UNITY_UI_CLIP_RECT
#pragma multi_compile_local _ UNITY_UI_ALPHACLIP
#pragma multi_compile_local _ SDF_UI_AA_FASTER
#pragma multi_compile_local _ SDF_UI_AA_SUPER_SAMPLING
#pragma multi_compile_local _ SDF_UI_AA_SUBPIXEL
#pragma multi_compile_local _ SDF_UI_ONION
#pragma multi_compile_local _ SDF_UI_OUTLINE_INSIDE
#pragma multi_compile_local _ SDF_UI_OUTLINE_OUTSIDE
#pragma multi_compile_local _ SDF_UI_SHADOW_ENABLED
I see, above is a shader keyword currently in use, it is ideal to leave only keyword for antialiasing option (SDF_UIAA*).
// ideal
#pragma multi_compile_local _ UNITY_UI_CLIP_RECT
#pragma multi_compile_local _ UNITY_UI_ALPHACLIP
#pragma multi_compile_local _ SDF_UI_AA_FASTER SDF_UI_AA_SUPER_SAMPLING SDF_UI_AA_SUBPIXEL
For now, I leave all shader keyword, but changed layout to prevent generating unintended variant.
#pragma multi_compile_local _ UNITY_UI_CLIP_RECT
#pragma multi_compile_local _ UNITY_UI_ALPHACLIP
#pragma multi_compile_local _ SDF_UI_AA_FASTER SDF_UI_AA_SUPER_SAMPLING SDF_UI_AA_SUBPIXEL
#pragma multi_compile_local SDF_UI_OUTLINE_INSIDE SDF_UI_OUTLINE_OUTSIDE
#pragma multi_compile_local _ SDF_UI_ONION
#pragma multi_compile_local _ SDF_UI_SHADOW_ENABLED
Already much better, but still about 800 variants sound like a lot.
Keyword that start with SDF_UI_OUTLINE*
is used only here.
// 1.
#ifdef SDF_UI_OUTLINE_INSIDE
tmp0 = 1 - saturaterange((_ShadowWidth - _ShadowDilate) - _ShadowBlur - delta, (_ShadowWidth - _ShadowDilate) + delta, dist);
tmp1 = 1 - smoothstep((_ShadowWidth - _ShadowDilate) - _ShadowBlur - delta, (_ShadowWidth - _ShadowDilate) + delta, dist);
#elif SDF_UI_OUTLINE_OUTSIDE
tmp0 = 1 - saturaterange(_OutlineWidth + (_ShadowWidth - _ShadowDilate) - _ShadowBlur - delta, _OutlineWidth + (_ShadowWidth - _ShadowDilate) + delta, dist);
tmp1 = 1 - smoothstep(_OutlineWidth + (_ShadowWidth - _ShadowDilate) - _ShadowBlur - delta, _OutlineWidth + (_ShadowWidth - _ShadowDilate) + delta, dist);
#endif
// 2.
#ifdef SDF_UI_OUTLINE_INSIDE
graphicAlpha = 1 - saturaterange(-_OutlineWidth - delta, -_OutlineWidth + delta, dist);
outlineAlpha = 1 - saturaterange(-delta, delta, dist);
#elif SDF_UI_OUTLINE_OUTSIDE
outlineAlpha = 1 - saturaterange(_OutlineWidth - delta, _OutlineWidth + delta, dist);
graphicAlpha = 1 - saturaterange(-delta, delta, dist);
#endif
Maybe replacing here to properties can reduce variant to half (400) without adding if statement.
// 1. Add _ShadowBorder
tmp0 = 1 - saturaterange(_ShadowBorder - _ShadowBlur - delta, _ShadowBorder + delta, dist);
tmp1 = 1 - smoothstep(_ShadowBorder - _ShadowBlur - delta, _ShadowBorder + delta, dist);
// 2. Add _OutlineBorder
outlineAlpha = 1 - saturaterange(_OutlineBorder - delta, _OutlineBorder + delta, dist);
graphicAlpha = 1 - saturaterange(-delta, delta, dist);
Comparison of SDF Quad and Unity Image (Rect 5×100) Also, with the outline off, there is a thin edge of black color. You can see better if you turn on outline, set width to 0 and change color.