Forums

Trouble with a shader effect
Last Post 26 May 2017 12:32 AM by A T. 15 Replies.
Printer Friendly
  •  
  •  
  •  
  •  
  •  
Sort:
PrevPrev NextNext
You are not authorized to post a reply.
Author Messages
A TUser is Offline
New Member
New Member
Posts:34


--
17 Sep 2016 12:47 AM
    I made a pixel shader:
    <Image Source="{Binding Image}" Width="{Binding Width}" Height="{Binding Height}">
        <Image.Effect>
            <effect:ChangeColor ColorInput="{Binding Color}" />
        </Image.Effect>
    </Image>
    

    Project builds successfully, but UI generator generates an error "#error Type ChangeColor not supported".

    Interestingly, I do not get this problem if shader doesn't have any properties.
    Filip DušekUser is Offline
    Advanced Member
    Advanced Member
    Posts:676


    --
    17 Sep 2016 10:40 AM
    You have 2 ways how to use custom shaders/effects.

    a) Use CustomEffect (namespace EmptyKeys.UserInterface.Designer.Effects) in XAML and set just EffectAsset, which is name of the asset of your shader file. Call EffectManager LoadEffects method after creating UIRoot instance. If you want to change any attribute (for example ColorInput in your case) of shader, you can get instance of the effect from EffectManager (method GetEffect and then GetNativeEffect and retype it to MonoGame effect). Think I will make example for it.

    b) Get source of UI Generator ( http://github.com/EmptyKeys/UI_Generator ) and implement designer class and GeneratorValue class for your effect. You can get inspired by DirectionalBlurEffect and DirectionalBlurEffectGeneratorValue implementation.

    I was planning to add more effects so if you want some effect or have some useful, let me know.
    A TUser is Offline
    New Member
    New Member
    Posts:34


    --
    17 Sep 2016 03:32 PM
    Thanks for the reply, maybe I'll try the first way.


    I was planning to add more effects so if you want some effect or have some useful, let me know.


    Currently I just want an effect that sets image color RGB (but not A) parameters. But I think it would be better to have a more generic effect with separate attributes for A, R, G and B (and, if some attributes aren't specified, then the respective parameter doesn't change).
    Filip DušekUser is Offline
    Advanced Member
    Advanced Member
    Posts:676


    --
    17 Sep 2016 04:47 PM
    I was thinking about effect like this - http://microsoft.github.io/Win2D/ht...Effect.htm

    That should do the trick tho
    A TUser is Offline
    New Member
    New Member
    Posts:34


    --
    18 Sep 2016 12:43 AM
    If there is a good chance that ColorMatrixEffect will be added in the near future, then I'll probably just wait.

    In case if I'll try to add a custom effect:


    Use CustomEffect (namespace EmptyKeys.UserInterface.Designer.Effects) in XAML and set just EffectAsset, which is name of the asset of your shader file.


    Did you mean .fx file?
    Filip DušekUser is Offline
    Advanced Member
    Advanced Member
    Posts:676


    --
    18 Sep 2016 04:30 PM
    Added effect to backlog - http://trello.com/b/vG4Xgcel/empty-keys-ui

    yes, .fx file
    A TUser is Offline
    New Member
    New Member
    Posts:34


    --
    05 May 2017 03:39 PM
    I was busy with other tasks and even suspended work on this entire project for some time, but now I'm returning to this.


    a) Use CustomEffect (namespace EmptyKeys.UserInterface.Designer.Effects) in XAML and set just EffectAsset, which is name of the asset of your shader file. Call EffectManager LoadEffects method after creating UIRoot instance. If you want to change any attribute (for example ColorInput in your case) of shader, you can get instance of the effect from EffectManager (method GetEffect and then GetNativeEffect and retype it to MonoGame effect). Think I will make example for it.


    EffectManager doesn't load my effect. The effect is compiled as .xnb file and successfully loaded by Content. But then, after I call EffectManager.Instance.LoadEffects(Content), the effect isn't loaded. EffectManager.Instance contains only default DirectionalBlurShader and calling EffectManager.Instance.GetEffect("ChangeColor") returns null.
    Filip DušekUser is Offline
    Advanced Member
    Advanced Member
    Posts:676


    --
    18 May 2017 07:24 PM
    You have to actually use it somewhere so for example

            <TextBlock Text="Custom Effect">
                <TextBlock.Effect>
                    <ef:CustomEffect EffectAsset="your_effect_name_here" />
                </TextBlock.Effect>
            </TextBlock>
    
            ef is xmlns:ef="clr-namespace:EmptyKeys.UserInterface.Designer.Effects;assembly=EmptyKeys.UserInterface.Designer"
    


    A TUser is Offline
    New Member
    New Member
    Posts:34


    --
    23 May 2017 12:07 AM
    I actually use it exactly as you've described:

    <Image Source="{Binding Image}" Width="{Binding Width}" Height="{Binding Height}">
        <Image.Effect>
            <ef:CustomEffect EffectAsset="ChangeColor" />
        </Image.Effect>
    </Image>
    


    And that's my C# code:

    Content.Load<Effect>("ChangeColor");
    EffectManager.Instance.LoadEffects(Content);
    byte r = (byte)new Random().Next(256);
    byte g = (byte)new Random().Next(256);
    byte b = (byte)new Random().Next(256);
    EffectManager.Instance.GetEffect("ChangeColor").UpdateEffectParameters(new object[1] { System.Windows.Media.Color.FromRgb(r, g, b) }); 
    


    But EffectManager.Instance.GetEffect("ChangeColor") returns null despite my effect is successfully loaded by Content (and I can see it in Content.LoadedAssets).
    Filip DušekUser is Offline
    Advanced Member
    Advanced Member
    Posts:676


    --
    23 May 2017 06:08 PM
    You don't have to Load effect manually, that's what LoadEffect method does. Don't forget to call LoadEffect method after creating UI instance.
    Filip DušekUser is Offline
    Advanced Member
    Advanced Member
    Posts:676


    --
    23 May 2017 06:12 PM
    BTW if you want to just change color of some image you can do this

    <Image ek:ImageBrush.ColorOverlay="{Binding Color}" />
    


    ek is xmlns:ek="clr-namespace:EmptyKeys.UserInterface.Designer;assembly=EmptyKeys.UserInterface.Designer"
    A TUser is Offline
    New Member
    New Member
    Posts:34


    --
    24 May 2017 02:05 AM
    Thanks for the suggestion, setting ColorOverlay will probably be enough for me now.

    But in case if I will need to use shaders in the future: what's wrong with the code above? How to set effect parameters? I called LoadEffects after creating UI instance. But when I tried to get effect in order to set it's parameters, I got null reference exception because GetEffect("ChangeColor") returned null.
    Filip DušekUser is Offline
    Advanced Member
    Advanced Member
    Posts:676


    --
    24 May 2017 05:12 PM
    No idea. Wrong base path of the effect maybe? I would have to see some not working example.

    One more thing. If your effect has parameters you have to implement an effect class, base class EmptyKeys.UserInterface.Media.Effects.Effect and override UpdateEffectParameters. Example:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace EmptyKeys.UserInterface.Media.Effects
    {
        /// <summary>
        /// Implements Directional Blur effect
        /// </summary>
        /// <seealso cref="EmptyKeys.UserInterface.Media.Effects.Effect" />
        public class DirectionalBlurEffect : Effect
        {
            private static Type typeOfThis = typeof(DirectionalBlurEffect);
    
            /// <summary>
            /// The angle property
            /// </summary>
            public static readonly DependencyProperty AngleProperty =
                DependencyProperty.Register("Angle", typeof(float), typeOfThis, 
                    new FrameworkPropertyMetadata(0f, FrameworkPropertyMetadataOptions.AffectsRender));
    
            /// <summary>
            /// Gets or sets the angle.
            /// </summary>
            /// <value>
            /// The angle.
            /// </value>
            public float Angle
            {
                get { return (float)GetValue(AngleProperty); }
                set { SetValue(AngleProperty, value); }
            }
    
            /// <summary>
            /// The blur amount property
            /// </summary>
            public static readonly DependencyProperty BlurAmountProperty =
                DependencyProperty.Register("BlurAmount", typeof(float), typeOfThis,
                    new FrameworkPropertyMetadata(0f, FrameworkPropertyMetadataOptions.AffectsRender));
    
            /// <summary>
            /// Gets or sets the blur amount.
            /// </summary>
            /// <value>
            /// The blur amount.
            /// </value>
            public float BlurAmount
            {
                get { return (float)GetValue(BlurAmountProperty); }
                set { SetValue(BlurAmountProperty, value); }
            }
    
            /// <summary>
            /// Initializes a new instance of the <see cref="DirectionalBlurEffect"/> class.
            /// </summary>
            public DirectionalBlurEffect() : base()
            {
                EffectAsset = "DirectionalBlurShader";
            }
    
            /// <summary>
            /// Updates the effect parameters.
            /// </summary>
            public override void UpdateEffectParameters()
            {
                base.UpdateEffectParameters();
    
                if (EffectInstance != null) // &amp;&amp; RenderDirty) this optimization would not work if this effect is used on two diff controls with diff values
                {
                    EffectInstance.UpdateEffectParameters(Angle, BlurAmount);
                    RenderDirty = false;
                }
            }
        }
    }
    
    
    A TUser is Offline
    New Member
    New Member
    Posts:34


    --
    25 May 2017 01:31 AM

    Wrong base path of the effect maybe?


    EffectManager loads effects from ContentManager, not directly from files, that's why an instance of ContentManager passed to LoadEffects (and my content manager loaded effect successfully, it is EffectManager that failed to load it), or I don't understand something?
    Filip DušekUser is Offline
    Advanced Member
    Advanced Member
    Posts:676


    --
    25 May 2017 08:47 PM
    EffectManager calls Load on ContentManager so it's actually loading files. LoadEffect has 2 arguments, one is directory in case effect is in subdirectory of Content dir.
    A TUser is Offline
    New Member
    New Member
    Posts:34


    --
    26 May 2017 12:32 AM
    My effect isn't in subdirectory. But at this point it's probably pointless to discuss it further since it's not even obvious I'll ever use shaders. By the way, it would be nice if an example of using custom shader was added to the examples .
    You are not authorized to post a reply.