webgl错误中的高斯模糊
gaussian blur in webgl error
我正在尝试在 webgl 中编写一个简单的高斯模糊着色器。我对此进行了很多搜索,我想我理解它背后的想法。
鉴于此 3X3 内核。我如何将它应用到我的顶点着色器?
[ 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]范围内。
另见
- How do opengl texture coordinates work?
- WebGL Fundamentals, WebGL 3D - Textures.
为了让你的着色器工作,你必须定义一个统一变量,它包含你用来创建模糊效果的纹素的偏移量:
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();
我正在尝试在 webgl 中编写一个简单的高斯模糊着色器。我对此进行了很多搜索,我想我理解它背后的想法。 鉴于此 3X3 内核。我如何将它应用到我的顶点着色器?
[ 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]范围内。
另见
- How do opengl texture coordinates work?
- WebGL Fundamentals, WebGL 3D - Textures.
为了让你的着色器工作,你必须定义一个统一变量,它包含你用来创建模糊效果的纹素的偏移量:
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();