一.简介
上一节记录了关于图像缩小时的一些处理方法,本节记录图像放大时的一些原理,并通过图像处理来理解像素插值的过程。
图像放大是发生在提供的图像小于实际绘制的像素,即一个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)