WebGL入门:高斯模糊
前言
高斯模糊这个词最早我是在一个Photoshop视频教程中看到的,当时还不知道它的原理,大致是知道它能让图片变的模糊,可以用来人像磨皮,用处挺大的,后来才知道它的本质实际上是通过正态分布曲线去确定权重值,然后叠加附近的颜色得到,瞬间就让我想到了一句很火的话“童年的一颗子弹,多年之后正中眉心”
一、基础概念
高斯模糊(Gaussian Blur) 是一种非常常见且重要的图像处理技术。它的核心思想是通过对图像中的每个像素及其周围的像素进行加权平均来实现的,而这个“加权”的权重是按照高斯分布(也称为正态分布)来计算的。
二、绘制素材
实际上就是制造一个图片用来做高斯模糊的素材,可以是个已经存在的图片也可以用画布画出来,此处不想依赖太多东西就用画布画出来了
2.1思路
- 绘制一个由圆心向四周颜色渐变的圆
- 绘制一个黑色的圆
- 绘制两个白色的矩形
2.2关键代码
function createTexture(gl: WebGLRenderingContext): WebGLTexture | null { const texture = gl.createTexture() if (!texture) return null // 创建一个简单的渐变纹理作为演示 const canvas = document.createElement(\'canvas\') canvas.width = 256 canvas.height = 256 const ctx = canvas.getContext(\'2d\')! // 绘制彩色渐变 const gradient = ctx.createRadialGradient(128, 128, 0, 128, 128, 128) gradient.addColorStop(0, \'#ff0000\') gradient.addColorStop(0.3, \'#00ff00\') gradient.addColorStop(0.6, \'#0000ff\') gradient.addColorStop(1, \'#ffff00\') ctx.fillStyle = gradient ctx.fillRect(0, 0, 256, 256) // 添加一些几何图形 ctx.fillStyle = \'#ffffff\' ctx.fillRect(50, 50, 50, 50) ctx.fillRect(150, 150, 50, 50) ctx.fillStyle = \'#000000\' ctx.beginPath() ctx.arc(128, 128, 30, 0, Math.PI * 2) ctx.fill() gl.bindTexture(gl.TEXTURE_2D, texture) gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) return texture}
三、 高斯模糊
3.1思路
- 确定采样点的数量为5
- 根据正态分布曲线确定5个采样点的权重值
- 根据权重值计算附近5个单位长度的点对当前点的颜色影响
3.2关键代码
const FSHADER_SOURCE = ` precision mediump float; uniform sampler2D u_Texture; uniform vec2 u_TextureSize; uniform float u_BlurIntensity; uniform int u_BlurDirection; // 0: horizontal, 1: vertical, 2: both uniform bool u_EnableBlur; varying vec2 v_TexCoord; void main() { if (!u_EnableBlur) { gl_FragColor = texture2D(u_Texture, v_TexCoord); return; } vec4 color = vec4(0.0); vec2 texelSize = 1.0 / u_TextureSize; // 高斯模糊权重 float weights[5]; weights[0] = 0.227027; weights[1] = 0.1945946; weights[2] = 0.1216216; weights[3] = 0.054054; weights[4] = 0.016216; // 中心像素 color += texture2D(u_Texture, v_TexCoord) * weights[0]; for (int i = 1; i < 5; i++) { vec2 offset = vec2(0.0); if (u_BlurDirection == 0 || u_BlurDirection == 2) { // 水平模糊 offset.x = texelSize.x * float(i) * u_BlurIntensity; color += texture2D(u_Texture, v_TexCoord + offset) * weights[i]; color += texture2D(u_Texture, v_TexCoord - offset) * weights[i]; } if (u_BlurDirection == 1 || u_BlurDirection == 2) { // 垂直模糊 offset = vec2(0.0, texelSize.y * float(i) * u_BlurIntensity); color += texture2D(u_Texture, v_TexCoord + offset) * weights[i]; color += texture2D(u_Texture, v_TexCoord - offset) * weights[i]; } } gl_FragColor = color; }`
最终效果
四、源码
传送门 欢迎点亮小星星