2017-01-23 1 views
0

Я пытаюсь создать простой трехцветный градиент угла в WPF. В лучшем случае вы получили ответ XAML-ONLY, но я уверен, что это невозможно.3 Цвета AngleGradient в WPF

Этот вопрос & Ответ (AngleGradient in WPF) - это половина того, чего я хочу. Я попытался сыграть с кодом, но я, очевидно, не получил правильную математику.

enter image description here

Мой вопрос: как я могу сделать то, что спрашивается в поставленный выше вопрос, но с 3-х цветов и градиента между цветом 3 и цвет 1 (В вопросе выше она идет с градиентом от синего до белого, но сразу после синего цвета, я бы хотел, чтобы был обратный градиент от белого до синего).

Подумайте об этом как о равностороннем треугольнике с идеальной горизонтальной базой. Я хочу, например, красный в верхнем углу, зеленый в левом нижнем углу и синий в нижнем правом углу, а также идеальный угловой радиант между ними.

enter image description here

Я не против того, чтобы собрать в .ps, как в ответ предложил в другой теме :)

Спасибо большое!

ответ

0

Я думаю, что другая нить хорошо объясняет, что вам нужно делать. Вам нужно будет передать все три цвета в шейдер, а затем выполнить математический пиксель, чтобы получить нужный вам цвет.

Получите угол 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> 

enter image description here

Имейте в виду, что выполнение интерполяции между различными оттенками в пространстве RGB обеспечит некоторые уродливые результаты в некоторых случаях. Возможно, вы захотите просмотреть преобразование в HSV и интерполировать оттенок, если это то, что вы ожидаете делать.

+0

Я не знаю, как построить шейдер, и я явно не понимаю часть шейдера. Но это работает! :) Единственное, что мне не хватает, - это понять, как повернуть ваше решение (в шейдере), чтобы получить первый цветной центр. – ericmas001

+0

Это простое изменение. Просто инвертируйте y-компонент положения в шейдере при вычислении угла для вертикальной перемотки. 'float value = (atan2 (pos.x, -pos.y) + 3.141596)/(2.0 * 3.141596);' Вы также можете инвертировать x, если хотите перевернуть по горизонтали. – Xavier

+0

Я отредактировал шейдер в своем ответе, чтобы перевернуть ось Y, а также изменил обработку альфа, чтобы альфа-значения в источнике соблюдались, а также в переданных цветах. Я также добавил кучу комментариев кода, чтобы попытаться объяснить, что делает шейдер. – Xavier

Смежные вопросы