webgl 影像處理 加速計算

隨遇丿而安 發表於 2021-09-17
WebGL

webgl 影像處理

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

影像處理1---資料傳輸

webgl 進行圖形處理的第一步: 傳輸資料到 GPU

下圖為傳輸點資料到 GPU 並進行相應渲染的結果

image-20210917225715565

資料傳輸過程

  1. 建立 canvas 元素, 用來承接 GPU 生成的資料
  2. 獲取 context, program 用於運算元據和使用相應 API
  3. 初始化著色器, 將寫的著色器編譯進 program 總
  4. 傳送資料, 將頂點資料, uv 資料, 等等資料, 均可以通過 sendData 方法將資料傳輸到 glsl 中的變數上
    1. 建立緩衝區
    2. 繫結緩衝區
    3. 向緩衝區中新增資料
    4. 將資料與 glsl 中的變數繫結
    5. 傳輸資料
    6. 所有傳輸資料的流程與此基本類似
  5. 清除之前的顏色, 清除顏色緩衝區, 畫出自己想要的圖形

下一階段

當前階段實現了將基本資料傳輸給 GPU

下一步是將 影像資料 傳輸到 GPU, GPU 接收到影像資訊後獲取每個畫素點的顏色值, 通過卷積重置畫素, 初步實現 webgl 的圖形處理功能

程式碼實現

// 兩種著色器
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;
  void main(){
    gl_FragColor = vec4(vUv.x, vUv.y, 0.6, 1.0);
  }
`;

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);

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

// 初始化著色器
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);
}