WebGL学习(十六)渲染到纹理(帧缓冲)

1.介绍

之前章节学习纹理的时候,我们是将外部现成的图片作为纹理,渲染到图像上。

而这里我们将要使用自己渲染的图像,作为纹理,渲染到另外一个图像上。也就是可以动态生成纹理。

2. 帧缓冲Framebuffer

帧缓冲介绍参考

帧缓冲对象介绍

虎书(fundamentals of computer graphics-5th)17章

linux下的帧缓冲

opengl es 帧缓冲

2.1. 缓冲区

在之前的学习中碰到了许多缓冲区,为什么需要建立这么多缓冲区?

顾名思义,缓冲区就是用来缓冲的,因为GPU的内存速度远远大于CPU或者其他设备内存速度。我们把cpu或者其他设备的内存称为host memorygpu等显示设备的内存称为device memory

如果你想做一些操作,比如将host memory的数据复制到device memory,最典型的就是顶点缓冲数据复制到GPU中,如果直接转移数据会因为host memory速度太慢device memory老是需要等待,从而影响了运行效率。

现在有了缓冲区,数据先被放入缓冲区,host memorydevice memory各自读写(当然这中间还有很多边界问题),不会因为某一方速度而影响了另外一方。

2.2. 帧缓冲区

帧缓冲区只是逻辑上的概念,并不是真的有这么一个缓冲区,它是深度缓冲区模板缓冲区颜色缓冲区积累缓冲区多重采样缓冲区等等缓冲区共同组成的一个集合

默认情况下,系统给我们提供了一个默认的帧缓冲区。

2.3. 帧缓冲对象

要操作帧缓冲区,就必须要操作真实的缓存对象而不是逻辑的一个集合,所以诞生了帧缓冲对象FBOFBO本身不存储任何数据,但是它有很多附着attachment点,用来绑定不同的缓冲对象,实现高级的效果。

比如:

  • 后期处理:在帧缓冲区中渲染场景,然后将帧缓冲区的内容作为纹理传递给另一个着色器,对其进行一些后期处理,比如模糊、泛光、色调映射等。
  • 阴影贴图:在帧缓冲区中从光源的视角渲染场景的深度值,然后将帧缓冲区的内容作为纹理传递给另一个着色器,根据深度值判断物体是否在阴影中。
  • 延迟渲染:在帧缓冲区中渲染场景的几何信息,比如位置、法线、材质等,然后将帧缓冲区的内容作为纹理传递给另一个着色器,根据几何信息计算光照和着色。

image.png
上图展示了一个FBO的结构:

  1. FBO包含多个附着点,至少一个颜色缓冲区附着点(color_attachment)一个深度缓冲区附着点(depth_attachment)一个模板缓冲区附着点(stencil_attachment)
  2. 每个附着点都可以绑定两种对象:纹理对象Texture Object渲染缓冲对象RenderBuffer Object
  3. 颜色附着点有多个的原因:因为渲染结果的颜色值可以有多种格式和通道,比如RGBRGBARGB16F等,而且可以使用MRT(多渲染目标)技术使着色器能够输出不同信息给多个缓冲区,就像这样:
    image.png
  4. 纹理对象和渲染缓冲对象区别,找到的信息比较少,大概就是渲染缓冲对象只能读写且存储的数据格式是opengl原始格式,无法采样或者更细致的操作,但是由于渲染缓冲对象更简单,存储的速度更快,操作耗时也更少。

3. 渲染到纹理

现在试着把在帧缓冲区绘制好的图像当成纹理,渲染到另外一个图像上。

我们将自定义FBO,替代默认的帧缓冲区。

image.png
我们先在自定义的帧缓冲上绘制,再替换默认的缓冲区。在替换之前的绘制被称之为离屏绘制。这样可以提高效率,毕竟是在内存中操作片元,就像dom中的fragment

msedge_hxWRfMcDUw.gif

就像这样,我把一个立方体当做纹理渲染到了一个平面上。

3.1. 创建帧缓冲

相当于创建了一个新的绘制空间,只不过不显示在屏幕上。

// 1. 首先绑定新的帧缓冲
const framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
// 离屏绘制区域大小
// webgl1的texImage2D只能是2的次幂宽高
const OFFSCREEN_WIDTH = 256
const OFFSCREEN_HEIGHT = 256
gl.viewport(0, 0, OFFSCREEN_WIDTH, OFFSCREEN_HEIGHT);

上面绑定了一个新的帧缓冲区域,默认的帧缓冲区域是gl.bindFramebuffer(gl.FRAMEBUFFER, null),然后设置了viewport,也就是绘制区域,越小绘制越快但是更模糊。

  // 2. 将纹理对象绑定到颜色缓冲附着点
  const framebufferTexture = gl.createTexture()
  gl.bindTexture(gl.TEXTURE_2D, framebufferTexture);

  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, OFFSCREEN_WIDTH, OFFSCREEN_HEIGHT, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, framebufferTexture, 0);

上面绑定了帧缓冲的一个颜色缓冲附着点,我们将一个纹理对象绑定在上面,这个纹理对象很重要,之后的绘制相当于就是在这个对象上绘制。

texImage2D中的最后一个参数,在之前的先学习中直接给的image标签,现在不需要绑定数据,因为数据是后面绘制上去的。

webgl 1中只有一个颜色附着点,所以framebufferTexture2D中只能绑定给COLOR_ATTACHMENT0

这里补充一点之前在纹理学习中没有的点:

WebGL 1中,texImage2D的宽高必须是2的次幂,否则会报错。这是因为WebGL 1只支持幂次方纹理(power-of-two textures),即纹理的宽度和高度都是2的次幂。如果你想使用非幂次方纹理(non-power-of-two textures),需要在WebGL 2中使用,并且满足以下条件:

  • 纹理过滤方式必须是gl.NEARESTgl.LINEAR,不能使用mipmap
  • 纹理包裹方式必须是gl.CLAMP_TO_EDGE,不能使用gl.REPEATgl.MIRRORED_REPEAT
  • 纹理的level参数必须是0,不能使用多级分辨率。
  // 3. 将渲染缓冲区绑定到深度缓冲附着点
  const depthBuffer = gl.createRenderbuffer();
  gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
  gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, OFFSCREEN_WIDTH, OFFSCREEN_HEIGHT);
  gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);

因为我们要绘制三维图形,所以需要深度信息,所以这里将渲染缓冲对象绑定到深度缓冲附着点。

这里要注意深度缓冲区的大小要和颜色缓冲区大小一样。

3.2 绘制纹理图像

上面的准备工作相当于新建了一个绘制的空间,里面有全新的深度缓冲区和颜色缓冲区。

  // 绘制立方体
  gl.bindFramebuffer(gl.FRAMEBUFFER, null);
  gl.bindTexture(gl.TEXTURE_2D, null);
  drawNormalCube()

上面代码值得注意的是,清空之前绑定的纹理对象bindTexture,因为此时绘制的图像不是贴上framebufferTexture,而是写入framebufferTexture

总之在初始化帧缓冲之后应该先清空绑定,这样可以避免未知现象。

3.3. 绘制纹理

  gl.bindFramebuffer(gl.FRAMEBUFFER, null);
  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
  gl.bindTexture(gl.TEXTURE_2D, framebufferTexture);

  drawPanel()

上面代码恢复了默认帧缓冲,然后重新设置了viewport,最后将之前的帧缓冲的纹理对象作为纹理绘制上去。

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYMVfHyP' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片