UWP/WinUI3 Win2d PixelShaderEffect 实现ColorPlacementEffect (颜色替换) 滤镜。

   在上一篇:UWP/WinUI3 PixelShaderEffect 实现ThresholdEffect 滤镜。 - 吃饭/睡觉 - 博客园 (cnblogs.com) 已经价绍了如何编写hsls,编译,和使用 PixelShaderEffect 来实现自定义滤镜效果了,那么本编将介绍如何编写一个 “颜色替换滤镜”;

    效果图:

 

 

      一.颜色匹配理论

    1.根据指定的颜色和阈值匹配到 指定颜色范围的像素。

    2.利用hsv颜色模式 调整选中像素的 色调,饱和度,亮度;

//设置输入源数量
#define D2D_INPUT_COUNT 1
//将输入源0 设置为简单采样模式
#define D2D_INPUT0_SIMPLE
//引入hlsl帮助程序
#include "d2d1effecthelpers.hlsli"

//定义属性
//源颜色 float(0,0,0)~float(1,1,1)
float3 sourceColor;
//替换后的颜色,hsv颜色 float(0,0,0)~float(1,1,1)
float3 hsv;
//颜色容差 0~1
float threshold;

//HSV 到 RGB 转换
float3 hsv2rgb(float3 c)
{
    float3 rgb = clamp(abs(fmod(c.x * 6.0 + float3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);
    float3 f1 = float3(1,1,1);
    return c.z * lerp(f1, rgb, float3(c.y,c.y,c.y));
    //return f1;
}
//rgb到hsv 转换
float3 rgb2hsv(float3 c)
{
    float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
    float a = step(c.b, c.g);
    float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), float4(a,a,a,a));
    float b = step(p.x, c.r);
    float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), float4(b,b,b,b));
    float d = q.x - min(q.w, q.y);
    float e = 1.0e-10;
    return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}

//主程序
D2D_PS_ENTRY(main){
    //获取当前像素颜色
    float4 sColor = D2DGetInput(0);
    float3 color = sColor.rgb;
    //当前像素的每个分量减去输入颜色的对于分量的绝对值跟阈值做比较,查看是否符合条件
    if(abs(color.r - sourceColor.r) < threshold && abs(color.g - sourceColor.g) < threshold && abs(color.b - sourceColor.b) < threshold)
    {
        //将当前颜色从 rgb转到hsv
        float3 hsv = rgb2hsv(color);
        //声明 计算后的hsv 变量
        
        float3 thsv = float3(0,0,0);
        //虽然这里用的都是rgb属性,但是对应的是hsv
        //当前像素的hsv与输入的hsv相加
        thsv.r= fmod(hsv.r*360+hsv.r*360,360)/360.0;
        thsv.g= clamp(hsv.g+hsv.g,0,1);
        thsv.b= clamp(hsv.b+hsv.b,0,1);
        //将累计后的hsv 转换成rgb然后输出颜色
        float3 resultColor = hsv2rgb(thsv);  
        return float4(resultColor,sColor.a);
    }
    //不符合条件的像素,返回输入的颜色即可
    return sColor;
}
颜色替换hlsl

    1).在代码里定义了三个属性

      1.sourceColor : 源颜色,用于匹配输入的颜色像素;

      2.hsv : 输入的hsv 颜色,用于跟符合条件的像素进行累加计算。(在这里我将hsv的三个分量的范围都设置成了-1~1之间了,因为使用到的两个转换函数返回的值都是在0~1之间的。)

      3.threshold: 阈值范围;

    2).在代码中定义了两个 颜色模式转换的函数,因为这两个函数是我从别的地方复制过来的所以我也不是很懂。

      颜色转换引用:Unity Shader - HSV 和 RGB 的相互转换 - 知乎 (zhihu.com)

  二.选区效果

    从效果图中可以看到我在图像的右下角显示了一个显示当前选中像素的效果图,其实这个也是用hlsl 绘制出来了,在这里也将这个hlsl 贴出来,因为代码上已经写了注释了,在这里就不详细讲了;

 1 //声明只有一张纹理输入
 2 #define D2D_INPUT_COUNT 1
 3 //将第一张纹理设置为简单模式采样
 4 #define D2D_INPUT0_SIMPLE
 5 //引入hlsl 帮助程序库
 6 #include "d2d1effecthelpers.hlsli"
 7 
 8 //输入颜色,用户配备
 9 float3 color;
10 //阈值
11 float threshold;
12 
13 // HSV 到 RGB 转换
14 float3 hsv2rgb(float3 c)
15 {
16     float3 rgb = clamp(abs(fmod(c.x * 6.0 + float3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);
17     float3 f1 = float3(1, 1, 1);
18     return c.z * lerp(f1, rgb, float3(c.y, c.y, c.y));
19 }
20 
21 D2D_PS_ENTRY(main){
22     //获取当前像素颜色
23     float4 sColor = D2DGetInput(0);
24     //将当前像素的rgb每个分量减去输入颜色的每个分量
25     //用于跟阈值对比,查看当前像素是否符合条件
26     float r = abs(sColor.r - color.r);
27     float g = abs(sColor.g - color.g);
28     float b = abs(sColor.b - color.b);
29     if( r< threshold && g < threshold && b < threshold)
30     {
31         //将色相和饱和度都设置为0
32         //将rgb三个分量相加后除以阈值*3 计算出比率然后设置当前像素的亮度
33         float3 hsv = float3(0, 0, 0);
34         hsv.b= 1-(r+g+b)/(threshold*3);
35         //用hsv2rgb 将hsv 转换成rgb颜色
36         float3 tColor = hsv2rgb(hsv);
37         return float4(tColor,sColor.a);
38     }
39     //不符合条件的像素返回黑色
40     return float4(0,0,0,1);
41 }
选区hlsl

  三.绘制方法,传入参数设定

 1 canvas.Draw += (s, e) =>
 2             {
 3                 //绘制黑白网格
 4                 Win2dUlit.DrawGridGraphics(e.DrawingSession, 100);
 5                 //判断effect 和位图是否为空
 6                 if (effect == null || bitmap == null)
 7                     return;
 8                 if (color != null)
 9                 {
10                     //绘制替换颜色效果
11                     //颜色将0~255 转换成0~1 因为hlsl里面的颜色是0~1范围的并且是float类型,所以这里需要将每个颜色的分量除以255
12                     Vector3 hsv = targetHsv.HsvToVector3();
13                     Vector3 sourceColor = new Vector3(color.Value.R / 255f, color.Value.G / 255f, color.Value.B / 255f);
14                     //通过键值对的形式设置属性传递到着色器里面
15                     effect.Properties["threshold"] = (float)threshold.Value;
16                     effect.Properties["hsv"] = hsv;
17                     effect.Properties["sourceColor"] = sourceColor;
18                     effect.Source1 = bitmap;
19                     //绘制效果图
20                     var effectTran = Win2dUlit.CalcutateImageCenteredTransform(effectWidth, effectHeight, bitmap.Size.Width, bitmap.Size.Height);
21                     effectTran.Source = effect;
22                     e.DrawingSession.DrawImage(effectTran);
23                     //绘制选区图 右下角
24                     var constTran = Win2dUlit.CalcutateImageCenteredTransform(previewWidth, previewHeight, bitmap.Size.Width, bitmap.Size.Height);
25                     constEffect.Source1 = bitmap;
26                     constEffect.Properties["threshold"] = (float)threshold.Value;
27                     constEffect.Properties["color"] = sourceColor;
28                     using (var ds = constRender.CreateDrawingSession())
29                     {
30                         ds.Clear(Colors.Transparent);
31                         ds.DrawImage(constEffect, new Rect(new Point(), bitmap.Size), new Rect(new Point(), bitmap.Size));
32                     }
33                     constTran.Source = constRender;
34                     e.DrawingSession.DrawImage(constTran, previewWidth, effectHeight);
35                 }
36                 else
37                 {
38                     var effectTran = Win2dUlit.CalcutateImageCenteredTransform(effectWidth, effectHeight, bitmap.Size.Width, bitmap.Size.Height);
39                     effectTran.Source = bitmap;
40                     e.DrawingSession.DrawImage(effectTran);
41                 }
42                 //绘制原图 左下角
43                 var previewTran = Win2dUlit.CalcutateImageCenteredTransform(previewWidth, previewHeight, bitmap.Size.Width, bitmap.Size.Height);
44                 previewTran.Source = bitmap;
45                 e.DrawingSession.DrawImage(previewTran, 0, effectHeight);
46                 //绘制选择颜色缩略图
47                 if (isPressed && (pos.X >= offsetX && pos.X <= offsetX + width && pos.Y >= offsetY && pos.Y <= offsetY + height))
48                 {
49                     //画三角形
50                     CanvasPathBuilder builder = new CanvasPathBuilder(canvas);
51                     builder.BeginFigure(pos);
52                     builder.AddLine(pos - new Vector2(5, 5));
53                     builder.AddLine(pos - new Vector2(-5, 5));
54                     builder.EndFigure(CanvasFigureLoop.Closed);
55                     CanvasGeometry geometry = CanvasGeometry.CreatePath(builder);
56                     e.DrawingSession.FillGeometry(geometry, Colors.White);
57                     e.DrawingSession.FillRoundedRectangle(new Rect(pos.X - 50, pos.Y - 100 - 5, 100, 100), 10, 10, Colors.White);
58                     //采样位置
59                     var usePx = (pos.X - offsetX) / scale;
60                     var usePy = (pos.Y - offsetY) / scale;
61                     e.DrawingSession.DrawImage(bitmap, pos.X - 50, pos.Y - 100 - 5, new Rect(usePx - 50, usePy - 50, 100, 100));
62                     e.DrawingSession.DrawRectangle(new Rect(pos.X - 1, pos.Y - 56, 3, 3), Colors.Orange);
63                     e.DrawingSession.DrawRoundedRectangle(new Rect(pos.X - 50, pos.Y - 100 - 5, 100, 100), 10, 10, Colors.Black);
64                 }
65             };
Draw

  在绘制方法里面我们只需要关心如和传入参数和参数类型以及范围

  1.sourceColor: 通过键值对的形式设置效果的参数,但是在传入颜色之前需要 从rgb(0~255) 转换成 vector3(0~1);

  2.hsv :在传入前需要将hsv颜色 从 (360,100,100)转换成 (1,1,1) 

  3.threshold: 因为在hlsl 上定义的是float 类型,所以传入时 也需要将值转换成float类型才能传入;

  四.所有代码

    因为下面的很多代码都是关于一些输入交互的所以这里直接将代码贴出来,复制到已经引用win2d库的项目应该就能运行了。

 1 <Grid>
 2         <Grid.RowDefinitions>
 3             <RowDefinition></RowDefinition>
 4             <RowDefinition Height="auto"></RowDefinition>
 5         </Grid.RowDefinitions>
 6         <canvas:CanvasControl x:Name="canvas"></canvas:CanvasControl>
 7         <StackPanel Grid.Row="1">
 8             <Button Content="选择图片" x:Name="selectPicture"></Button>
 9             <Slider Header="阈值:" Value="0.2" Maximum="1" StepFrequency="0.01" x:Name="threshold"></Slider>
10             <StackPanel Orientation="Horizontal">
11                 <Border Margin="10 0 10 0" x:Name="selectColor" BorderBrush="Black" BorderThickness="2">
12                     <TextBlock Text="选择的颜色" HorizontalAlignment="Center" VerticalAlignment="Center" ></TextBlock>
13                 </Border>
14                 <StackPanel Width="300">
15                     <Rectangle Width="200" Fill="Gray" Height="30" x:Name="replaceColor"></Rectangle>
16                     <Slider Header="色相" x:Name="hue" StepFrequency="1" Minimum="-180" Maximum="180"></Slider>
17                     <Slider Header="饱和度" x:Name="saturation" Minimum="-100" Maximum="100"></Slider>
18                     <Slider Header="亮度" x:Name="brightness" Minimum="-100" Maximum="100"></Slider>
19                 </StackPanel>
20             </StackPanel>
21 
22         </StackPanel>
23     </Grid>
Xaml
  1 public sealed partial class ColorReplacementPage : Page
  2     {
  3         PixelShaderEffect effect;
  4         //选区效果
  5         PixelShaderEffect constEffect;
  6         CanvasRenderTarget constRender;
  7         CanvasBitmap bitmap;
  8         Color[] bitmapColors;
  9         public ColorReplacementPage()
 10         {
 11             this.InitializeComponent();
 12             Init();
 13             InitOperate();
 14         }
 15         Hsv targetHsv = new Hsv();
 16         Hsv sourceHsv = new Hsv();
 17         Color? color;
 18         float effectWidth;
 19         float effectHeight;
 20         float previewWidth;
 21         float previewHeight;
 22         //记录鼠标是否在画布上按下
 23         bool isPressed;
 24         //记录小图在画布上的大小和位置,和偏移
 25         float offsetX, offsetY, scale, width, height;
 26         Vector2 pos;
 27         void Init()
 28         {
 29             canvas.CreateResources += async (s, e) =>
 30             {
 31                 //获取着色器二进制文件
 32                 StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Shaders/ColorReplacementEffect.bin"));
 33                 IBuffer buffer = await FileIO.ReadBufferAsync(file);
 34                 //转换成字节数组
 35                 var bytes = buffer.ToArray();
 36                 //用 字节数组 初始化一个 PixelShaderEffect 对象;b v
 37                 effect = new PixelShaderEffect(bytes);
 38 
 39                 file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Shaders/ConstituencyEffect.bin"));
 40                 buffer = await FileIO.ReadBufferAsync(file);
 41                 bytes = buffer.ToArray();
 42                 constEffect = new PixelShaderEffect(bytes);
 43             };
 44             //选择图片
 45             selectPicture.Click += async (s, e) =>
 46             {
 47                 var file = await Ulit.SelectFileAsync(new List<string> { ".png", ".jpg" });
 48                 if (file == null)
 49                 {
 50                     bitmap = null;
 51                     color = null;
 52                 }
 53                 else
 54                 {
 55                     bitmap = await CanvasBitmap.LoadAsync(canvas.Device, await file.OpenAsync(FileAccessMode.Read));
 56                     //获取图像像素数组
 57                     bitmapColors = bitmap.GetPixelColors();
 58                     constRender = new CanvasRenderTarget(canvas, bitmap.Size);
 59                 }
 60                 CalcuationDrawPosition();
 61                 canvas.Invalidate();
 62             };
 63             canvas.Draw += (s, e) =>
 64             {
 65                 //绘制黑白网格
 66                 Win2dUlit.DrawGridGraphics(e.DrawingSession, 100);
 67                 //判断effect 和位图是否为空
 68                 if (effect == null || bitmap == null)
 69                     return;
 70                 if (color != null)
 71                 {
 72                     //绘制替换颜色效果
 73                     //颜色将0~255 转换成0~1 因为hlsl里面的颜色是0~1范围的并且是float类型,所以这里需要将每个颜色的分量除以255
 74                     Vector3 hsv = targetHsv.HsvToVector3();
 75                     Vector3 sourceColor = new Vector3(color.Value.R / 255f, color.Value.G / 255f, color.Value.B / 255f);
 76                     //通过键值对的形式设置属性传递到着色器里面
 77                     effect.Properties["threshold"] = (float)threshold.Value;
 78                     effect.Properties["hsv"] = hsv;
 79                     effect.Properties["sourceColor"] = sourceColor;
 80                     effect.Source1 = bitmap;
 81                     //绘制效果图
 82                     var effectTran = Win2dUlit.CalcutateImageCenteredTransform(effectWidth, effectHeight, bitmap.Size.Width, bitmap.Size.Height);
 83                     effectTran.Source = effect;
 84                     e.DrawingSession.DrawImage(effectTran);
 85                     //绘制选区图 右下角
 86                     var constTran = Win2dUlit.CalcutateImageCenteredTransform(previewWidth, previewHeight, bitmap.Size.Width, bitmap.Size.Height);
 87                     constEffect.Source1 = bitmap;
 88                     constEffect.Properties["threshold"] = (float)threshold.Value;
 89                     constEffect.Properties["color"] = sourceColor;
 90                     using (var ds = constRender.CreateDrawingSession())
 91                     {
 92                         ds.Clear(Colors.Transparent);
 93                         ds.DrawImage(constEffect, new Rect(new Point(), bitmap.Size), new Rect(new Point(), bitmap.Size));
 94                     }
 95                     constTran.Source = constRender;
 96                     e.DrawingSession.DrawImage(constTran, previewWidth, effectHeight);
 97                 }
 98                 else
 99                 {
100                     var effectTran = Win2dUlit.CalcutateImageCenteredTransform(effectWidth, effectHeight, bitmap.Size.Width, bitmap.Size.Height);
101                     effectTran.Source = bitmap;
102                     e.DrawingSession.DrawImage(effectTran);
103                 }
104                 //绘制原图 左下角
105                 var previewTran = Win2dUlit.CalcutateImageCenteredTransform(previewWidth, previewHeight, bitmap.Size.Width, bitmap.Size.Height);
106                 previewTran.Source = bitmap;
107                 e.DrawingSession.DrawImage(previewTran, 0, effectHeight);
108                 //绘制选择颜色缩略图
109                 if (isPressed && (pos.X >= offsetX && pos.X <= offsetX + width && pos.Y >= offsetY && pos.Y <= offsetY + height))
110                 {
111                     //画三角形
112                     CanvasPathBuilder builder = new CanvasPathBuilder(canvas);
113                     builder.BeginFigure(pos);
114                     builder.AddLine(pos - new Vector2(5, 5));
115                     builder.AddLine(pos - new Vector2(-5, 5));
116                     builder.EndFigure(CanvasFigureLoop.Closed);
117                     CanvasGeometry geometry = CanvasGeometry.CreatePath(builder);
118                     e.DrawingSession.FillGeometry(geometry, Colors.White);
119                     e.DrawingSession.FillRoundedRectangle(new Rect(pos.X - 50, pos.Y - 100 - 5, 100, 100), 10, 10, Colors.White);
120                     //采样位置
121                     var usePx = (pos.X - offsetX) / scale;
122                     var usePy = (pos.Y - offsetY) / scale;
123                     e.DrawingSession.DrawImage(bitmap, pos.X - 50, pos.Y - 100 - 5, new Rect(usePx - 50, usePy - 50, 100, 100));
124                     e.DrawingSession.DrawRectangle(new Rect(pos.X - 1, pos.Y - 56, 3, 3), Colors.Orange);
125                     e.DrawingSession.DrawRoundedRectangle(new Rect(pos.X - 50, pos.Y - 100 - 5, 100, 100), 10, 10, Colors.Black);
126                 }
127             };
128             threshold.ValueChanged += (s, e) => canvas.Invalidate();
129             RangeBaseValueChangedEventHandler hsvChanged = (s, e) =>
130             {
131                 if (color == null)
132                     return;
133 
134                 if (s is Slider slider)
135                 {
136                     switch (slider.Name)
137                     {
138                         case "hue":
139                             targetHsv.H = e.NewValue;
140                             break;
141                         case "saturation":
142                             targetHsv.S = e.NewValue;
143                             break;
144                         case "brightness":
145                             targetHsv.V = e.NewValue;
146                             break;
147                     }
148                     replaceColor.Fill = new SolidColorBrush((sourceHsv + targetHsv).HsvToRgb());
149                 }
150                 canvas.Invalidate();
151             };
152             hue.ValueChanged += hsvChanged;
153             saturation.ValueChanged += hsvChanged;
154             brightness.ValueChanged += hsvChanged;
155         }
156 
157         void InitOperate()
158         {
159             //画布大小更改,计算绘制区域
160             canvas.SizeChanged += (s, e) => CalcuationDrawPosition();
161             //鼠标再画布按下
162             canvas.PointerPressed += (s, e) =>
163             {
164                 isPressed = true;
165                 canvas.Invalidate();
166             };
167             //鼠标松开
168             canvas.PointerReleased += (s, e) =>
169             {
170                 isPressed = false;
171                 canvas.Invalidate();
172             };
173 
174             canvas.PointerMoved += (s, e) =>
175             {
176                 if (!isPressed || bitmap == null)
177                     return;
178                 Point p = e.GetCurrentPoint(canvas).Position;
179                 pos = p.ToVector2();
180                 //是否进入范围
181                 if ((p.X >= offsetX && p.X <= offsetX + width && p.Y >= offsetY && p.Y <= offsetY + height))
182                 {
183                     var x = p.X - offsetX;
184                     var y = p.Y - offsetY;
185                     x = x / scale;
186                     y = y / scale;
187                     color = bitmapColors[(int)x + (int)y * bitmap.SizeInPixels.Width];
188                     sourceHsv = sourceHsv.RgbToHsv(color.Value);
189                     replaceColor.Fill = new SolidColorBrush((sourceHsv + targetHsv).HsvToRgb());
190                     //设置选中的颜色
191                     selectColor.Background = new SolidColorBrush(color.Value);
192                     canvas.Invalidate();
193                 }
194             };
195         }
196         void CalcuationDrawPosition()
197         {
198             //判断effect 和位图是否为空
199             if (effect == null || bitmap == null)
200                 return;
201             var element = canvas;
202             effectWidth = (float)element.ActualWidth;
203             effectHeight = (float)element.ActualHeight * 0.7f;
204             previewWidth = effectWidth*0.5f;
205             previewHeight = (float)element.ActualHeight * 0.3f;
206             var mat = Win2dUlit.CalcutateImageCenteredMat(previewWidth, previewHeight, bitmap.Size.Width, bitmap.Size.Height);
207             scale = mat.M11;
208             offsetX = mat.M31;
209             offsetY = mat.M32 + effectHeight;
210             width = (float)(scale * bitmap.Size.Width);
211             height = (float)(scale * bitmap.Size.Height);
212         }
213     }
后台代码
 1  public static class Win2dUlit
 2     {
 3         public static void DrawGridGraphics(CanvasDrawingSession draw, int size = 100)
 4         {
 5             var target = new CanvasRenderTarget(draw, new Windows.Foundation.Size(size * 2, size * 2));
 6             using (var ds = target.CreateDrawingSession())
 7             {
 8                 for (int x = 0; x < 2; x++)
 9                 {
10                     for (int y = 0; y < 2; y++)
11                     {
12                         Color color = (x + y) % 2 == 0 ? Colors.White : Color.FromArgb(255, 219, 219, 219);
13                         ds.FillRectangle(new Rect(x * size, y * size, size, size), color);
14                     }
15                 }
16             }
17             var gridTargetEffect = new BorderEffect()
18             {
19                 Source = target,
20                 ExtendY = CanvasEdgeBehavior.Wrap,
21                 ExtendX = CanvasEdgeBehavior.Wrap,
22             };
23             draw.DrawImage(gridTargetEffect);
24         }
25 
26         public static Transform2DEffect CalcutateImageCenteredTransform(double cWidth, double cHeight, double iWidth, double iHeight)
27         {
28             var mat = CalcutateImageCenteredMat(cWidth, cHeight, iWidth, iHeight);
29             return new Transform2DEffect() { TransformMatrix = mat };
30         }
31 
32         public static Transform2DEffect CalcutateImageCenteredTransform(Windows.Foundation.Size cSize, Windows.Foundation.Size iSize)
33         {
34             return CalcutateImageCenteredTransform(cSize.Width, cSize.Height, iSize.Width, iSize.Height);
35         }
36 
37         public static Matrix3x2 CalcutateImageCenteredMat(Windows.Foundation.Size cSize, Windows.Foundation.Size iSize)
38         {
39             return CalcutateImageCenteredMat(cSize.Width, cSize.Height, iSize.Width, iSize.Height);
40         }
41         public static Matrix3x2 CalcutateImageCenteredMat(double cWidth, double cHeight, double iWidth, double iHeight)
42         {
43             float f = (float)Math.Min(cWidth / iWidth, cHeight / iHeight);
44             float ox = (float)(cWidth - iWidth * f) / 2;
45             float oy = (float)(cHeight - iHeight * f) / 2;
46             Matrix3x2 matrix3X2 = Matrix3x2.CreateScale(f) * Matrix3x2.CreateTranslation(ox, oy);
47             return matrix3X2;
48         }
49     }
Win2dUlit
  1 public class Hsv 
  2     {
  3         public Hsv() { }
  4         public Hsv(double h, double s, double v)
  5         {
  6             H = h;
  7             S = s;
  8             V = v;
  9         }
 10         /// <summary>
 11         /// 色相 0~360
 12         /// </summary>
 13         public double H { get;set; }
 14         /// <summary>
 15         /// 亮度 0~100
 16         /// </summary>
 17         public double S { get;set; }
 18         /// <summary>
 19         /// 饱和度 0~100
 20         /// </summary>
 21         public double V { get;set; }
 22        
 23         /// <summary>
 24         /// 将当前hsv转换成颜色返回
 25         /// </summary>
 26         public Color HsvToRgb()
 27         {
 28             double s = this.S / 100;
 29             double v = this.V / 100;
 30             double h = this.H;
 31             double R = 0, G = 0, B = 0;
 32             if (s == 0)
 33                 R = G = B = v;
 34             else
 35                 h /= 60;
 36             int i = (int)(h);
 37             double f = h - i;
 38             double a = v * (1 - s);
 39             double b = v * (1 - s * f);
 40             double c = v * (1 - s * (1 - f));
 41             switch (i)
 42             {
 43                 case 0:
 44                 case 6:
 45                     R = v; G = c; B = a;
 46                     break;
 47                 case 1:
 48                     R = b; G = v; B = a;
 49                     break;
 50                 case 2:
 51                     R = a; G = v; B = c;
 52                     break;
 53                 case 3:
 54                     R = a; G = b; B = v;
 55                     break;
 56                 case 4:
 57                     R = c; G = a; B = v;
 58                     break;
 59                 case 5:
 60                     R = v; G = a; B = b;
 61                     break;
 62             }
 63             R *= 255;
 64             G *= 255;
 65             B *= 255;
 66             Color color = Color.FromArgb(255, (byte)R, (byte)G, (byte)B);
 67             return color;
 68         }
 69         public Hsv RgbToHsv(Color c)
 70         {
 71             int R, G, B;
 72             R = c.R;
 73             G = c.G;
 74             B = c.B;
 75             double V, S, H = 0;
 76             double max = Math.Max(R, Math.Max(G, B));
 77             double min = Math.Min(R, Math.Min(G, B));
 78             var dalte = max - min;
 79             V = max / 255;
 80             S = dalte == 0 ? 0 : dalte / max;
 81             if (R == max) H = (G - B) / (max - min) * 60;
 82             if (G == max) H = 120 + (B - R) / (max - min) * 60;
 83             if (B == max) H = 240 + (R - G) / (max - min) * 60;
 84             if (dalte == 0) H = 0;
 85             if (H < 0)
 86                 H = H + 360;
 87             Hsv hsv = new Hsv() { H = (int)H, S = S * 100, V = V * 100 };
 88             return hsv;
 89         }
 90         public static Hsv operator +(Hsv a, Hsv b)
 91         {
 92             var h = (a.H + b.H) % 360;
 93             var s = (a.S + b.S) % 100;
 94             var v = (a.V + b.V) % 100;
 95             return new Hsv(h, s, v);
 96         }
 97         public Vector3 HsvToVector3()
 98         {
 99             var x = (float)(this.H / 360.0);
100             var y = (float)(this.S / 100.0);
101             var z = (float)(this.V / 100.0);
102             return new Vector3(x, y, z);
103         }
104     }
105 }
Hsv

  五.总结

    通过使用hlsl着色器代码,我们就可以实现自定义滤镜,来实现不同的效果。

posted @ 2022-09-23 00:37  吃饭/睡觉  阅读(135)  评论(0编辑  收藏  举报