webgl错误中的高斯模糊

时间:2018-06-28 17:07:35

标签: glsl webgl fragment-shader gaussianblur

我正在尝试在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;   

1 个答案:

答案 0 :(得分:5)

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

另请参见

要使着色器正常工作,必须定义一个统一变量,该变量包含用于创建模糊效果的texel的偏移量:

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