2014-10-02 64 views
2

我试图在XAML中创建角度渐变(类似于Photoshop中的一个)。我发现这篇文章:https://stackoverflow.com/a/21096028/2555957哪些工作,但只支持两个渐变停止,我需要11.带有多个GradientStops的XAML角度渐变

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

+0

使它在Illustrator和[导出到XAML(http://www.mikeswanson.com/xamlexport/ ) – 2014-10-02 16:52:20

+0

谢谢,我会试试 – Paul 2014-10-03 07:52:26

+0

如果你得到fxc的错误看看 https://stackoverflow.com/questions/44067 027/error-x3000-illegal-character-in-shader-file – 2017-12-26 14:03:53

回答

2

这里有一个着色器类似于this one支持20个停靠,周围的边缘无走样:

enter image description here

设置:

  • 将着色器代码shader.hlsl和使用fxc.exe进行编译。我运行的命令是“fxc/T ps_3_0/Fo shader.ps shader.hlsl”。
  • 将输出文件shader.ps添加到项目的根目录并将其构建类型更改为资源。
  • 将效果码和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 /渐变停止的一致性。

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。如果是这样,着色器可以重写,轻松支持任意数量的停止,并且会更加优雅。

资源:

+0

谢谢,这正是我所需要的。 – Paul 2014-10-04 22:21:36

+0

使用'float angle [20]:register(C10); // C10 - C29'和'float4 color [20]:寄存器(C50); // C50 - C69'在着色器HLSL中,代码可能会简化。 – xmedeko 2017-10-26 07:39:19

+0

使用'float angle = atan2(p.y,p.x)+ 3.141596;'对于WPF坐标更自然 - 角度0位于中心的右侧。 – xmedeko 2017-10-26 07:40:29