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)

相关推荐
多多米100544 分钟前
初学Vue(2)
前端·javascript·vue.js
柏箱1 小时前
PHP基本语法总结
开发语言·前端·html·php
新缸中之脑1 小时前
Llama 3.2 安卓手机安装教程
前端·人工智能·算法
hmz8561 小时前
最新网课搜题答案查询小程序源码/题库多接口微信小程序源码+自带流量主
前端·微信小程序·小程序
看到请催我学习1 小时前
内存缓存和硬盘缓存
开发语言·前端·javascript·vue.js·缓存·ecmascript
blaizeer2 小时前
深入理解 CSS 浮动(Float):详尽指南
前端·css
编程老船长2 小时前
网页设计基础 第一讲:软件分类介绍、工具选择与课程概览
前端
编程老船长2 小时前
网页设计基础 第二讲:安装与配置 VSCode 开发工具,创建第一个 HTML 页面
前端
速盾cdn2 小时前
速盾:网页游戏部署高防服务器有什么优势?
服务器·前端·web安全
小白求学12 小时前
CSS浮动
前端·css·css3