Learn ComputeShader 08 Blurring an image

1.原理

假设要模糊一个像素,模糊半径为10,如下图所示

模糊一个像素需要合并400个像素的颜色,如果要模糊整张图像那么计算量是非常大的。

 

为了简化计算量,我们将这个模糊分成两个阶段 。首先,水平模糊图像,然后使用这个图像垂直模糊图像,这个方法可以极大减少计算量

原始 模糊的计算量:

假设图像的尺寸为 width x height,我们对每个像素都要应用这个 21x21 的卷积核。因此:

  • 对于每个像素,都要处理它周围的 21x21 个像素,总共需要处理 441 个像素的加权平均。

  • 由于图像有 width * height 个像素,因此整个图像的计算量就是:

优化后的计算量:

1. 水平模糊

对于图像的每一行(height 行,每行 width 个像素),使用高斯权重对当前像素及其左右 10 个邻居像素进行加权平均。这一操作只沿着 x 方向进行。

  • 对于每一个像素,处理 2 * 10 + 1 = 21 个像素。
  • 总的计算量为:width * height * 21

2. 垂直模糊

水平模糊结束后,得到一个经过水平方向模糊的图像。然后我们在垂直方向上对每一列进行模糊(width 列,每列 height 个像素),这次的模糊只沿着 y 方向进行。

  • 对于每一个像素,处理 2 * 10 + 1 = 21 个像素。
  • 总的计算量为:width * height * 21

总计算量 =  width * height * 21 + width * height * 21 = width * height * 42

可以看到计算量减少了百分之90,而且优化后的模糊效果和原来的效果几乎没有什么差别。

2.实战

这里主要学习核函数的编写思路

首先得到左边第一个开始的像素颜色,为了避免是负数加上了限制,然后计算一共要模糊的像素的数量,这里的代码可以根据下面这张图来理解。然后从最左边的像素的颜色开始,计算所有要计算的颜色之和,最后除以计算的像素的数量。

[numthreads(8, 8, 1)]
void HorzPass(uint3 id : SV_DispatchThreadID)
{
    int left = max(0, (int)(id.x - blurRadius));
    int count = min(blurRadius, (int)id.x) + min(blurRadius, source.Length.x - (int)id.x);
    float4 color = 0;
    uint2 index = uint2((uint)left, id.y);

    [unroll(100)]//限制最大循环次数
    for(int x = 0; x < count; x++) {
        color += source[index];
        index.x++;
    }

    color /= (float)count;
    horzOutput[id.xy] = color;
}

 一样的思路,在另一个核函数中编写垂直方向的模糊。

从下往上,计算像素的颜色并求平均,最后输出模糊后的颜色

[numthreads(8, 8, 1)]
void Highlight(uint3 id : SV_DispatchThreadID)
{
    int bottom = max(0, (int)(id.y - blurRadius));
    int count = min(blurRadius, (int)id.y) + min(blurRadius, source.Length.y - (int)id.y);
    float4 blurColor = 0;
    uint2 index = uint2(id.x, (uint)bottom);

    [unroll(100)]
    for(int y = 0; y < count; y++) {
        blurColor += horzOutput[index];
        index.y++;
    }

    blurColor /= (float)count;
    output[id.xy] = blurColor;

}

模糊效果:

 

但是我们想要人物周围是清晰的,其它地方是模糊且变暗的。先将模糊后的颜色乘上shade进行变暗,然后根据当前像素是否在人物周围来对颜色进行插值

[numthreads(8, 8, 1)]
void Highlight(uint3 id : SV_DispatchThreadID)
{
    int bottom = max(0, (int)(id.y - blurRadius));
    int count = min(blurRadius, (int)id.y) + min(blurRadius, source.Length.y - (int)id.y);
    float4 blurColor = 0;
    uint2 index = uint2(id.x, (uint)bottom);

    [unroll(100)]
    for(int y = 0; y < count; y++) {
        blurColor += horzOutput[index];
        index.y++;
    }

    blurColor /= (float)count;

    float4 srcColor = source[id.xy];
    float4 shadedBlurColor = blurColor * shade;
    float highlight = inCircle((float2)id.xy, center.xy, radius, edgeWidth);
    float4 color = lerp(shadedBlurColor, srcColor, highlight);

    output[id.xy] = color;

}

效果:

 总结:这种模糊是最简单的模糊。还有一种高斯模糊效果更好,它的做法是根据与模糊像素的距离来赋予每个像素一个权重,而不是像这次这样平均模糊。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值