鍍金池/ 教程/ HTML/ WebGL 圖像處理
WebGL 文本 HTML
WebGL 文本 Canvas 2D
WebGL 2D 圖像旋轉(zhuǎn)
WebGL 圖像處理(續(xù))
WebGL 2D 矩陣
WebGL 繪制多個東西
WebGL 圖像處理
WebGL 2D 圖像轉(zhuǎn)換
WebGL 3D 透視
WebGL 是如何工作的
WebGL 文本 紋理
WebGL 2D 圖像伸縮
WebGL 場景圖
WebGL 3D 攝像機
WebGL 文本 使用字符紋理
WebGL 正交 3D
WebGL 基本原理
WebGL - 更少的代碼,更多的樂趣
WebGL 著色器和 GLSL

WebGL 圖像處理

在 WebGL 中圖像處理是很簡單的,多么簡單?

為了在 WebGL 中繪制圖像,我們需要使用紋理。類似于當渲染代替像素時,WebGL 會需要操作投影矩陣的坐標,WebGL 讀取紋理時需要獲取紋理坐標。紋理坐標范圍是從 0.0 到 1.0。

因為我們僅需要繪制由兩個三角形組成的矩形,我們需要告訴 WebGL 在矩陣中紋理對應的那個點。我們可以使用特殊的被稱為多變變量,會將這些信息從頂點著色器傳遞到片段著色器。WebGL 將會插入這些值,這些值會在頂點著色器中,當對每個像素繪制時均會調(diào)用片段著色器。

我們需要在紋理坐標傳遞過程中添加更多的信息,然后將他們傳遞到片段著色器中。

attribute vec2 a_texCoord;
...
varying vec2 v_texCoord;

void main() {
   ...
   // pass the texCoord to the fragment shader
   // The GPU will interpolate this value between points
   v_texCoord = a_texCoord;
}

然后,我們提供一個片段著色器來查找顏色紋理。

<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;

// our texture
uniform sampler2D u_image;

// the texCoords passed in from the vertex shader.
varying vec2 v_texCoord;

void main() {
   // Look up a color from the texture.
   gl_FragColor = texture2D(u_image, v_texCoord);
}
</script>

最后,我們需要加載一個圖片,然后創(chuàng)建一個問題,將該圖片傳遞到紋理里面。因為,是在瀏覽器里面顯示,所以圖片是異步加載,所以我們安置我們的代碼來等待紋理的加載。一旦,加載完成就可以繪制。

function main() {
  var image = new Image();
  image.src = "http://someimage/on/our/server";  // MUST BE SAME DOMAIN!!!
  image.onload = function() {
render(image);
  }
}

function render(image) {
  ...
  // all the code we had before.
  ...
  // look up where the texture coordinates need to go.
  var texCoordLocation = gl.getAttribLocation(program, "a_texCoord");

  // provide texture coordinates for the rectangle.
  var texCoordBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
  0.0,  0.0,
  1.0,  0.0,
  0.0,  1.0,
  0.0,  1.0,
  1.0,  0.0,
  1.0,  1.0]), gl.STATIC_DRAW);
  gl.enableVertexAttribArray(texCoordLocation);
  gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);

  // Create a texture.
  var texture = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, texture);

  // Set the parameters so we can render any size image.
  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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

  // Upload the image into the texture.
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
  ...
}

如下是 WebGL 渲染出來的圖像。

http://wiki.jikexueyuan.com/project/webgl/images/G6OfQqJ.png" alt="" />

下面我們對這個圖片進行一些操作,來交換圖片中的紅色和藍色。

...
gl_FragColor = texture2D(u_image, v_texCoord).bgra;
...

現(xiàn)在紅色和藍色已經(jīng)被交換了。效果如下:

http://wiki.jikexueyuan.com/project/webgl/images/jm636Vk.png" alt="" />

假如我們想做一些圖像處理,那么我們可以看一下其他像素。自從 WebGL 引用紋理的紋理坐標從 0.0 到 1.0 。然后,我們可以計算移動的多少個像素 onePixel = 1.0 / textureSize。

這里有個片段著色器來平均紋理中每個像素的左側和右側的像素。

<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;

// our texture
uniform sampler2D u_image;
uniform vec2 u_textureSize;

// the texCoords passed in from the vertex shader.
varying vec2 v_texCoord;

void main() {
   // compute 1 pixel in texture coordinates.
   vec2 onePixel = vec2(1.0, 1.0) / u_textureSize;

   // average the left, middle, and right pixels.
   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>

然后,我們需要通過 JavaScript 傳遞出紋理的大小。

...
var textureSizeLocation = gl.getUniformLocation(program, "u_textureSize");
...
// set the size of the image
gl.uniform2f(textureSizeLocation, image.width, image.height);
...

比較上述兩個圖片

http://wiki.jikexueyuan.com/project/webgl/images/20X0ox5.png" alt="" />

現(xiàn)在,我們知道如何讓使用像素卷積內(nèi)核做一些常見的圖像處理。這里,我們會使用 3x3 的內(nèi)核。卷積內(nèi)核就是一個 3x3 的矩陣,矩陣中的每個條目代表有多少像素渲染。然后,我們將這個結果除以內(nèi)核的權重或 1.0.這里是一個非常好的參考文章這里有另一篇文章顯示出一些實際代碼,它是使用 C++ 寫的

在我們的例子中我們要在著色器中做這樣工作,這里是一個新的片段著色器。

<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;

// our texture
uniform sampler2D u_image;
uniform vec2 u_textureSize;
uniform float u_kernel[9];
uniform float u_kernelWeight;

// the texCoords passed in from the vertex shader.
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] ;

   // Divide the sum by the weight but just use rgb
   // we'll set alpha to 1.0
   gl_FragColor = vec4((colorSum / u_kernelWeight).rgb, 1.0);
}
</script>

在 JavaScript 中,我們需要提供一個卷積內(nèi)核和它的權重。

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

 ...
 var kernelLocation = gl.getUniformLocation(program, "u_kernel[0]");
 var kernelWeightLocation = gl.getUniformLocation(program, "u_kernelWeight");
 ...
 var edgeDetectKernel = [
 -1, -1, -1,
 -1,  8, -1,
 -1, -1, -1
 ];
 gl.uniform1fv(kernelLocation, edgeDetectKernel);
 gl.uniform1f(kernelWeightLocation, computeKernelWeight(edgeDetectKernel));
 ...

我們在列表框內(nèi)選擇不同的內(nèi)核。

我們希望通過這篇文章講解,能夠讓你覺得使用 WebGL 做圖像處理很簡單。下面,我們將講解如何在一個圖像上應用更多的效果。