Web 技术研究所

我一直坚信着,Web 将会成为未来应用程序的主流

WebGL(拾壹) 帧缓冲的基本概念

  一般情况下,在我们执行绘制时会直接把绘制的数据渲染到屏幕上。但是有时候我们并不需要直接绘制到屏幕上,而是要先在后台绘制。这时候我们就需要创建个用于后台绘图的对象,这就是帧缓冲了。这篇文章就来说说帧缓冲的基本概念和工作流程。
  打个比方吧。现在你需要做一场绘画表演,一群人在围观着。你要是直接就开始表演了,要是画不好什么的就一定很丢人。所以在表演之前自己需要做个草稿。但是表演用画板是固定在舞台上的,而且上面只有一张纸可以用,你当然不可能到舞台上打草稿了。这时候你就会自己背个画板在幕后打草稿。这个画板就是帧缓冲,这就是帧缓冲的基本概念。
  可是把这个概念用在WebGL中又有问题了,在自己的帧缓冲上绘制的数据要如何使用呢?我们已经在自己的画板上画图了,既然是画图,那么画的就一定是图。也就是说,我们在自己的帧缓冲中绘制之后得到的是图。而WebGL中要把现有的图绘制出来就只有一个方法,那就是贴图,也就是使用texture。所以,我们可以先创建帧缓冲,然后在里面绘制东西,最后再把这个绘制的东西当作贴图数据再绘制到屏幕上。这就是使用帧缓冲的工作流程。下面,咱来看看代码
var fb,rb,texture;
//创建帧缓冲,并设置为当前工作的帧缓冲
fb=webgl.createFramebuffer();
webgl.bindFramebuffer(webgl.FRAMEBUFFER,fb);
//创建渲染缓冲,并设置渲染缓冲为当前操作渲染缓冲
rb=webgl.createRenderbuffer();
webgl.bindRenderbuffer(webgl.RENDERBUFFER,rb);
//设置渲染缓冲区的存储尺寸
webgl.renderbufferStorage(
  webgl.RENDERBUFFER,webgl.DEPTH_COMPONENT16,256,256
);
//把渲染缓冲绑定到当前工作的帧缓冲上
webgl.framebufferRenderbuffer(
  webgl.FRAMEBUFFER,webgl.DEPTH_ATTACHMENT,webgl.RENDERBUFFER,rb
);
//渲染缓冲的参数设置完毕,把它从当前操作对象中取消(这步可以不用)
webgl.bindRenderbuffer(webgl.RENDERBUFFER,null);
//创建贴图对象,并设置为当前贴图对象
texture=webgl.createTexture();
webgl.activeTexture(webgl.TEXTURE0);
webgl.bindTexture(webgl.TEXTURE_2D,texture);
//把贴图对象也绑定到帧缓冲中
webgl.framebufferTexture2D(
  webgl.FRAMEBUFFER,webgl.COLOR_ATTACHMENT0,
  webgl.TEXTURE_2D,texture,0
);
//把贴图对象的图片数据指针注销(交给帧缓冲管理)
webgl.texImage2D(
  webgl.TEXTURE_2D,0,webgl.RGB,256,256,0,
  webgl.RGB,webgl.UNSIGNED_BYTE,null
);
//设置贴图对象的属性
webgl.texParameteri(
  webgl.TEXTURE_2D,webgl.TEXTURE_MIN_FILTER,webgl.LINEAR
);
  代码的注释已经把该说的都说了,关键是要搞清楚它的工作流程。为什么这里又冒出个渲染缓冲呢?这货我也查了好多资料才了解的。我们之前说帧缓冲是画板,要在画板上画图当然还需要纸张,渲染缓冲就相当于纸张。实际的图像数据是保存在渲染缓冲中的,帧缓冲只是管理相关对象的而已,它本身并不是一个物理上的缓冲区。帧缓冲在创建时也有自带的一些必要相关对象,但是我们无法对其修改参数,所以需要创建自己帧缓冲相关对象。
  这段代码中我们自己创建的帧缓冲的相关对象就两个,渲染缓冲对象和贴图对象。代码中以“framebuffer”开头的方法有两个吧?“framebufferRenderbuffer”和“framebufferTexture2D”。以这个单词开头的方法就是给当前工作的帧缓冲设置相关对象。我们的帧缓冲需要存储绘图数据,并设置其属性,所以我们给它添加上一个渲染缓冲对象。之后还需要使用这个帧缓冲上的数据作为贴图,所以我们又给他设置了一个贴图对象。这就是上面这个代码的工作流程。
  “renderbufferStorage”是设置当前工作的渲染缓冲的存储大小。接触了WebGL这么久,基本上对它的命名套路都熟悉了吧,看到一个方法基本上都能猜到它的功能了,只是参数比较麻烦而已。这个方法的前两个参数基本都是固定搭配的,虽然有其他值可以用,但是那是OpenGL的,在WebGL上没有相应的兼容,所以目前不用纠结它。后面两个参数是缓冲区尺寸,虽然可以随便设置,但是要记住我们在缓冲区绘图后是要作为贴图数据使用的。贴图数据的尺寸就必须是2的n次方,所以不要设置奇葩的数值。
  “texImage2D”这个方法之前做贴图时也有使用过,不过这回它的参数和之前不同。OpenGL的设计是函数式的,有些地方很不人性化。这个函数的参数列表就太复杂了,而且移植到WebGL上还有很多多载,有时候不看文档实在写是难以写出程序。其它参数我就不在这里说了,这里就说最后一个参数。它设置为null时就是把数据指针交给它所属的帧缓冲来管理,也就是让贴图使用帧缓冲中的数据。
  其他方法我就不逐个说明了,我想告诉大家的主要是帧缓冲的工作流程和代码逻辑。至于具体的方法参数,这些东西是死的,完全可以在网络上找到。下面再来看看绘制时的代码
//绘制到帧缓冲
webgl.uniform1i(tp,false);
webgl.bindFramebuffer(webgl.FRAMEBUFFER,fb);
webgl.clear(webgl.COLOR_BUFFER_BIT|webgl.DEPTH_BUFFER_BIT);
webgl.drawElements(webgl.TRIANGLES,36,webgl.UNSIGNED_SHORT,0);
//绘制到屏幕
webgl.uniform1i(tp,true);
webgl.bindFramebuffer(webgl.FRAMEBUFFER,null);
webgl.clear(webgl.COLOR_BUFFER_BIT|webgl.DEPTH_BUFFER_BIT);
webgl.drawElements(webgl.TRIANGLES,36,webgl.UNSIGNED_SHORT,0);
  有了之前说过的逻辑,这个代码很容易理解吧?当把帧缓冲设置成当前工作帧缓冲时,绘制的东西就是往帧缓冲里面绘制的。当取消当前工作帧缓冲时候就是直接绘制到屏幕上的。这里还需要注意使用了帧缓冲就要每次调用clear来清除已经绘制出的东西了,要不然会糊成一团。另外,我的代码中还设置了个布尔型的uniform,这个是我在片段着色器中定义的。为false时候使用顶点颜色绘制对象,为true时使用贴图绘制对象。下面这个是片段着色器的代码,顶点着色器我就不贴出了没有什么新的东西 //片段着色器
precision lowp float;
varying vec3 co_v;
varying vec2 mp_v;
varying vec3 li;
uniform bool tp;
uniform vec3 li_en;
uniform sampler2D tex;
void main(){
  //如果tp为真则用贴图绘制,否则用颜色绘制
  if(tp) 
    gl_FragColor=texture2D(tex,mp_v)*vec4(li,1.0)+vec4(li_en,1.0);
  else
    gl_FragColor=vec4(co_v*li+li_en,1.0);
}
  
  最后是这篇文章的完整例子

  WebGL帧缓冲的基本概念

参考:
  http://wgld.org/d/webgl/w040.html
网名:
54.196.182.*
电子邮箱:
仅用于接收通知
提交 悄悄的告诉你,Ctrl+Enter 可以提交哦
神奇海螺
[查看全部主题]
各类Web技术问题讨论区
发起新主题
本模块采用即时聊天邮件通知的模式
让钛合金F5成为历史吧!
次碳酸钴的技术博客,文章原创,转载请保留原文链接 ^_^