GUI

Soft Mask For UGUI

SoftMaskForUGUI

Soft masking for uGUI elements in Unity.

PRs Welcome

<< Description | WebGL Demo | Download | Usage | Development Note >>

What’s new? See changelog

Do you want to receive notifications for new releases? Watch this repo





Description

SoftMask is a smooth masking component for uGUI elements in Unity. By using SoftMask instead of default Mask, rounded edges of UI elements can be expressed beautifully.

Features

  • SoftMask is compatible with Mask.
  • You can adjust the visible part.
  • Text, Image, RawImage can be used as a masking.
  • Support multiple-sprites and SpriteAtlas.
  • Support up to 4 nested soft masks.
  • Support scroll view.
  • Support inversed soft mask.
  • Support overlay, camera space and world space.
  • Raycast is filtered only for the visible part.
  • Contain soft maskable UI shader.
  • Support soft masks in your custom shaders by adding just 3 lines. For details, please see Development Note.
  • Adjust soft mask buffer size to improve performance.
  • Convert existing Mask to SoftMask from context menu.

Future plans

  • Render the soft mask buffer only when needed to improve performance.
  • Add a SoftMaskable component to the child UI elements of SoftMask From the inspector.
  • Preview soft mask buffer in inspector.
  • Support TextMeshPro.
  • Component icon.

Known issues

  • SceneView does not display SoftMask properly. It is displayed like Mask. (By design)

Components

Component Description Screenshot
SoftMask Use instead of Mask for smooth masking.

Show Mask Graphic: Show the graphic that is associated with the Mask render area.
Desampling Rate: The desampling rate for soft mask buffer. The larger the value, the better the performance but the lower the quality.
Softness: The value used by the soft mask to select the area of influence defined over the soft mask’s graphic.
Ignore Parent: Should the soft mask ignore parent soft masks?
SoftMaskable Add this component to Graphic under SoftMask for smooth masking.

Inverse: The graphic will be visible only in areas where no mask is present.





Demo

WebGL Demo





Usage

  1. Download *.unitypackage from Releases.
  2. Import the package into your Unity project. Select Import Package > Custom Package from the Assets menu.
  3. Add SoftMask component instead of Mask component. Or, convert existing Mask component to SoftMask component from the context menu.
  4. Add SoftMaskable components to the child UI elements of SoftMask component.
  5. Adjust softness of SoftMask.
  6. Enjoy!
Requirement
  • Unity 2017+ (including Unity 2018.x)
  • No other SDK are required





Development Note

Support soft masks in your custom shaders

You can support soft masks in your custom shaders, by adding just 3 lines!

  1. Add #pragma and #include. SOFTMASK_EDITOR is a keyword for editor, not included in the build.
#include "Assets/Coffee/UIExtensions/SoftMaskForUGUI/SoftMask.cginc"
#pragma shader_feature __ SOFTMASK_EDITOR
  1. Apply a soft mask in the fragment shader. IN.vertex is clip position.
color.a *= SoftMask(IN.vertex);

For details, please see UI-Default-SoftMask.shader.



Why is not it displayed properly in SceneView?

SoftMask calculates the final alpha value based on the value of each channel of the soft mask buffer. The soft mask buffer is a buffer generated based on GameView’s screen space.

Since SceneView has a view matrix and a projection matrix independent of GameView, the SceneView’s camera can not refer to the soft mask buffer properly.

Therefore, in GameView, it is displayed properly. but in ScreenView, it is displayed like default Mask.



Tips: Convert component from context menu

Converting components from the context menu is very convenient. You can convert multiple components at the same time, without having to remove the source components.

If the destination component has the same properties as the source component, the value is set automatically.

In addition, if the destination component is compatible with the source component, it will not lose its reference. For example, if public Mask mask; refers to a Mask, converting it to SoftMask in this way does not lose references.

This way consists of two generic methods.

/// <summary>
/// Verify whether it can be converted to the specified component.
/// </summary>
protected static bool CanConvertTo<T>(Object context)
	where T : MonoBehaviour
{
	return context && context.GetType() != typeof(T);
}

/// <summary>
/// Convert to the specified component.
/// </summary>
protected static void ConvertTo<T>(Object context) where T : MonoBehaviour
{
	var target = context as MonoBehaviour;
	var so = new SerializedObject(target);
	so.Update();

	bool oldEnable = target.enabled;
	target.enabled = false;

	// Find MonoScript of the specified component.
	foreach (var script in Resources.FindObjectsOfTypeAll<MonoScript>())
	{
		if (script.GetClass() != typeof(T))
			continue;

		// Set 'm_Script' to convert.
		so.FindProperty("m_Script").objectReferenceValue = script;
		so.ApplyModifiedProperties();
		break;
	}

	(so.targetObject as MonoBehaviour).enabled = oldEnable;
}

In SoftMask, they are implemented as follows.

  • Mask and SoftMask can be converted to each other.
  • If conversion is not possible, gray out the context menu.
[MenuItem("CONTEXT/Mask/Convert To SoftMask", true)]
static bool _ConvertToSoftMask(MenuCommand command)
{
	return CanConvertTo<SoftMask>(command.context);
}
[MenuItem("CONTEXT/Mask/Convert To SoftMask", false)]
static void ConvertToSoftMask(MenuCommand command)
{
	ConvertTo<SoftMask>(command.context);
}
[MenuItem("CONTEXT/Mask/Convert To Mask", true)]
static bool _ConvertToMask(MenuCommand command)
{
	return CanConvertTo<Mask>(command.context);
}
[MenuItem("CONTEXT/Mask/Convert To Mask", false)]
static void ConvertToMask(MenuCommand command)
{
	ConvertTo<Mask>(command.context);
}

For details, please see SoftMaskEditor.cs.



Tips: Shader code for editor only

Do you know how to implement shader code “for editor only”? SoftMask uses SOFTMASK_EDITOR keyword in shader code to determine whether it is running in the editor.

#pragma shader_feature __ SOFTMASK_EDITOR

SOFTMASK_EDITOR keyword is set from the editor script, but it is not set after it is built. Also, this shader variant will be excluded from build.

#if UNITY_EDITOR
material = new Material(shader);
material.hideFlags = HideFlags.HideAndDontSave;
material.EnableKeyword("SOFTMASK_EDITOR");
#endif



Tips: Shader code for SceneView only

Do you know how to implement shader code “for SceneView only”? SoftMask understands that the current rendering is for SceneView, when SceneView’s view projection matrix and UNITY_MATRIX_VP match.

fixed isSceneView = 1 - any(UNITY_MATRIX_VP - _SceneViewVP);

Actually, because of the movement operation in SceneView, use “approximate” instead of “match”.

float4x4 _SceneViewVP;

fixed Approximate(float4x4 a, float4x4 b)
{
	float4x4 d = abs(a - b);
	return step(
		max(d._m00,max(d._m01,max(d._m02,max(d._m03,
		max(d._m10,max(d._m11,max(d._m12,max(d._m13,
		max(d._m20,max(d._m21,max(d._m22,max(d._m23,
		max(d._m30,max(d._m31,max(d._m32,d._m33))))))))))))))),
		0.01);
}

fixed isSceneView = Approximate(UNITY_MATRIX_VP, _SceneViewVP);

_SceneViewVP is set every frame from the editor script.

#if UNITY_EDITOR
UnityEditor.EditorApplication.update += ()
{
    Camera cam = UnityEditor.SceneView.lastActiveSceneView.camera;
    Matrix4x4 vp = cam.projectionMatrix * cam.worldToCameraMatrix;
    material.SetMatrix("_SceneViewVP", vp);
};
#endif





License

  • MIT
  • © UTJ/UCL

Author

mob-sakai

See Also