본문 바로가기

WebGL

WebGL Fundamentals > WebGL Image Processing

https://webglfundamentals.org/webgl/lessons/webgl-image-processing.html

 

WebGL Image Processing

How to image process in WebGL

webglfundamentals.org

https://github.com/Myoungmin/WebGL_Fundamentals

 

GitHub - Myoungmin/WebGL_Fundamentals: WebGL 학습 프로젝트

WebGL 학습 프로젝트. Contribute to Myoungmin/WebGL_Fundamentals development by creating an account on GitHub.

github.com

 

 

WebGL Image Processing

WebGL에서 이미지를 그리기 위해서는 텍스처를 사용해야 한다.

렌더링할 때 WebGL이 픽셀 대신 클립 공간 좌표를 유추하는 것과 마찬가지로, 

텍스처를 읽을 때 WebGL은 텍스처 좌표를 유추한다.

텍스처 좌표는 텍스처 크기에 상관없이 0.0에서 1.0사이가 된다.

 

단 하나의 사각형(정확히는 2개의 삼각형)만 그리기 때문에 사각형의 각 점이 텍스처의 어느 위치에 해당하는지 WebGL에 알려줘야 한다.
'varying'이라고 불리는 특수 변수를 이용해 이 정보를 정점 셰이더에서 프래그먼트 셰이더로 전달해야 한다.

 

정점 셰이더에서 텍스처 좌표 전달을 위한 attribute를 추가한 다음, varying을 이용하여 프래그먼트 셰이더로 전달한다.

//텍스처 좌표 전달을 위한 attribute
attribute vec2 a_texCoord;
//...
varying vec2 v_texCoord;
 
void main() {
  //...
  // 프래그먼트 셰이더로 텍스처 좌표 전달
  // GPU는 점들 사이의 값을 보간
  v_texCoord = a_texCoord;
}

 

정점 셰이더에서 넘어오는 varying인 텍스처 좌표를 통해, 텍스처의 색상을 찾을 수 있도록 프래그먼트 셰이더를 작성한다.

<script id="fragment-shader-2d" type="x-shader/x-fragment">
precision mediump float;
 
// 텍스처
uniform sampler2D u_image;
 
// 정점 셰이더에서 전달된 텍스처 좌표
varying vec2 v_texCoord;
 
void main() {
  // 텍스처의 색상 탐색
  gl_FragColor = texture2D(u_image, v_texCoord);
}
</script>

 

이미지를 불러오고, 텍스처를 생성한 다음, 이미지를 텍스처로 복사한다.

브라우저에서 이미지를 비동기적으로 불러오기 때문에 텍스처 로딩을 기다리도록 코드를 구성해야 한다.

onload 이벤트에 의해 이미지를 불러오자마자 render가 실행된다.

 

function main() {
  var image = new Image();
  image.src = "http://someimage/on/our/server";  // 같은 도메인이여야 합니다!!!
  image.onload = function() {
    render(image);
  }
}
 
function render(image) {
  //...
  // 이전에 작성한 모든 코드
  //...
  // 텍스처 좌표가 필요한 곳을 탐색
  var texCoordLocation = gl.getAttribLocation(program, "a_texCoord");
 
  // 사각형의 텍스처 좌표 제공
  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);
 
  // 텍스처 생성
  var texture = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, texture);
 
  // 어떤 크기의 이미지도 렌더링할 수 있도록 매개변수 설정
  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);
  //...
}

 

컨볼루션 커널을 이용해서 일반적인 이미지 처리

컨볼루션 커널을 프래그먼트 셰이더에서 수행하므로 새로운 프래그먼트 셰이더를 작성한다.

컨볼루션 커널은 행렬의 각 항목이 렌더링하는 픽셀 주변에 있는 8개의 픽셀에 얼마나 곱할지 나타내는 3x3 행렬이다.

 

그런 다음 결과를 커널의 가중치(커널에 있는 모든 값의 합) 또는 1.0 중에 더 큰 값으로 나눈다.

 

<script id="fragment-shader-2d" type="x-shader/x-fragment">
precision mediump float;
 
// 텍스처
uniform sampler2D u_image;
uniform vec2 u_textureSize;
uniform float u_kernel[9];
uniform float u_kernelWeight;
 
// 정점 셰이더에서 전달된 텍스처 좌표
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] ;
 
  // 합계를 가중치로 나누지만 rgb만을 사용
  // 알파는 1.0으로 설정
  gl_FragColor = vec4((colorSum / u_kernelWeight).rgb, 1.0);
}
</script>

 

자바스크립트에서 컨볼루션 커널인 3x3 행렬과 가중치를 fragment shader에 제공해 준다.

아래는 윤곽선 검출을 위한 컨볼루션 커널을 적용한다.

 

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

 

 

 

https://myoungmin.github.io/WebGL_Fundamentals/

 

WebGL_Fundamentals

WebGL이란? WebGL은 Web Graphics Library의 약자로 웹상에서 2D 및 3D 그래픽을 렌더링하기 위한 로우 레벨 Javascript API.

myoungmin.github.io