WPF 中的 3 种颜色 AngleGradient

3 Colors AngleGradient in WPF

我正在尝试在 WPF 中实现一个简单的 3 色角度渐变。最好的情况是你得到了 XAML-ONLY 答案,但我很确定这是不可能的。

这个问答(AngleGradient in WPF)是我想要的一半。我试着玩弄代码,但我显然没有正确理解数学。

我的问题是:我怎么能完全按照上面问题中的要求做,但是有 3 种颜色和颜色 3 和颜色 1 之间的渐变(在上面的问题中,它是从蓝色到白色的渐变,但之后直接变回蓝色,我也想要从白色到蓝色的反向渐变。

把它想象成一个有完美水平底边的等边三角形。例如,我想要红色在上角,绿色在左下角,蓝色在右下角,中间是完美的 angular 辐射点。

我不介意编译成 .ps 就像在另一个线程中建议的答案中那样:)

非常感谢!

我认为其他帖子很好地解释了您需要做什么。您需要将所有三种颜色传递给着色器,然后进行数学计算 per-pixel 以获得您想要的颜色。

获取当前像素点的0-1角度,方法与其他答案相同。然后以该角度在适当的颜色之间插入。

着色器

sampler2D inputSampler : register(S0);
float2 center : register(C0);
float4 firstColor : register(C1);
float4 secondColor : register(C2);
float4 thirdColor : register(C3);

float4 main(float2 uv : TEXCOORD) : COLOR
{
    // Put the three colors into an array.
    float4 colors[3] = { firstColor, secondColor, thirdColor };

    // Figure out where this pixel is in relation to the center point
    float2 pos = center - uv;

    // Compute the angle of this pixel relative to the center (in radians),
    // then divide by 2 pi to normalize the angle into a 0 to 1 range.
    // We are flipping the Y here so that 0 is at the top (instead of the bottom) and we
    // rotate clockwise. Could also flip X if we want to rotate counter-clockwise.
    float value = (atan2(pos.x, -pos.y) + 3.141596) / (2.0 * 3.141596);

    // Scale the angle based on the size of our array and determine which indices
    // we are currently between, wrapping around to 0 at the end.
    float scaledValue = value * 3;
    float4 prevColor = colors[(int)scaledValue];
    float4 nextColor = colors[((int)scaledValue + 1) % 3];

    // Figure out how far between the two colors we are
    float lerpValue = scaledValue - (float)((int)scaledValue);

    // Get the alpha of the incoming pixel from the sampler.
    float alpha = tex2D(inputSampler, uv).a;

    // Lerp between the colors. Multiply each color by its own alpha and the result by the
    // incoming alpha becuse WPF expects shaders to return premultiplied alpha pixel values.
    return float4(
        lerp(prevColor.rgb * prevColor.a, nextColor.rgb * nextColor.a, lerpValue) * alpha,
        lerp(prevColor.a, nextColor.a, lerpValue) * alpha);
}

效果

class AngleGradientEffect : ShaderEffect
{
    public Brush Input
    {
        get { return (Brush)GetValue(InputProperty); }
        set { SetValue(InputProperty, value); }
    }
    public static readonly DependencyProperty InputProperty = RegisterPixelShaderSamplerProperty("Input", typeof(AngleGradientEffect), 0);

    public Point Center
    {
        get { return (Point)GetValue(CenterProperty); }
        set { SetValue(CenterProperty, value); }
    }
    public static readonly DependencyProperty CenterProperty = DependencyProperty.Register("Center", typeof(Point), typeof(AngleGradientEffect),
        new PropertyMetadata(new Point(0.5, 0.5), PixelShaderConstantCallback(0)));

    public Color FirstColor
    {
        get { return (Color)GetValue(FirstColorProperty); }
        set { SetValue(FirstColorProperty, value); }
    }
    public static readonly DependencyProperty FirstColorProperty = DependencyProperty.Register("FirstColor", typeof(Color), typeof(AngleGradientEffect),
        new PropertyMetadata(Color.FromRgb(255, 0, 0), PixelShaderConstantCallback(1)));

    public Color SecondColor
    {
        get { return (Color)GetValue(SecondColorProperty); }
        set { SetValue(SecondColorProperty, value); }
    }
    public static readonly DependencyProperty SecondColorProperty = DependencyProperty.Register("SecondColor", typeof(Color), typeof(AngleGradientEffect),
        new PropertyMetadata(Color.FromRgb(0, 255, 0), PixelShaderConstantCallback(2)));

    public Color ThirdColor
    {
        get { return (Color)GetValue(ThirdColorProperty); }
        set { SetValue(ThirdColorProperty, value); }
    }
    public static readonly DependencyProperty ThirdColorProperty = DependencyProperty.Register("ThirdColor", typeof(Color), typeof(AngleGradientEffect),
        new PropertyMetadata(Color.FromRgb(0, 0, 255), PixelShaderConstantCallback(3)));

    public AngleGradientEffect()
    {
        // ResourceHelper is my own utility that formats URIs for me. The returned URI
        // string will be something like /AssemblyName;component/Effects/AngleGradient.ps
        PixelShader = new PixelShader() { UriSource = ResourceHelper.GetResourceUri("Effects/AngleGradient.ps", relative: true)};

        UpdateShaderValue(InputProperty);
        UpdateShaderValue(CenterProperty);
        UpdateShaderValue(FirstColorProperty);
        UpdateShaderValue(SecondColorProperty);
        UpdateShaderValue(ThirdColorProperty);
    }
}

用法

<Ellipse
    Width="200"
    Height="200"
    Fill="White">
    <Ellipse.Effect>
        <effects:AngleGradientEffect
            FirstColor="Red"
            SecondColor="Lime"
            ThirdColor="Blue" />
    </Ellipse.Effect>
</Ellipse>

请记住,在 RGB space 中的不同色调之间进行插值在某些情况下会产生一些难看的结果。如果您希望这样做,您可能想考虑转换为 HSV 并插入色调。