webgl 圖像處理2---圖像像素處理


webgl 圖像處理

webgl 不僅僅可以用來進行圖形可視化, 它還能進行圖像處理

圖像處理2---圖像傳輸

之前已經進行了點和 uv 數據的傳輸

webgl 進行圖形處理的第二步: 傳輸圖片到 GPU

下圖為傳輸圖片並進行相應渲染的結果

image-20210919224155536

對圖像進行模糊處理, 並轉換為數組輸出

image-20210919224314334

處理過程詳解

  1. 加載圖片

    1. 由於加載圖片是異步方法, 因此很多內容都需要寫在加載圖片的回調函數內
    2. 在回調函數中進行傳輸圖片操作
  2. 傳輸圖片到 GPU

    1. 之前傳輸數據的步驟
      1. 創建緩存區
      2. 綁定緩沖區
      3. 向緩存區中寫入內容
      4. 綁定 shader 中的變量
      5. 開始傳輸數據
    2. 現在傳輸圖像的步驟, 類似
      1. 創建材質 Texture ( 對應前面第 1 步 )
      2. y 軸反轉, 這是由於瀏覽器 y 軸向下, 需要矯正
      3. 激活紋理單元 ( 簡單理解為與紋理綁定的 內容, 一個紋理綁定一個紋理單元 )
      4. 綁定 texture ( 對應前面第 2 步)
      5. 配置圖片信息, 兩種, 一種是縮放相關參數, 用來將圖片的寬高置為 2 的冪次方, 一種是圖片內容 ( 對應前面第 3 步)
      6. 傳輸圖片 ( 對應前面第 4,5 步)
  3. 查詢當前像素的上下左右的顏色值並進行平均

    1. 當前節點的 uv 為 vUv, 是一個二維向量, 范圍從 0-1
    2. 由於圖片設置為 200 * 200, 因此可以將 vUv 轉換為具體的像素點位置, floor(vUv * 200.0)/200.0
    3. 計算周邊點的位置及像素值
      1. 例如該像素左邊點位置為, floor(vUv * 200.0 + vec2(-1.0, 0.0)) / 200.0
      2. 像素值為, texture2D(u_Texture, floor(vUv * 200.0 + vec2(-1.0, 0.0)) / 200.0)
  4. 輸出圖像到數組中

    // 將圖片數據加載到 pixels 數組中
    const pixels = new Uint8Array(200 * 200 *4);
    gl.readPixels(0, 0, 200, 200, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
    console.log(pixels);
    

    最后去得到一個 arrayBuffer 數組

下一階段

當前階段對圖片進行像素顆粒的控制, 利用這個思路能實現大部分對圖片的操作

下個階段是輸入一個數組, 在 GPU 中對數組進行計算, 最后得到相應的數值, 加速計算, 充分利用 GPU 並行計算的能力

代碼實現

import Img from "./img/img1.jpg";

// 兩種着色器
const VSHADER_SOURCE = `
  attribute vec4 a_Position;
  attribute vec2 uv;
  varying vec2 vUv;
  void main(){
    // 進行插值計算
    vUv = uv;
    gl_Position = a_Position;
  }
`;

const FSHADER_SOURCE = `
  // 片元着色器中一定要聲明精度
  precision mediump float;
  varying vec2 vUv;
  uniform sampler2D u_Texture;

  // 求平均
  vec4 calculate(vec4 color, vec2 vUv){
    vec4 tempColor = color;
    if(vUv.x != 0.0 && vUv.y != 0.0){
      vec4 left = texture2D(u_Texture, floor(vUv * 200.0 + vec2(-1.0, 0.0)) / 200.0);
      vec4 right = texture2D(u_Texture, floor(vUv * 200.0 + vec2(1.0, 0.0)) / 200.0);
      vec4 top = texture2D(u_Texture, floor(vUv * 200.0 + vec2(0.0, 1.0)) / 200.0);
      vec4 bottom = texture2D(u_Texture, floor(vUv * 200.0 + vec2(0.0, -1.0)) / 200.0);
      // tempColor.rg = 1.0 * (left.rg + right.rg + top.rg + tempColor.rg + bottom.rg) / 5.0;
      tempColor = 1.0 * (left + right + top + tempColor + bottom) / 5.0;
    }

    return tempColor;
  }

  void main(){
    vec4 color = texture2D(u_Texture, vUv);

    color = calculate(color, vUv);

    gl_FragColor = color;
  }
`;

init();

function init() {
  const canvas = document.createElement("canvas");
  canvas.width = 200;
  canvas.height = 200;
  document.body.appendChild(canvas);

  // 獲取 gl 環境
  const gl = canvas.getContext("webgl");
  if (!gl) {
    console.log("Fail to init content");
    return;
  }

  // webgl 程序
  const programe = gl.createProgram();

  // 初始化着色器
  initShader(gl, VSHADER_SOURCE, FSHADER_SOURCE, programe);

  // 發送數據
  sendData("a_Position", 2, [-1, 1, -1, -1, 1, -1, 1, 1], gl, programe);

  sendData("uv", 2, [0, 1, 0, 0, 1, 0, 1, 1], gl, programe);

  // 加載圖片
  loadImage(gl, programe);

}

// 初始化着色器
function initShader(gl, VSHADER_SOURCE, FSHADER_SOURCE, programe) {
  // 創建 shader
  const vertexShader = gl.createShader(gl.VERTEX_SHADER);
  // 綁定資源
  gl.shaderSource(vertexShader, VSHADER_SOURCE);
  // 編譯着色器
  gl.compileShader(vertexShader);
  const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER, FSHADER_SOURCE);
  gl.shaderSource(fragmentShader, FSHADER_SOURCE);
  gl.compileShader(fragmentShader);

  // 常規流程
  gl.attachShader(programe, vertexShader);
  gl.attachShader(programe, fragmentShader);
  gl.linkProgram(programe);
  gl.useProgram(programe);
}

// 發送數據到 GPU
function sendData(name, size, arr, gl, programe) {
  // 獲取地址空間
  const variate = gl.getAttribLocation(programe, name);
  if (variate < 0) {
    console.log(`Failed to get the location of ${name}`);
    return;
  }
  const variates = new Float32Array(arr);
  // 1. 創建緩存區
  const buffer = gl.createBuffer();
  if (!buffer) {
    console.log("Failed to create buffer");
  }
  // 2. 綁定緩存區
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  // 3. 向緩沖區中添加數據
  gl.bufferData(gl.ARRAY_BUFFER, variates, gl.STATIC_DRAW);
  // 4. 將緩沖區與 glsl 中變量綁定
  gl.vertexAttribPointer(variate, size, gl.FLOAT, false, 0, 0);
  // 5. 開始傳輸
  gl.enableVertexAttribArray(variate);
}

function loadImage(gl, programe){
  // 初始化 Image
  const image = new Image();
  // 通過 loader 加載圖像路徑
  image.src = Img;

  // 設置回調函數
  image.onload = ()=>{
    const texture = gl.createTexture();
    // y 軸反轉
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
    // 激活 0 號紋理單元
    gl.activeTexture(gl.TEXTURE0);
    // 綁定 texture
    gl.bindTexture(gl.TEXTURE_2D, texture);
    // 圖像處理, 一定要有, 用來將圖片寬高擴展到 2 的冪
    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);

    // 配置圖片
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); // 配置紋理圖像
    // 傳輸圖片
    const u_Texture = gl.getUniformLocation(programe, "u_Texure");
    gl.uniform1i(u_Texture, 0);

    // 刷新顏色
    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    // 清除
    gl.clear(gl.COLOR_BUFFER_BIT);
    // 畫圖形
    gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);

    // 將圖片數據加載到 pixels 數組中
    const pixels = new Uint8Array(200 * 200 *4);
    gl.readPixels(0, 0, 200, 200, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
    console.log(pixels);
  }

}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM