combining two smoothsteped shapes results in weirdness. How can I improve it?

I’m drawing two circles with smoothstep. When I combine them, the smoothstep creates a kind of multiply blend, distorting the two shapes. Is there a way to avoid this when combining two or more smoothstep shapes ? Thanks!


fixed4 frag(v2f i) : SV_Target
                //Draw ring using smoothstep
                float alpha = smoothstep(_Inner, _Inner + _Soften, length(i.position.xy))
                    - smoothstep(_Outer, _Outer + _Soften, length(i.position.xy));
                fixed4 ringColor = fixed4(_RingColor.rgb, 1) * alpha;

                //Draw circle using smoothstep
                float2 offset = float2(i.position.x + 0.15, i.position.y - 0.01 - (_Inner / 2 + _Outer / 2));
                float alphaDot = 1 - smoothstep(_Outer / 2 - _Inner / 2, (_Outer / 2 - _Inner / 2) + _Soften, length(offset));
                float4 dotColor = float4(_RingColor.rgb, 1) * alphaDot;

                //add the two colors
                return ringColor + dotColor;

Not really the way you’re combining the two shapes as you just add them. Adding them means their value gets added together. So at any overlap area the value is added. So for example if you have an alpha value of 0.5 for both shapes at the same point, the values would add up to 1, so full color even though the point is outside the actual shape as it’s in the smooth blending zone of both shapes. As a solution you would need to pick the largest alpha value of all shapes using the “max” function instead. Since you’re just adding the final colors, that’s a bit of an issue as it’s not clear how to actually blend the two colors. In your image both seem to use just white in which case it’s kinda trivial. Though it’s not that clear when you have two or more colors.

Maybe calculating the blend between all shapes first based on their relative alpha values and finally scaling the final color by the desired target alpha (the max alpha values from all shapes) may work.

So if you have 3 shapes, one red, one blue and one green and they overlap with those alpha values for each shape:

red 0.7
blue 0.2
green 0.5

You may want to blend them based on their relative alpha values by scaling by the total. So the total is 0.7 + 0.2 + 0.5 == 1.4, so the blending would happen at

red 0.5  ( == 0.7/1.4)
blue 0.14286 (  == 0.2/1.4)
green 0.35714 (  == 0.5/1.4)

This gives you a total weighted sum of 1. This would be the full color of the point. Now we choose the actual alpha for the point by picking the max alpha value of all shapes

max (0.7, 0.2, 0.5) == 0.7

So the final color would be something like (0.5, 0.35714, 0.14286, 0.7)

Though there are countless of different blend modes how to combine several colors into one and it’s up to you how to actually mix them. Though since you want to preserve the shapes, the important thing is picking the max alpha value as the final alpha for the result. Letting it saturate to 1 would give you what you have at the moment. You get those typical metaballs blending areas as they also just add up the actual iso value of each ball. So when two getting close their sum would add up to a value larger than the threshold and in your case, a value larger than 1 which means solid / full color.

@Bunny83 can I get your opinion:

I did what you suggested but it doesn’t seem to work. I’m drawing two circles, same color with smoothstep. I add up the aplhas and divide each by that number. I then choose the largest, but I loose transparency and the colors seem to invert


            fixed4 frag(v2f i) : SV_Target
                //Draw 0 circle using smoothstep
                float2 offset0 = float2(i.position.x, i.position.y - (_Inner / 2 + _Outer / 2));
                float alpha0 = 1 - smoothstep(_Outer / 2 - _Inner / 2, (_Outer / 2 - _Inner / 2) + _Soften, length(offset0));
                float4 color0 = float4(1, 1, 1, 1) * alpha0;

                //Draw 1 circle using smoothstep
                float2 offset1 = float2(i.position.x + 0.03, i.position.y - 0.03 - (_Inner / 2 + _Outer / 2));
                float alpha1 = 1 - smoothstep(_Outer / 2 - _Inner / 2, (_Outer / 2 - _Inner / 2) + _Soften, length(offset1));
                float4 color1 = float4(1, 1, 1, 1) * alpha1;

                float compAlpha = color0.a + color1.a;
                color0.a /= compAlpha;
                color1.a /= compAlpha;

                return fixed4(color0.rgb + color1.rgb, max(color0.a, color1.a));


I loose transparency on the smoothstep, but have it on the intersection