WebGL像素处理

在对图形进行像素处理之前,需要对其进行像素化。

所谓像素化,就是把一个图像看成是由一组像素点组合而成的。每个像素点负责描述图像上的一个点,并且带有这个点的基本绘图信息。那对于一张 800 像素宽、600 像素高的图片来说,整张图一共就有 48 万个像素点。

每一个像素点都存储着四个颜色通道RGBA

而常见的像素处理应用有:

  • 颜色滤镜(灰度图、颜色滤镜等)
  • 高斯滤镜(人像美颜等)

在实际的可视化项目中,可以通过像素处理来增强视觉效果

1.颜色滤镜

[1]灰度图

实现灰度图简单来说就是将一张彩色图片变为灰白色图片。具体的实现思路是,我们先将该图片的每个像素点的 R、G、B 通道的值进行加权平均,然后将这个值作为每个像素点新的 R、G、B 通道值,具体公式如下:
V = a R + b G + c B R ′ = G ′ = B ′ = v a + b + c V=aR+bG+cB\\ R'=G'=B'=\frac{v}{a+b+c} V=aR+bG+cBR=G=B=a+b+cv
其中 R、G、B 是原图片中的 R、G、B 通道的色值,V 是加权平均色值,a、b、c 是加权系数,满足 (a + b + c) = 1。

下面使用公式对应的颜色矩阵来实现:

precision mediump float; 
uniform sampler2D u_image;
varying vec2 v_texCoord;  
void main() {
    vec4 color = texture2D(u_image, v_texCoord); //原色
    float average = (color.r + color.g + color.b) / 3.0;//加权平均
    gl_FragColor = vec4(average, average, average, color.a);
}

对于webgl加载纹理图像这里就不过多阐述

在这里插入图片描述

[2]改变图象亮度

实现原理,分别给RGB通道乘一个常数p,大于1时变亮,小于1时变暗
R ′ = R ∗ p G ′ = G ∗ p B ′ = B ∗ p R'=R*p\\ G'=G*p\\ B'=B*p R=RpG=GpB=Bp

precision mediump float; 
uniform sampler2D u_image;
varying vec2 v_texCoord;  
void main() {
    vec4 color = texture2D(u_image, v_texCoord); //原色
    float p = 2.0;
    gl_FragColor = vec4(color.r * p,color.g * p, color.b * p, color.a);
}

在这里插入图片描述

处理使用公式直接对原图的rgb通道直接修改实现一些简单的图像处理效果

2.高斯模糊/平滑

上面的滤镜都是一些直接操作图像颜色通道的简单滤镜,而现在一种相对复杂的滤镜——高斯模糊(Gaussian Blur),这个滤镜可以对图像进行平滑处理(美颜)或者是做部分模糊处理,从而突出我们要呈现给用户的内容

在这里插入图片描述

对于高斯模糊/平滑在之前的计算机图形学文章中有做过笔记记录——Rasterization光栅化,也是解决锯齿/走样Alising的一种方法,所以高斯模糊就是一个低通滤波(图像与二维正态分布做卷积),简单理解的话就是按照高斯分布的权重对当前像素点加权平均,而距离当前像素越近的点的权重越高,权重分布满足正态分布,从而达到平滑/模糊效果。

为了减低算法复杂度和提高性能这里就不对每个像素计算整张图片像素的权重,而是采用——均值模糊/滤波(滤波器/卷积核,整体操作叫卷积)来做简单平滑/模糊,这里以3x3的卷积核方式做测试学习。

卷积内核就是一个 3×3 的矩阵,矩阵中的每一项代表当前处理的像素和周围8个像素的乘法因子, 相乘后将结果加起来除以内核权重(内核中所有值的和或 1.0 ,取二者中较大者)

这里采用最简单的3x3卷积核进行处理(也就是之前学计算机图形学中举的示例的那个卷积核—Rasterization光栅化

在这里插入图片描述

precision mediump float;

// 纹理
uniform sampler2D u_image;
uniform vec2 u_textureSize;
uniform float u_kernel[9];
uniform float u_kernelWeight; //权重

// 从顶点着色器传入的纹理坐标
varying vec2 v_texCoord;

void main() {
    vec2 onePixel = vec2(1.0, 1.0) / u_textureSize;
    vec4 colorSum =
    texture2D(u_image, v_texCoord + onePixel * vec2(-1, -1)) * u_kernel[0] +
    texture2D(u_image, v_texCoord + onePixel * vec2( 0, -1)) * u_kernel[1] +
    texture2D(u_image, v_texCoord + onePixel * vec2( 1, -1)) * u_kernel[2] +
    texture2D(u_image, v_texCoord + onePixel * vec2(-1,  0)) * u_kernel[3] +
    texture2D(u_image, v_texCoord + onePixel * vec2( 0,  0)) * u_kernel[4] +
    texture2D(u_image, v_texCoord + onePixel * vec2( 1,  0)) * u_kernel[5] +
    texture2D(u_image, v_texCoord + onePixel * vec2(-1,  1)) * u_kernel[6] +
    texture2D(u_image, v_texCoord + onePixel * vec2( 0,  1)) * u_kernel[7] +
    texture2D(u_image, v_texCoord + onePixel * vec2( 1,  1)) * u_kernel[8] ;

    // 只把rgb值求和除以权重
    gl_FragColor = vec4((colorSum / u_kernelWeight).rgb, 1.0);
}
const kernelLocation = gl.getUniformLocation(program, "u_kernel[0]");
const kernelWeightLocation = gl.getUniformLocation(program, "u_kernelWeight");

const gaussianBlur3 = [
    1, 1, 1,
    1, 1, 1,
    1, 1, 1
];


function computeKernelWeight(kernel) {
    cosnt weight = kernel.reduce(function(prev, curr) {
    	return prev + curr;
    });
    return weight <= 0 ? 1 : weight;
}
gl.uniform1fv(kernelLocation, gaussianBlur3);
gl.uniform1f(kernelWeightLocation, computeKernelWeight(gaussianBlur3));

在不同的需求情况下可以选择不同的卷积内核/矩阵来实现多种多样的处理效果webgl fundamentals网站有很多处理的示例

3.多层滤镜叠加

要实现多层滤镜效果叠加可以采用帧缓冲区技术,创建两个或多个纹理,然后将一个纹理渲染的结果传递给另一个纹理,如果还要叠加,在将这个纹理的结果传递回去,依次叠加,像打乒乓球一样。

其实现大致思路为:首先在颜色缓冲区渲染绘制原图像,然后创建两纹理对象并将其分别关联到两个帧缓冲区对象中的颜色关联对象,然后定义一些滤镜卷积内核来叠加效果,先是绘制在定义的一个矩形框内把原始图像作为纹理贴上去(在第一个帧缓冲区中),然后将第一个帧缓冲取中绘制的原始图像做为一个纹理对象传递给第二个帧缓冲区中作为初始纹理图像,接下来在对其进行卷积核处理,处理完后再作为一个纹理图像(对象)传递给回第一个帧缓冲区,它将其看作初始纹理做卷积核处理,一直如此直至将要叠加的效果全都叠加上去,最后将最终结果作为一个纹理对象传递个颜色缓冲区进行绘制gl.bindFramebuffer(gl.FRAMEBUFFER, null);

下面截取部分核心代码进行说明

//从原图开始
gl.bindTexture(gl.TEXTURE_2D, originalImageTexture);
//fbo为null时在颜色缓冲区,为帧缓冲区对象时在帧缓冲区中绘制
function setFramebuffer(fbo, width, height) {
    gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
    //传递给shader帧缓冲区或者颜色缓冲区的高宽
    gl.uniform2f(resolutionLocation, width, height);
    //设置屏幕空间为帧缓冲区大小
    gl.viewport(0, 0, width, height);
}
//循环叠加绘制多种滤镜
var count = 0;
for (var ii = 0; ii < tbody.rows.length; ++ii) {
    var checkbox = tbody.rows[ii].firstChild.firstChild;
    if (checkbox.checked) {
        //在帧缓冲区中绘制
        setFramebuffer(framebuffers[count % 2], image.width, image.height);
        drawWithKernel(checkbox.value);
        //将前一个绘制
        gl.bindTexture(gl.TEXTURE_2D, textures[count % 2]);

        ++count;
    }
}
//最后在颜色缓冲区中将所有滤镜叠加的效果作为一个纹理对象传递个颜色缓冲区对象绘制
setFramebuffer(null, gl.canvas.width, gl.canvas.height);

在这里插入图片描述

示例代码:像素图象处理

参考:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

seeooco

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值