径向含糊简介
径向含糊,是一种从核心向外呈幅射状,逐步含糊的成果。因而径向含糊常常会产生一些核心的发散成果,在 PS 中同样也有径向含糊的滤镜成果。径向含糊通常也称为变焦含糊。径向含糊(Radial Blur)能够给画面带来很好的速度感,是各类游戏中后处理的常客,也罕用于 Sun Shaft 等后处理特效中作为光线投射(体积光)的模仿。
在游戏中,经常应用径向含糊来模仿一些静止的动感成果。如鬼泣 4 中的场景切换特效,和一些技能打击特效;赛车游戏也尝用来模仿动感含糊,如狂野飙车,极品飞车等。径向含糊还是实现体积光照的一种技术手段之一,如下图:
径向含糊的原理
图形学中含糊的大抵原理都是一样的:就是从原像素四周去寻找左近像素的色彩,来独特影响以后的像素色彩。如高斯含糊就是将原像素周围像素的色彩加权求和作为原像素的色彩以达到含糊的目标。
不同的含糊就是取周边像素和加权求和的算法不太一样。径向含糊的特点是从某个中心点向外散射扩散,因而其须要采样的像素来自于以后的像素点和中心点的连线上,通过参数能够管制采样的数量和采样步进的间隔。像素的色彩是由该像素的点与中心点之间连线上进行采样,而后求将这些采样点色彩的加权均匀。依据径向含糊的个性,离指标点越近采样点越密集,反之亦然。
因而,实现径向含糊的大抵流程如下:
- 确定径向含糊的中心点,个别为画布的中心点,或这个某个对象的中心点在屏幕投影所在的地位。(留神中心点是 2 维坐标)
- 计算以后像素和中心点的间隔和向量线段。
- 在线段下面进行采样,加权。
- 将含糊的后果和原图进行一个叠加合成(可能须要)
webgl 实现径向含糊
径向含糊是一个后处理过程,径向含糊能够对动态的图片施加成果,也能够对动静渲染的图像施加成果。本示例中将对动静的图像施加成果。先上一张图看看成果:
首先绘制的几个圆环对象,而后对绘制的图像施加径向含糊。
渲染到纹理
要施加径向含糊,首先要把圆环绘制到 texture 对象下面,咱们晓得,通过 framebuffer 的技术,能够实现把绘制成果输入到贴图对象上。
无关 framebuffer 的技术,不属于本文重点介绍的内容,如果读者不相熟,能够自行查找相干材料。也能够参考:渲染到纹理。
输出贴图对象
要把贴图对象输入到屏幕下面,咱们须要结构一个矩形对象,该对象正好是 webgl 坐标系中的四个顶点,代码如下:
function quad() {var pos = [-1,1,0, -1,-1,0, 1,-1,0, 1,1,0],
st = [0,1,0,0,1,0, 1,1],
idx = [0,1,2,0,2,3];
return {
p:pos,
t:st,
i:idx,
}
}
上述对象能够正好把一个贴图对象残缺的输入到屏幕上(webgl 坐标系)
实现径向含糊
径向含糊的次要在着色器语言中进行实现,而且次要是在片元着色器中,上面是片元着色器的代码:
var ofs = `precision mediump float;
uniform sampler2D texture;
uniform float strength;
varying vec2 vTexCoord;
const float tFrag = 1.0 / 512.0;
const float nFrag = 1.0 / 30.0;
const vec2 centerOffset = vec2(256.0, 256.0);
float rnd(vec3 scale, float seed) {return fract(sin(dot(gl_FragCoord.stp + seed, scale)) * 43758.5453 + seed);
}
vec4 fragRadialBlur(){vec2 fc = vec2(gl_FragCoord.s, 512.0 - gl_FragCoord.t);
vec2 fcc = fc - centerOffset;
vec3 destColor = vec3(0.0);
float random = rnd(vec3(12.9898, 78.233, 151.7182), 0.0);
float totalWeight = 0.0;
for (float i = 0.0; i <= 30.0; i++) {float percent = (i + random) * nFrag;
float weight = percent - percent * percent;
vec2 t = fc - fcc * percent * strength * nFrag;
destColor += texture2D(texture, t * tFrag).rgb * weight;
totalWeight += weight;
}
return vec4(destColor / totalWeight, 1.0);
}
void main(void) {gl_FragColor = fragRadialBlur();
}`;
随机函数
首先申明了一个 rnd 函数,此函数是一个简略的随机数生成器,当给定向量和种子值时,将返回 0 到 1 范畴内的随机数。
应用随机数,是为了让含糊的成果出现肯定的随机状态,而不是依照某种一致性的准则进行含糊。
rnd 在每次片元着色器中都会调用,因而要尽量应用轻量化的实现,不然可能会造成性能负载。
定义相干变量
const float tFrag = 1.0 / 512.0;
const float nFrag = 1.0 / 30.0;
const vec2 centerOffset = vec2(256.0, 256.0);
而后定义相干变量。在此示例中,缩放的中心点设置为画布的核心。
画布的大小为 512 像素,因而下面的代码相应地申明了一些常量。
vec2 变量 centerOffset 用于定义核心地位。
floag 变量 tFrag 用于规范化,把二维顶点坐标转换成归一化为 uv 坐标,以正确援用着色器中的纹理像素。
另一个 float 类型常量 nFrag 用于着色器中 for 的语句进行迭代解决进行归一化。
径向含糊逻辑
vec4 fragRadialBlur(){vec2 fc = vec2(gl_FragCoord.s, 512.0 - gl_FragCoord.t);
vec2 fcc = fc - centerOffset;
vec3 destColor = vec3(0.0);
float random = rnd(vec3(12.9898, 78.233, 151.7182), 0.0);
float totalWeight = 0.0;
for (float i = 0.0; i <= 30.0; i++) {float percent = (i + random) * nFrag;
float weight = percent - percent * percent;
vec2 t = fc - fcc * percent * strength * nFrag;
destColor += texture2D(texture, t * tFrag).rgb * weight;
totalWeight += weight;
}
return vec4(destColor / totalWeight, 1.0);
}
首先,通过 gl_FragCoord 计算出变量 fc,示意以后像素在 canvas 画布的坐标。留神 gl_FragCoord 坐标的原点是在左下角,而 canvas 画布的坐标原点在左上角,应此做了一个翻转计算:
512.0 - gl_FragCoord.t
计算变量 fcc,示意以后坐标到中心点的向量。
定义变量 destColor 用于保留最终要输入的像素色彩。而后计算一个随机值 random。totalWeight 示意每次迭代时候采样像素所占的比重之和。
而后开始迭代解决。
在片段着色器中通过 for 语句进行迭代解决,应用 i 加上随机数之和来计算指标像素的 percent(比重),而后通过 percent – percent * percent 是为了把线性的比重数据变成非线性的比重 weight。
而后通过 percent 和 strength 计算出一采样的坐标点 t,其中 strength 示意径向含糊的强度,此值越大,偏离越大,含糊成果越强。并通过 texture2D 函数获取对应坐标点的色彩值,而后乘以 weight 并退出到 destColor,并最终计算 totalWeight。
最初,实现迭代过程后进行归一化 destColor:destColor / totalWeight。
阐明
此处咱们应用了 30 次迭代,看起来性能并没有太大影响。理论过程中,能够抉择不同的迭代次数,来达到成果和性能的均衡。
最终成果如下,
本文也发表在我的 webgl 专栏,残缺代码能够在专栏中获取:
https://xiaozhuanlan.com/topic/6480975213
下一篇文章讲述利用径向含糊实现体积光的成果,先上一张图看看成果:
案例视频 能够关注视频号 “ITman 彪叔 ” 观看,也欢送关注公众号。