webgl错误中的高斯模糊

gaussian blur in webgl error

我正在尝试在 webgl 中编写一个简单的高斯模糊着色器。我对此进行了很多搜索,我想我理解它背后的想法。 鉴于此 3X3 内核。我如何将它应用到我的顶点着色器?

Here is my current output

[ 0.0625  0.125  0.0625 ]
[ 0.125   0.25   0.125  ]
[ 0.0625  0.125  0.0625 ]

特别是如何获取相邻像素?

这个逻辑有道理吗?

precision mediump float;
varying vec2 vUV;

uniform sampler2D uTexture;

void main(){
     gl_FragColor = texture2D(uTexture, vUV + vec2(????,????)*0.0625;


}

我应该在上面的 vec2() 中输入什么?说如果我想获得内核的左上角纹理值。 假设 vUv 是 (20,20) 得到 (19,19) 我应该写

     gl_FragColor = vec4(0.0);
     gl_FragColor += texture2D(uTexture, vUV  +  vec2(vUV.x-1.0,vUV.y-1.0))*0.0625;
     gl_FragColor += texture2D(uTexture, vUV + vec2(vUV.x,vUV.y-1.0))*0.125;    
     gl_FragColor += texture2D(uTexture, vUV + vec2(vUV.x+1.0,vUV.y-1.0))*0.0625;

     gl_FragColor += texture2D(uTexture, vUV  +  vec2(vUV.x-1.0,vUV.y))*0.125;
     gl_FragColor += texture2D(uTexture, vUV + vec2(vUV.x,vUV.y))*0.25; 
     gl_FragColor += texture2D(uTexture, vUV + vec2(vUV.x+1.0,vUV.y))*0.125;    


     gl_FragColor += texture2D(uTexture, vUV  +  vec2(vUV.x-1.0,vUV.y+1.0))*0.0625;
     gl_FragColor += texture2D(uTexture, vUV + vec2(vUV.x,vUV.y+1.0))*0.125;    
     gl_FragColor += texture2D(uTexture, vUV + vec2(vUV.x+1.0,vUV.y+1.0))*0.0625;   

texture2D用于查找纹理的纹理坐标必须在[0.0, 1.0]范围内。

另见

为了让你的着色器工作,你必须定义一个统一变量,它包含你用来创建模糊效果的纹素的偏移量:

uniform vec2 offs_blur;

将此偏移量用于 9 次纹理查找:

gl_FragColor = vec4(0.0);
gl_FragColor += texture2D(uTexture, vUV + vec2(-offs_blur.x, -offs_blur.y))*0.0625;
gl_FragColor += texture2D(uTexture, vUV + vec2(         0.0, -offs_blur.y))*0.125;  
gl_FragColor += texture2D(uTexture, vUV + vec2( offs_blur.x, -offs_blur.y))*0.0625;

gl_FragColor += texture2D(uTexture, vUV + vec2(-offs_blur.x,          0.0))*0.125;
gl_FragColor += texture2D(uTexture, vUV + vec2(         0.0,          0.0))*0.25;   
gl_FragColor += texture2D(uTexture, vUV + vec2( offs_blur.x,          0.0))*0.125;  


gl_FragColor += texture2D(uTexture, vUV + vec2(-offs_blur.x, offs_blur.y))*0.0625;
gl_FragColor += texture2D(uTexture, vUV + vec2(         0.0, offs_blur.y))*0.125;   
gl_FragColor += texture2D(uTexture, vUV + vec2( offs_blur.x, offs_blur.y))*0.0625;  

相邻纹素的偏移量是纹理大小的倒数(1/width, 1/height).
但是偏移量不需要是到相邻纹素的精确偏移量,以获得模糊效果。 如果增加偏移量,模糊效果会增加。当然,这会导致质量损失。随着偏移量的增加,伪像和条带效应将会增加。为避免这种情况,您必须增加查找的纹素数量(例如,查找 5x5 纹素)

像这样设置制服:

offs_blur = gl.getUniformLocation(program,"offs_blur");

var blur = 20.0;
gl.uniform2fv(offs_blur,[blur/image.width, blur/image.height]);

查看示例,该示例将答案中的建议应用到您问题的原始代码中:

var canvas = document.createElement('canvas')
 canvas.width = window.innerWidth
 canvas.height = window.innerHeight
 document.body.appendChild(canvas)
 

 var gl = canvas.getContext('webgl')
 //  clear canvas with any color you want
 gl.clearColor(0.75, 0.85, 0.8, 1.0)
 gl.clear(gl.COLOR_BUFFER_BIT)


 function main() {
   var image = new Image();
     image.crossOrigin = "anonymous";

   image.src = "https://i.imgur.com/GdkFHnw.jpg";
   image.onload = function() {
  render(image);
   }
 }

 // generic function to create shaders
 function createShader(gl, source, type){
  var shader = gl.createShader(type);
  gl.shaderSource(shader, source);
  gl.compileShader(shader);
    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
  console.error('ERROR compiling shader type=>'+ type, gl.getShaderInfoLog(shader));
  return;
   }
 return shader
 }


 function createBuffer(data) {
   data = data instanceof Float32Array ? data : new Float32Array(data);
   var buffer = gl.createBuffer();
   gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
   gl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW);
   return buffer;
 }


 // function that returns a `program` from compiled vertex & fragment shaders
 function createProgram(gl, vertexShader, fragmentShader) {
   var program = gl.createProgram();
   gl.attachShader(program, vertexShader);
   gl.attachShader(program, fragmentShader);
   gl.linkProgram(program);
   var success = gl.getProgramParameter(program, gl.LINK_STATUS);
   if (!success) {
    // something went wrong with the link
    throw ("program filed to link:" + gl.getProgramInfoLog (program));
   }
   return program;
 };

  var texture_width = 1, texture_height = 1; 

 // Creates a texture from an existing canvas or HTMLImage (new Image())
 // without needing width & height or with a typed array (Uint8Array) that has
 // a specified width & height
 // e.g.
 // createTexture(HTMLImageElement) will work just fine
 // createTexture(Uint8Array,width,height), remember that a texture needs four values for one pixel
 function createTexture(image,width,height) {
   
    var texture = gl.createTexture();
   
   // Set the active texture slot to 0
   // WebGL has ~30 texture slots, meaning you could have about 30 textures bound at once
   // Think of it as an array of 30 pointers to texture objects that you can set
   gl.activeTexture(gl.TEXTURE0); // Sets the current 'index'
   gl.bindTexture(gl.TEXTURE_2D,texture); // binds the selected texture object to the current pointer
   
   // How to filter the texture when it needs resizing when sampled
   // (Is it going to be blurred when streched?)
   // (gl.NEAREST means no blur)
   gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.NEAREST);
   gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.NEAREST);
   
   // What to do if UV coordinates go outside the texture's size
   // gl.CLAMP_TO_EDGE repeats the pixel at the texture's border.
   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);
   
   width === undefined && height === undefined ?
  gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,gl.RGBA,gl.UNSIGNED_BYTE,image):
  gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,width,height,0,gl.RGBA,gl.UNSIGNED_BYTE,image);

   return texture;
 }


 function render(image){
  //alert('rendering...')
 //vertex shader source

 const vertexShaderSource = [  
   'attribute vec2 aPosition;',
   'attribute vec2 aUV;',
   'varying vec2 vUV;',
   'void main(){',
   '  gl_Position = vec4(aPosition, 0.0, 1.0);',
   '  vUV = aUV;',
   '}',
 ].join("\n");


 //fragment shader source
 const fragShaderSource = `

   precision mediump float;
   varying vec2 vUV;
   uniform sampler2D uTexture;
   void main(){
   float brightness = 1.1;

   gl_FragColor = texture2D(uTexture, vUV);
   gl_FragColor.rgb *= brightness;

   }`


 const blurShader = `

 precision mediump float;
 varying vec2 vUV;

 uniform sampler2D uTexture;
  uniform vec2 offs_blur;

 //[ 0.0625  0.125  0.0625 ]
 //[ 0.125   0.25   0.125  ]
 //[ 0.0625  0.125  0.0625 ]
 void main(){
  
     gl_FragColor = vec4(0.0);
   gl_FragColor += texture2D(uTexture, vUV + vec2(-offs_blur.x, -offs_blur.y))*0.0625;
   gl_FragColor += texture2D(uTexture, vUV + vec2(         0.0, -offs_blur.y))*0.125; 
   gl_FragColor += texture2D(uTexture, vUV + vec2( offs_blur.x, -offs_blur.y))*0.0625;
   
   gl_FragColor += texture2D(uTexture, vUV + vec2(-offs_blur.x,          0.0))*0.125;
   gl_FragColor += texture2D(uTexture, vUV + vec2(         0.0,          0.0))*0.25; 
   gl_FragColor += texture2D(uTexture, vUV + vec2( offs_blur.x,          0.0))*0.125; 


   gl_FragColor += texture2D(uTexture, vUV + vec2(-offs_blur.x, offs_blur.y))*0.0625;
   gl_FragColor += texture2D(uTexture, vUV + vec2(         0.0, offs_blur.y))*0.125; 
   gl_FragColor += texture2D(uTexture, vUV + vec2( offs_blur.x, offs_blur.y))*0.0625; 
 }
 `
 // const fragShaderSource =  [
 //     'precision highp float;',
 //     'varying vec2 vUV;',
 //     'uniform sampler2D texture;',
 //     '',
 //     'void main(void) {',
 //     'vec4 c = texture2D(texture, vUV);',
 //     'gl_FragColor = vec4(1.0 - c.r, 1.0 - c.g, 1.0 - c.b, c.a);',
 //     '}'
 // ].join('\n');
 //create vertex shader
 var vertexShader = createShader(gl, vertexShaderSource, gl.VERTEX_SHADER);

 //create fragment shader
 var fragShader = createShader(gl, blurShader, gl.FRAGMENT_SHADER);

 //create program
 var program = createProgram(gl,vertexShader, fragShader);

 // get location of attributes & uniforms
 aPosition = gl.getAttribLocation(program,"aPosition");
 aUV = gl.getAttribLocation(program,"aUV");
 uTexture = gl.getUniformLocation(program,"uTexture");
  offs_blur = gl.getUniformLocation(program,"offs_blur");
  

 var buffer = createBuffer([
   // X  Y     U   V
    0.5, 0.5,  1.0,0.0,
   -0.5, 0.5,  0.0,0.0,
    0.5,-0.5,  1.0,1.0,
    
    0.5,-0.5,  1.0,1.0,
   -0.5, 0.5,  0.0,0.0,
   -0.5,-0.5,  0.0,1.0,
 ]);

  texture = createTexture(image);

  // Setup GL State
 gl.useProgram(program); 
 gl.uniform1i(uTexture,0);
  var blur = 20.0;
  gl.uniform2fv(offs_blur,[blur/image.width, blur/image.height]);

  gl.activeTexture(gl.TEXTURE0);
 gl.bindTexture(gl.TEXTURE_2D,texture);

 gl.bindBuffer(gl.ARRAY_BUFFER,buffer);

 // These functions tell WebGL how to interpret the vertexbuffer data

 // Every sixteen bytes, use the first eight (4 bytes is a float) for the 'aPosition' attribute
 gl.vertexAttribPointer(aPosition,2,gl.FLOAT,gl.FALSE,16,0);

 // Every sixteen bytes, use the last eight bytes for the 'aUV' attribute
 gl.vertexAttribPointer(aUV,2,gl.FLOAT,gl.FALSE,16,8);

 // These need to be enabled or the vertex data isn't fed indo the vertex shader
 gl.enableVertexAttribArray(aPosition);
 gl.enableVertexAttribArray(aUV);

  window.onresize = resize;
  resize();
  requestAnimationFrame(draw);
}

function draw(delteMS){

 gl.clearColor(0.5,0.5,0.5,1.0);
 gl.clear(gl.COLOR_BUFFER_BIT);
 gl.drawArrays(gl.TRIANGLES,0,6);

  requestAnimationFrame(draw);
}

function resize() {
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;
  gl.viewport( 0, 0, canvas.width, canvas.height );
}

main();