Custom Effects

Hey,

I would like to create my own custom effects, but it’s all inside a black box. I cannot extend Effect or BasicEffect, which I was hoping to give me possibility to override the Render function. Any pointers how I could create my own effects?

Why can’t you extend Effect or BasicEffect? It’s intended for this to work, and I’ve seen this done out in the wild…

Because lack of skills.

Error E3004: Base class Fuse.Effects.Effect does not have a default constructor

You need to pass EffectType to the Effect-constructor.

BasicEffect is a bit easier to get started with (no wrangling with render-bounds etc), here’s a very simple example of how to extend it:

public sealed class MyEffect : BasicEffect
{
	public MyEffect() : base(EffectType.Composition)
	{
	}

	protected override void OnRender(DrawContext dc, Rect elementRect)
	{
		var original = Element.CaptureRegion(dc, elementRect, int2(0));
		if (original == null)
			return;

		// draw what you want within elementRect

		FramebufferPool.Release(original);
	}
}

Thanks a lot!

Could you give me an example how to draw the original?

using Uno;
using Uno.Graphics;
using Uno.UX;
using Fuse.Elements;

namespace Fuse.Effects
{
	/** Applies a gaussian blur to an @Element.
		@examples Docs/Blur/Examples.md
		@remarks Docs/Blur/Remarks.md
	*/
	public sealed class Blur : BasicEffect
	{
		public Blur() :
			base(EffectType.Composition)
		{
			Radius = 3;
		}

		float _radius;
		/** The radius/size of the blur */
		public float Radius
		{
			get { return _radius; }
			set
			{
				if (_radius != value)
				{
					_radius = value;

					OnRenderingChanged();
					OnRenderBoundsChanged();
				}
			}
		}
		
		public override bool Active { get { return Radius > 0; } }

		public override VisualBounds ModifyRenderBounds( VisualBounds inBounds )
		{
			return inBounds.InflateXY(Padding);
		}

		internal float Sigma { get { return Math.Max(Radius, 1e-5f); } }
		internal float Padding { get { return Math.Ceil(Sigma * 3 * Element.AbsoluteZoom) / Element.AbsoluteZoom; } }

		protected override void OnRender(DrawContext dc, Rect elementRect)
		{
			Rect paddedRect = Rect.Inflate(elementRect, Padding);

			// capture stuff
			var original = Element.CaptureRegion(dc, paddedRect, int2(0));
			if (original == null)
				return;

			var blur = EffectHelpers.Instance.Blur(original.ColorBuffer, dc, Sigma * Element.AbsoluteZoom);
			FramebufferPool.Release(original);

			draw Fuse.Drawing.Planar.Image
			{
				DrawContext: dc;
				Visual: Element;
				Position: elementRect.Minimum - Padding;
				Invert: true;
				Size: paddedRect.Size;
				Texture: blur.ColorBuffer;

				apply Fuse.Drawing.PreMultipliedAlphaCompositing;
				DepthTestEnabled: false;
			};

			FramebufferPool.Release(blur);
		}
	}
}

Here’s the source code of our Desaturate-effect, which does just that:

using Uno;
using Uno.Graphics;
using Uno.UX;

namespace Fuse.Effects
{
	/** Desaturates an @Element.
		@examples Docs/Desaturate/Examples.md
	*/
	public sealed class Desaturate : BasicEffect
	{
		public Desaturate() : base(EffectType.Composition)
		{
		}

		float _amount = 1;
		/**
			The amount of desaturation to apply.
			When `1`, the @Element will be completely grayscale.
		*/
		public float Amount
		{
			get { return _amount; }
			set
			{
				if (_amount != value)
				{
					_amount = value;
					OnRenderingChanged();
				}
			}
		}

		protected override void OnRender(DrawContext dc, Rect elementRect)
		{
			var original = Element.CaptureRegion(dc, elementRect, int2(0));
			if (original == null)
				return;

			draw Fuse.Drawing.Planar.Image
			{
				DrawContext: dc;
				Visual: Element;
				Position: elementRect.Minimum;
				Invert: true;
				Size: elementRect.Size;
				Texture: original.ColorBuffer;
				TextureColor: float4(prev TextureColor.XYZ / Math.Max(prev TextureColor.W, 1e-5f), prev TextureColor.W);
				float3 Primaries: float3(0.299f, 0.587f, 0.114f); // CCIR 601
				float Luminance: Math.Sqrt(Vector.Dot(TextureColor.XYZ * TextureColor.XYZ, Primaries)); // HSP Color Model
				PixelColor: float4(Math.Lerp(TextureColor.XYZ, float3(Luminance), Amount), TextureColor.W);
			};

			FramebufferPool.Release(original);
		}
	}
}