Table of Contents
这篇文字主要记录使用 WebGL 中遇到的问题和……其他琐碎的东西。
使用 WebGL 是为了解决图像处理等密集型任务原生 js 性能不足的问题。但 WebGL 好像也无法做到 16ms 内处理一张图片(1280 x 800),5×5 的高斯模糊卷积核大概需要 35ms(不是非常确定浏览器用的核显还是独显,并且整个时间包括)。
for 循环
在关于 WebGL 的介绍文章中,很少有提及循环的,以至于我在写卷积的时候,用 js 生成卷积核每一个像素的处理语句,这导致卷积核较大的时候,语句很长,然后报错——webgl ERROR: Expression too complex。
于是我就想,webgl 总得有循环吧,但网上搜到的几篇文章都没有提到循环,后来是以 glsl 作为关键字搜索到的。也是,webgl 是封装,具体的 glsl 语法还是要看 glsl 的。
int length = ${kernelHeight * kernelWidth}; for (int i = 0; i < length; i++) { int x = i % ${kernelWidth}; int y = i / ${kernelHeight}; colorSum += texture(u_image, v_texCoord + onePixel * vec2(x - ${halfWidth}, y - ${halfHeight})) * u_kernel[i]; }
${} 部分是 js 模板字符串里的表达式,不需要关心。
glsl 的 for 循环和 c 基本一样,和 js 也就关键字的差别。
WebGL 获取图片数据
gl.readPixels() 函数会返回纹理的像素数据。
const results = new ImageData(imgData.width, imgData.height) gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, results.data)
但是,这里获取的图片数据是上下颠倒的,因为 gl 默认原点在左下角。为了得到正确的图片数据,开发者需要自行处理数据。
function flipImageY (source: ImageData) { const half = source.height >> 1 let temp = new Uint8ClampedArray(0) for (let y = 0; y < half; y++) { temp = source.data.slice(y * source.width * 4, (y + 1) * source.width * 4) source.data.set( source.data.slice( (source.height - y - 1) * source.width * 4, (source.height - y) * source.width * 4 ), y * source.width * 4 ) source.data.set(temp, (source.height - y - 1) * source.width * 4) } }
不能直接获取正确的数据吗?嗯……反正我没找到。