我正在尝试在XAML中创建一个角度渐变(类似于Photoshop所做的工作)。我找到了这篇文章:https://stackoverflow.com/a/21096028/2555957确实有效,但仅支持两个渐变挡块,我需要11个。

我已经看过了在后端进行计算的着色器,但是我不知道如何添加对更多渐变停止的支持。即使将着色器也硬编码为也可以使用11种颜色,它也将起作用。

最佳答案

这是一个类似于this one的着色器,它支持20个光阑并且在边缘没有锯齿:



设定:


将着色器代码放入shader.hlsl中,并使用fxc.exe对其进行编译。我运行的命令是“ fxc / T ps_3_0 / Fo shader.ps shader.hlsl”。
将输出文件shader.ps添加到项目的根目录,并将其构建类型更改为resource。
将效果代码和xaml添加到您的项目中。


Xaml:

<Canvas Height="405"
        Margin="50">
    <Ellipse Width="400"
             Height="400"
             Fill="Transparent"
             Stroke="Red"
             StrokeThickness="15">
        <Ellipse.Effect>
            <local:AngularGradientEffect>
                <local:AngularGradientEffect.GradientStops>
                    <GradientStop Offset="0.0"
                                      Color="#41b1e1"></GradientStop>
                    <GradientStop Offset=".1"
                                      Color="#3e3e3e"></GradientStop>
                    <GradientStop Offset=".2"
                                      Color="#41b1e1"></GradientStop>
                    <GradientStop Offset=".3"
                                      Color="#3e3e3e"></GradientStop>
                    <GradientStop Offset=".4"
                                      Color="#41b1e1"></GradientStop>
                    <GradientStop Offset=".5"
                                      Color="#3e3e3e"></GradientStop>
                    <GradientStop Offset=".6"
                                      Color="#41b1e1"></GradientStop>
                    <GradientStop Offset=".7"
                                      Color="#3e3e3e"></GradientStop>
                    <GradientStop Offset=".8"
                                      Color="#41b1e1"></GradientStop>
                    <GradientStop Offset=".9"
                                      Color="#3e3e3e"></GradientStop>
                    <GradientStop Offset="1"
                                      Color="#41b1e1"></GradientStop>
                </local:AngularGradientEffect.GradientStops>
            </local:AngularGradientEffect>
        </Ellipse.Effect>
    </Ellipse>
</Canvas>


我重用了LinearGradientBrush的GradientStopCollection / GradientStop来保持一致性。

AngularGradientEffect.cs:

public class AngularGradientEffect : ShaderEffect
{
    const int STOP_COUNT = 20;
    const int STOP_ANGLE_OFFSET = 10;
    const int STOP_COLOR_OFFSET = 50;

    public static readonly DependencyProperty InputProperty = ShaderEffect.RegisterPixelShaderSamplerProperty(
        "Input",
        typeof(AngularGradientEffect),
        0);

    public static readonly DependencyProperty CenterPointProperty = DependencyProperty.Register(
        "CenterPoint",
        typeof(Point),
        typeof(AngularGradientEffect),
        new UIPropertyMetadata(new Point(0.5D, 0.5D), PixelShaderConstantCallback(0)));

    public static readonly DependencyProperty GradientStopsProperty =
        DependencyProperty.Register("GradientStops",
        typeof(GradientStopCollection),
        typeof(AngularGradientEffect),
        new PropertyMetadata(new GradientStopCollection()));

    static readonly DependencyProperty[] StopAngles = new DependencyProperty[STOP_COUNT];
    static readonly DependencyProperty[] StopColors = new DependencyProperty[STOP_COUNT];

    static AngularGradientEffect()
    {
        for (int i = 0; i < STOP_COUNT; i++)
        {
            StopAngles[i] = DependencyProperty.Register("GradientStopAngle" + i, typeof(float), typeof(AngularGradientEffect), new UIPropertyMetadata(-1.0f, PixelShaderConstantCallback(STOP_ANGLE_OFFSET + i)));
            StopColors[i] = DependencyProperty.Register("GradientStopColor" + i, typeof(Color), typeof(AngularGradientEffect), new UIPropertyMetadata(Colors.RosyBrown, PixelShaderConstantCallback(STOP_COLOR_OFFSET + i)));
        }
    }

    public AngularGradientEffect()
    {
        PixelShader = new PixelShader()
        {
            UriSource = new Uri("/ShaderTest;component/shader.ps", UriKind.Relative)
        };

        UpdateShaderValue(InputProperty);
        UpdateShaderValue(CenterPointProperty);
        GradientStops = new GradientStopCollection();
        GradientStops.Changed += GradientStops_Changed;
    }

    void GradientStops_Changed(object sender, EventArgs e)
    {
        SetGradientStopDependencyProperties(sender as GradientStopCollection);
    }

    public void SetGradientStopDependencyProperties(IEnumerable<GradientStop> stops)
    {
        var orderedStops = stops.OrderBy(s => s.Offset).ToArray();

        for (int i = 0; i < STOP_COUNT; i++)
        {
            var current = orderedStops.ElementAtOrDefault(i);

            DependencyProperty angleProperty = StopAngles[i];
            SetValue(angleProperty, current == null ? -1.0f : (float)current.Offset * 2 * 3.141596f);

            DependencyProperty colorProperty = StopColors[i];
            SetValue(colorProperty, current == null ? Color.FromArgb(0, 0, 0, 0) : current.Color);
        }
    }

    public Brush Input
    {
        get
        {
            return ((Brush)(this.GetValue(InputProperty)));
        }
        set
        {
            this.SetValue(InputProperty, value);
        }
    }

    public Point CenterPoint
    {
        get
        {
            return ((Point)(this.GetValue(CenterPointProperty)));
        }
        set
        {
            this.SetValue(CenterPointProperty, value);
        }
    }

    public GradientStopCollection GradientStops
    {
        get { return (GradientStopCollection)GetValue(GradientStopsProperty); }
        set
        {
            SetValue(GradientStopsProperty, value);
        }
    }
}


着色器:

sampler2D  inputSampler : register(S0);

float2 centerPoint : register(C0);

float angle1 : register(C10);
float angle2 : register(C11);
float angle3 : register(C12);
float angle4 : register(C13);
float angle5 : register(C14);
float angle6 : register(C15);
float angle7 : register(C16);
float angle8 : register(C17);
float angle9 : register(C18);
float angle10 : register(C19);
float angle11 : register(C20);
float angle12 : register(C21);
float angle13 : register(C22);
float angle14 : register(C23);
float angle15 : register(C24);
float angle16 : register(C25);
float angle17 : register(C26);
float angle18 : register(C27);
float angle19 : register(C28);
float angle20 : register(C29);

float4 color1 : register(C50);
float4 color2 : register(C51);
float4 color3 : register(C52);
float4 color4 : register(C53);
float4 color5 : register(C54);
float4 color6 : register(C55);
float4 color7 : register(C56);
float4 color8 : register(C57);
float4 color9 : register(C58);
float4 color10 : register(C59);
float4 color11 : register(C60);
float4 color12 : register(C61);
float4 color13 : register(C62);
float4 color14 : register(C63);
float4 color15 : register(C64);
float4 color16 : register(C65);
float4 color17 : register(C66);
float4 color18 : register(C67);
float4 color19 : register(C68);
float4 color20 : register(C69);

float4 main(float2 uv : TEXCOORD) : COLOR
{
    float4 src = tex2D(inputSampler, uv);
    float2 p = float2(centerPoint)-uv;
    float angle = atan2(p.x, p.y) + 3.141596;

    float startAngle;
    float endAngle;
    float4 startColor;
    float4 endColor;

    if (angle >= angle1 && angle < angle2)
    {
        startAngle = angle1;
        startColor = color1;

        endAngle = angle2;
        endColor = color2;
    }
    else if (angle >= angle2 && angle < angle3)
    {
        startAngle = angle2;
        startColor = color2;

        endAngle = angle3;
        endColor = color3;
    }
    else if (angle >= angle3 && angle < angle4)
    {
        startAngle = angle3;
        startColor = color3;

        endAngle = angle4;
        endColor = color4;
    }
    else if (angle >= angle4 && angle < angle5)
    {
        startAngle = angle4;
        startColor = color4;

        endAngle = angle5;
        endColor = color5;
    }
    else if (angle >= angle5 && angle <= angle6)
    {
        startAngle = angle5;
        startColor = color5;

        endAngle = angle6;
        endColor = color6;
    }
    else if (angle >= angle6 && angle <= angle7)
    {
        startAngle = angle6;
        startColor = color6;

        endAngle = angle7;
        endColor = color7;
    }
    else if (angle >= angle7 && angle <= angle8)
    {
        startAngle = angle7;
        startColor = color7;

        endAngle = angle8;
        endColor = color8;
    }
    else if (angle >= angle8 && angle <= angle9)
    {
        startAngle = angle8;
        startColor = color8;

        endAngle = angle9;
        endColor = color9;
    }
    else if (angle >= angle9 && angle <= angle10)
    {
        startAngle = angle9;
        startColor = color9;

        endAngle = angle10;
        endColor = color10;
    }
    else if (angle >= angle10 && angle <= angle11)
    {
        startAngle = angle10;
        startColor = color10;

        endAngle = angle11;
        endColor = color11;
    }
    else if (angle >= angle11 && angle <= angle12)
    {
        startAngle = angle11;
        startColor = color11;

        endAngle = angle12;
        endColor = color12;
    }
    else if (angle >= angle12 && angle <= angle13)
    {
        startAngle = angle12;
        startColor = color12;

        endAngle = angle13;
        endColor = color13;
    }
    else if (angle >= angle13 && angle <= angle14)
    {
        startAngle = angle13;
        startColor = color13;

        endAngle = angle14;
        endColor = color14;
    }
    else if (angle >= angle14 && angle <= angle15)
    {
        startAngle = angle14;
        startColor = color14;

        endAngle = angle15;
        endColor = color15;
    }
    else if (angle >= angle15 && angle <= angle16)
    {
        startAngle = angle15;
        startColor = color15;

        endAngle = angle16;
        endColor = color16;
    }
    else if (angle >= angle16 && angle <= angle17)
    {
        startAngle = angle16;
        startColor = color16;

        endAngle = angle17;
        endColor = color17;
    }
    else if (angle >= angle17 && angle <= angle18)
    {
        startAngle = angle17;
        startColor = color17;

        endAngle = angle18;
        endColor = color18;
    }
    else if (angle >= angle18 && angle <= angle19)
    {
        startAngle = angle18;
        startColor = color18;

        endAngle = angle19;
        endColor = color19;
    }
    else if (angle >= angle19 && angle <= angle20)
    {
        startAngle = angle19;
        startColor = color19;

        endAngle = angle20;
        endColor = color20;
    }

    float offset = (angle - startAngle) / (endAngle - startAngle);
    float4 color = lerp(startColor, endColor, offset);

    // Multiply by the transparency of the source pixel
    float3 output = color.rgb * src.a;

    return float4(output, src.a);
}


HLSL似乎支持浮点常量缓冲区中的数组,但WPF doesn't。如果是这样,可以重写着色器以轻松支持任意数量的光圈,并且会更加美观。

资源:


http://www.codeproject.com/Articles/71617/Getting-Started-with-Shader-Effects-in-WPF
AngleGradient in WPF
http://msdn.microsoft.com/en-us/library/windows/desktop/bb509709(v=vs.85).aspx

10-06 14:46