简体   繁体   English

WPF中的3种颜色AngleGradient

[英]3 Colors AngleGradient in WPF

I'm trying to achieve a simple 3 color Angle Gradient in WPF. 我正在尝试在WPF中实现简单的3色角度渐变。 Best case scenario you got an XAML-ONLY answer, but i'm pretty sure this is impossible. 最好的情况是您得到了仅XAML的答案,但是我很确定这是不可能的。

This Question & Answer ( AngleGradient in WPF ) is half of what i want. 这个问题和答案( 在WPF中AngleGradient )是我想要的一半。 I tried to play with the code but I clearly don't get the math right. 我尝试使用该代码,但显然数学没有正确。

在此处输入图片说明

My question is: how can I do exactly what is asked in the question above, but with 3 colors and a gradient between color 3 and color 1 (In the question above it goes with a gradient from blue to white, but straight back to blue after, i would like a reverse gradient from white to blue too). 我的问题是:我该如何正确执行上述问题,但要使用3种颜色以及3和1之间的渐变(在上面的问题中,渐变是从蓝色到白色,但又直接回到蓝色)之后,我也想要从白色到蓝色的反向渐变。

Think of it as a Equilateral Triangle with a perfect horizontal base. 将其视为具有完美水平底面的等边三角形。 I want for example Red at the top corner, Green at the left bottom corner and Blue at the bottom right corner, and a perfect angular radiant in between. 例如,我要在上角处使用红色,在左下角处使用绿色,在右下角处使用蓝色,并且在两者之间具有理想的角度辐射。

在此处输入图片说明

I don't mind having to compile into a .ps like in the answer suggested in the other thread :) 我不介意像其他线程建议的答案那样编译成.ps :)

Thanks a lot ! 非常感谢 !

I think that other thread does a good job of explaining what you need to do. 我认为其他线程可以很好地解释您需要做什么。 You will need to pass all three colors to the shader, and then do the math per-pixel to get the color you want. 您将需要将所有三种颜色传递给着色器,然后对每个像素进行数学运算以获得所需的颜色。

Get the 0 - 1 angle of the current pixel the same way as the other answer. 以与其他答案相同的方式获取当前像素的0-1角。 Then lerp between the appropriate colors at that angle. 然后以那个角度在适当的颜色之间徘徊。

Shader 着色器

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);
}

Effect 影响

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);
    }
}

Usage 用法

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

在此处输入图片说明

Keep in mind that doing an interpolation between different hues in RGB space will provide some ugly results in some cases. 请记住,在某些情况下,在RGB空间中的不同色调之间进行插值会提供一些难看的结果。 You might want to to look into converting to HSV and interpolating the hue if that is something you expect to be doing. 如果您希望这样做,则可能要考虑转换为HSV并插入色相。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM