【零基础学WebGL】FrameBuffer blend失真问题

本文探讨了WebGL中使用framebuffer和blend功能对绘制效果的影响。通过示例展示了开启blend后,如何进行多层混合计算,以及framebuffer如何作为额外图层参与混合,导致颜色变化。为避免framebuffer带来的额外混合计算导致内容失真,建议在将framebuffer内容绘制到canvas时关闭blend功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文主旨是探究使用framebuffer特性,对blend功能有何影响。

WebGL Blend

回顾前文,可以通过gl.blend开启WebGL混合功能。

本文再通过绘制一个半透明的蓝色矩形,进一步开启gl.blend之后,浏览器是如何一步步合成最终内容的。

gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);

const vertexData = [-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0];
setAttribute(program, gl, vertexData, "a_position");
const v_color = gl.getUniformLocation(program, 'v_color');
gl.uniform4f(v_color, 0,0,1,0.5);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); 

可以发现,最终显示的颜色值是(0,0,31)。整个混合过程,涉及三个图层(自下而上):浏览器页面(黑色)、canvas(黑色)、矩形(具有透明度的蓝色)。

混合过程的顺序是,webgl内容和canvas混合,然后再与浏览器内容混合。

1、蓝色矩形和canvas进行混合,计算过程遵循gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA):

(0,0,1,0.5) * 0.5 + (0,0,0,0) * 0.5 = (0,0,0.5,0.25) 

2、webgl混合内容与浏览器内容混合,这个计算过程由浏览器决定,不可改变。计算公式如gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)相同。

(0,0,0.5,0.25) * 0.25 + (0,0,0,0) * 0.75 = (0,0,0.125,0.0625) = (0,0,32,0.0625) 

FrameBuffer Texture

FrameBuffer的作用是存储WebGL绘制的内容,Canvas WebGL上下文默认会创建一个FrameBuffer对象,应用可以使用createFramebuffer创建新的FrameBuffer,然后使用bindFramebuffer替换默认的FrameBuffer。

// 创建和绑定framebuffer
const framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer); 

FrameBuffer,本质上是一个附件集合,附件的类型可以是WebGLTexture 或 WebGLRenderbuffer 。WebGL提供方法framebufferTexture2D用来绑定texture附件,framebufferRenderbuffer用来绑定renderBuffer附件。

// 绑定texture附件。
const framebufferTexture = gl.createTexture();
gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, this.gl.TEXTURE_2D, framebufferTexture , 0); 

内容绘制到自定义frameBuffer之后,我们可以像普通texture一样使用framebuffer texture。把framebuffer texture绘制到默认的framebuffer即可。

gl.bindFramebuffer(gl.FRAMEBUFFER, null); // 切换回默认的framebuffer

gl.bindTexture(gl.TEXTURE_2D, framebufferTexture);// 绘制framebuffer纹理
const uTexture = gl.getUniformLocation(program, 'u_texture');
gl.uniform1i(uTexture, 0);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); 

总结一下framebuffer texture使用过程如下图

FrameBuffer Blend

回到文章开始,使用framebuffer绘制一个半透明的蓝色矩形,看看有什么变化。

 // 绑定自定义framebuffer
  const framebuffer = gl.createFramebuffer();
  gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
  const framebufferTexture = createTextureImage(gl, null, gl.canvas.width, gl.canvas.height);
  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, framebufferTexture, 0);
  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

  // 开启blend
  gl.enable(gl.BLEND);
  gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);

  // 绘制矩形
  const vertexData = [-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0];
  setAttribute(program, gl, vertexData, "a_position");
  const v_color = gl.getUniformLocation(program, 'v_color');
  gl.uniform4f(v_color, ...color);
  gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

  // 切换回默认framebuffer
  gl.bindFramebuffer(gl.FRAMEBUFFER, null);

  // 绘制framebuffer纹理
  gl.bindTexture(gl.TEXTURE_2D, framebufferTexture);
  const uTexture = gl.getUniformLocation(program, 'u_texture');
  gl.uniform1i(uTexture, 0);
  gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); 

最终绘制的内容如下图,颜色值变成了(0,0,2)。

framebuffer texture作为一个新的图层,使得整个混合过程最终涉及四个图层(自下而上):浏览器页面(黑色)、canvas(黑色)、framebuffer texture(黑色)、矩形(具有透明度的蓝色)。

混合过程的顺序是,webgl内容和framebuffer texture混合,然后再与canvas混合,最后再与浏览器内容混合。

1、蓝色矩形和framebuffer texture进行混合,计算公式遵循gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA):

(0,0,1,0.5) * 0.5 + (0,0,0,0) * 0.5 = (0,0,0.5,0.25) 

2、framebuffer texture混合内容与canvas进行混合。默认,计算公式和第一步一致,开发者可以选择设置不同的计算公式。我们这里并未做额外设置,所以和第一步的计算过程一样,同样遵循gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA):

(0,0,0.5,0.25) * 0.25 + (0,0,0,0) * 0.75 = (0,0,0.125,0.0625) 

3、canvas混合内容与浏览器内容进行混合,计算公式由浏览器决定,不可改变。公式如gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)相同。

(0,0,0.125,0.0625) * 0.0625 + (0,0,0,0) * 0.9375 = (0,0,0.0078125,0.004) = (0,0,2) 

因此,为了避免framebuffer texture带来的额外混合计算,导致绘制内容失真。应该在framebuffer texture绘制到canvs时,关闭blend功能。

 gl.bindFramebuffer(gl.FRAMEBUFFER, null);
 gl.disable(gl.BLEND); 

完整代码见:framebuffer texture blend

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值