webgl 纹理进阶(二)双线性插值与图像处理(卷积)

一.简介

上一节记录了关于图像缩小时的一些处理方法,本节记录图像放大时的一些原理,并通过图像处理来理解像素插值的过程。

图像放大是发生在提供的图像小于实际绘制的像素,即一个texel对应多个pixel,此时纹理采样会采用双线性插值的算法,下图是一个示例:

Bilinear Interpolation 双线性插值

双线性插值:水平插值+竖直插值

lerp为linear Interpolation 的缩写

下图为最近的四个点插值:

  • 找到目标点周围四点
  • 红点离四个点左下角的垂直与水平距离
  • 在u01与u11之间线性插值得到u1,同理得到u0,同样将u0与u1插值得到红点

二.在webgl中的使用

在webgl中,已经默认实现了双线性插值 ,因此使用非常简单: gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.NEAREST) 对此函数解释如下:

Available in WebGL 1
gl.TEXTURE_MAG_FILTER Texture magnification filter gl.LINEAR (default value), gl.NEAREST.

下面是一段使用代码:

js 复制代码
function initTexture(gl,image){
    var texture=gl.createTexture()
    //获取纹理存储位置
    var u_Sampler=gl.getUniformLocation(gl.program, 'u_Sampler')
    //y轴翻转
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL,1)

    // //设置一次读取一字节
    // const alignment =1;
    // gl.pixelStorei(gl.UNPACK_ALIGNMENT, alignment);
    //开启0号纹理单元
    gl.activeTexture(gl.TEXTURE0)
    //想target绑定纹理单元
    gl.bindTexture(gl.TEXTURE_2D, texture)
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,gl.RGBA,gl.UNSIGNED_BYTE,image)


    gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR)


    gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.NEAREST)


    // 设置参数,让我们可以绘制任何尺寸的图像
    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);


    //将纹理传递给着色器
    gl.uniform1i(u_Sampler,0)

}

我加载了一个240*180的图片,结果如下:

在上述代码中,在webgl1中限制了图片为 2的n次幂,但是本次图片不是,因此加了下面两句: 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);

三.图像处理

由于双线性插值已经由webgl内部实现,但是可以通过图像处理的过程可以理解如何通过周边像素计算当前像素的值。

3.1 计算与左右像素的均值

由于WebGL的纹理坐标范围是 0.0 到 1.0 , 那我们可以简单计算出移动一个像素对应的距离, onePixel = 1.0 / textureSize。 因此在shader中可以如下:

js 复制代码
<script id="fragment-shader-2d" type="x-shader/x-fragment">
  precision mediump float;
   
  // 纹理
  uniform sampler2D u_image;
  uniform vec2 u_textureSize;
   
  // 从顶点着色器传入的像素坐标
  varying vec2 v_texCoord;
    
  void main() {
  // 计算1像素对应的纹理坐标
  vec2 onePixel = vec2(1.0, 1.0) / u_textureSize;
   
  // 对左中右像素求均值
  gl_FragColor = (
  texture2D(u_image, v_texCoord) +
  texture2D(u_image, v_texCoord + vec2(onePixel.x, 0.0)) +
  texture2D(u_image, v_texCoord + vec2(-onePixel.x, 0.0))) / 3.0;
  }
  </script>

在js中:

js 复制代码
const u_textureSizeLoc=gl.getUniformLocation(gl.program,'u_textureSize')
gl.uniform2f(u_textureSizeLoc,240,180)

效果如下:

3.2 使用附近9个像素的平均值并给每个像素添加权重:

shader如下:

c++ 复制代码
const fShader=/*glsl*/`
precision mediump float;
uniform sampler2D u_Sampler;
uniform vec2 u_textureSize;
uniform float u_kernel[9]; 
uniform float u_kernelWeight; 
varying vec2 v_TexCoord;
void main(){
     // 计算1像素对应的纹理坐标
    vec2 onePixel = vec2(1.0, 1.0) / u_textureSize;
    vec4 color=
    texture2D(u_Sampler,v_TexCoord+onePixel*vec2(-1.0,-1.0))*u_kernel[0]+
    + texture2D(u_Sampler,v_TexCoord+onePixel*vec2(0.0,-1.0))*u_kernel[1]+
    + texture2D(u_Sampler,v_TexCoord+onePixel*vec2(1.0,-1.0))*u_kernel[2]+
    + texture2D(u_Sampler,v_TexCoord+onePixel*vec2(-1.0,0.0))*u_kernel[3]+
    + texture2D(u_Sampler,v_TexCoord+onePixel*vec2(0.0,0.0))*u_kernel[4]+
    + texture2D(u_Sampler,v_TexCoord+onePixel*vec2(1.0,0.0))*u_kernel[5]+
    + texture2D(u_Sampler,v_TexCoord+onePixel*vec2(-1.0,1.0))*u_kernel[6]+
    + texture2D(u_Sampler,v_TexCoord+onePixel*vec2(0.0,1.0))*u_kernel[7]+
    + texture2D(u_Sampler,v_TexCoord+onePixel*vec2(1.0,1.0))*u_kernel[8];
    gl_FragColor=vec4((color/u_kernelWeight).rgb,1.0);
}`

js中传入数组和权重:

js 复制代码
 const edgeDetect= [
    -1, -1, -1,
    -1,  8, -1,
    -1, -1, -1
    ],
const u_kernelLoc=gl.getUniformLocation(gl.program,'u_kernel')
const u_kernelWeightLoc=gl.getUniformLocation(gl.program,'u_kernelWeight')
gl.uniform1fv(u_kernelLoc,edgeDetect)
gl.uniform1f(u_kernelWeightLoc,computeKernelWeight(edgeDetect))

 function computeKernelWeight(kernel) {
    var weight = kernel.reduce(function(prev, curr) {
        return prev + curr;
    });
    return weight <= 0 ? 1 : weight;
}

得到结果如下,从图片可以看出实现了边缘检测的效果:

实现不同的图片处理效果,只需要修改数组即可,而这个数组就成为卷积核 ,实现这种图像处理的方法就是卷积 。体验地址:WebGL - 2D image 3x3 convolution (webglfundamentals.org)

相关推荐
wearegogog1237 小时前
基于 MATLAB 的卡尔曼滤波器实现,用于消除噪声并估算信号
前端·算法·matlab
Drawing stars7 小时前
JAVA后端 前端 大模型应用 学习路线
java·前端·学习
品克缤7 小时前
Element UI MessageBox 增加第三个按钮(DOM Hack 方案)
前端·javascript·vue.js
小二·7 小时前
Python Web 开发进阶实战:性能压测与调优 —— Locust + Prometheus + Grafana 构建高并发可观测系统
前端·python·prometheus
小沐°7 小时前
vue-设置不同环境的打包和运行
前端·javascript·vue.js
qq_419854058 小时前
CSS动效
前端·javascript·css
烛阴8 小时前
3D字体TextGeometry
前端·webgl·three.js
桜吹雪9 小时前
markstream-vue实战踩坑笔记
前端
C_心欲无痕9 小时前
nginx - 实现域名跳转的几种方式
运维·前端·nginx
花哥码天下9 小时前
恢复网站console.log的脚本
前端·javascript·vue.js