【Unity3D Shader】学习笔记-卷积核
前言
在纹理采样中已经介绍了简单的模糊效果是如何制作的,本篇通过卷积核来介绍其原理,能制作的效果不仅限于下面的两种,还有更多的效果可以去搜一下。
[声明:本笔记系列文章的图片资源都源自百度图片搜索,如有问题联系我]
一、卷积核
上两个图就是卷积核,左侧就是简单模糊的卷积核,右侧则是高斯模糊的卷积核。针对左侧的卷积核最终结果想要保持在1的范围内,就必须去平均值。而右侧是已经计算好确保和在1的范围内(超过1之后最终效果就会很亮)。在进行纹理采样时,中间的点为当前的(u,v)坐标,周围的格子坐标就是在当前的坐标上进行偏移“N个单位”。将采样的结果乘以格子中的对应值然后相加,即可得到结果(左侧形式需要除以5)。
https://www.jianshu.com/p/8d2d93c4229b
这里说的比较详细
二、高斯模糊
高斯模糊就需要说到正态分布,具体的资料请百度了解,下面是二维的计算公式,其中σ(西格玛)是参数取值,(x,y)这里就可以是uv坐标。高斯模糊在很多效果制作中需要,因此可以定义成公共的函数方法,以提供不同效果快速调用。
static const float TWO_PI = 6.28319; static const float E = 2.71828; // 套用上面的公式 float gaussian(int x, int y, float sigma) { return (1 / sqrt(TWO_PI * sigma)) * pow(E, -((x * x) + (y * y)) / (2 * sigma)); } // uv坐标, lower和upper是模糊的格子3x3(-1,1) 5x5(-2,2), sigma外部提供 fixed3 gaussian_blur(fixed2 uv, int lower, int upper, float sigma) { fixed3 col = fixed3(0,0,0); float kernelSum = 0; for (int x = lower; x <= upper; ++x) { for (int y = lower; y <= upper; ++y) { float gauss = gaussian(x, y, sigma); kernelSum += gauss; fixed2 offset = fixed2(_MainTex_TexelSize.x * x, _MainTex_TexelSize.y * y); col += gauss * tex2D(_MainTex, uv + offset); } } col /= kernelSum; return col; }
还可以使用已经计算好的卷积核算子进行,下面是某一个sigma生成的3x3卷积核。
fixed4 gaussian_blur_3v3(fixed2 uv) { /* 0.0947416 | 0.118318 | 0.0947416 0.118318 | 0.147761 | 0.118318 0.0947416 | 0.118318 | 0.0947416 */ float2 texelSize = _MainTex_TexelSize * _Blur; //模糊程度 fixed4 color = fixed4(0,0,0,0); color += tex2D(_MainTex, uv + float2(-texelSize.x, -texelSize.y)) * 0.0947416; color += tex2D(_MainTex, uv + float2(-texelSize.x, 0)) * 0.118318; color += tex2D(_MainTex, uv + float2(-texelSize.x, texelSize.y)) * 0.0947416; color += tex2D(_MainTex, uv + float2( 0, -texelSize.y)) * 0.118318; color += tex2D(_MainTex, uv + float2( 0, 0)) * 0.147761; color += tex2D(_MainTex, uv + float2( 0, texelSize.y)) * 0.118318; color += tex2D(_MainTex, uv + float2( texelSize.x, -texelSize.y)) * 0.0947416; color += tex2D(_MainTex, uv + float2( texelSize.x, 0)) * 0.118318; color += tex2D(_MainTex, uv + float2( texelSize.x, texelSize.y)) * 0.0947416; return color; }
上图只使用了3x3的卷积核计算,还可以使用5x5或者更高,出于性能考虑越高影响越大。
三、边缘检测
https://www.cnblogs.com/ninghechuan/p/9529936.html
Sobel边缘检测
fixed luminance(fixed4 color) { return 0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b; } fixed sobel_3v3(fixed2 uv) { /* -1, 0, 1, 1, 2, 1 -2, 0, 2, 0, 0, 0, -1, 0, 1 -1,-2,-1 */ float2 texelSize = _MainTex_TexelSize; fixed gcol = 0; fixed gx = 0; fixed gy = 0; gcol = luminance(tex2D(_MainTex, uv + float2(-texelSize.x, -texelSize.y))); gx += gcol * -1; gy += gcol * 1; gcol = luminance(tex2D(_MainTex, uv + float2(-texelSize.x, 0))); gx += gcol * -2; gcol = luminance(tex2D(_MainTex, uv + float2(-texelSize.x, texelSize.y))); gx += gcol * -1; gy += gcol * -1; gcol = luminance(tex2D(_MainTex, uv + float2( 0, -texelSize.y))); gy += gcol * 2; //gcol = luminance(tex2D(_MainTex, uv + float2( 0, 0))); gcol = luminance(tex2D(_MainTex, uv + float2( 0, texelSize.y))); gy += gcol * -2; gcol = luminance(tex2D(_MainTex, uv + float2( texelSize.x, -texelSize.y))); gx += gcol * 1; gy += gcol * 1; gcol = luminance(tex2D(_MainTex, uv + float2( texelSize.x, 0))); gx += gcol * 2; gcol = luminance(tex2D(_MainTex, uv + float2( texelSize.x, texelSize.y))); gx += gcol * 1; gy += gcol * -1; return abs(gx) + abs(gy); } fixed4 frag (v2f i) : SV_Target { fixed gv = sobel_3v3(i.uv); fixed4 col = tex2D(_MainTex, i.uv); fixed4 edge = fixed4(1,0,0,1); fixed4 final_col = lerp(edge, col, gv); return final_col; }