实时渲染
渲染到纹理
就是将 渲染结果 作为纹理贴到另一个三维物体上去,
实际上就是 动态生成图像。
动态模糊
景深效果
帧缓冲区对象和渲染缓冲区对象
还记得WebGL的绘制结果存储在哪个缓冲区中吗?
答:
- 默认情况下在 颜色缓冲区 中绘图
- 开启DEPTH_TEST时,还会用到 深度缓冲区
- 总之,绘制结果存储在
颜色缓冲区 中
帧缓冲区对象
可以用来代替 颜色缓冲区 或 深度缓冲区
为什么在帧缓冲区中绘制的过程又称为离屏绘制(offscreen drawing)
答:
- 因为绘制在帧缓冲区的对象不会直接显示在画布上
- 帧缓冲区的内容一般会做如下处理:
- 进行一些处理再显示
- 直接用其中的内容作为 纹理图像
绘制操作不是直接发生在 帧缓冲区 中的,
而是发生在帧缓冲区 所关联的对象(attachment) 上。
- 颜色关联对象 color attachment 代替颜色缓冲区
- 深度关联对象 depth attachment 代替深度缓冲区
- 模板关联对象 stencil attachment 代替模板缓冲区
渲染缓冲区对象
帧缓冲区的每一个关联对象可以有两种类型:
- 纹理对象
存储了纹理图像 - 渲染缓冲区对象
表示一种 更加通用 的绘图区域,可以向其中写入多种类型的数据
如何实现渲染到纹理
实现步骤
实现目的: 把WebGL渲染出的图像作为纹理使用
实现方式:
- 将纹理对象作为颜色关联对象关联到 帧缓冲区对象上
纹理对象 ← 颜色关联对象 - 将渲染缓冲区对象作为深度关联对象关联到 帧缓冲区对象上
渲染缓冲区对象 ← 深度关联对象
小比喻:雪媚娘
绘图就像烹饪一样,对应的对象就像是 食材 与 工序 一样。
WebGL的缓冲区对应烹饪中的几个要素一样:
- 颜色缓冲区:食材
- 深度缓冲区:层次
但是只使用颜色缓冲区和深度缓冲区就好像做一道工序简单的菜,
食材 新鲜,层次 直观,
就比如要做西红柿炒鸡蛋,
食材(颜色缓冲区)有鸡蛋、番茄,工序(深度缓冲区)就是先放鸡蛋再放番茄。
但如果想要做 雪媚娘 这样工序比较繁琐的甜点,
你需要考虑 皮料(糯米粉、凉白开),
并且皮料的工序已经比较复杂,所以我们可以将准备皮料的工序单独取出来,
和其他工序区别开来,这就相当于帧缓冲区的 离屏绘制,
那么上面使用帧缓冲区对象渲染到纹理的目的和方式也可以对应如下的描述:
实现目的: 自制雪媚娘
实现方式: 将食谱(帧缓冲区对象)中的食材(颜色关联对象)指向糯米皮(纹理对象)
至于糯米皮怎么制作的,又是单独的一道工序了(即纹理绘制)
step 1
创建 帧缓冲区对象
1 | gl.createFramebuffer() |
step 2
创建 纹理对象 并设置其尺寸和参数
1 | gl.createTexture() |
step 3
创建 渲染缓冲区对象
1 | gl.createRenderbuffer() |
step 4
绑定渲染缓冲区对象 并设置尺寸
1 | gl.bindRenderbuffer() |
step 5
分配 纹理对象 到帧缓冲区的 颜色关联对象
1 | gl.framebufferRenderbuffer() |
step 6
分配 渲染缓冲区对象 到帧缓冲区的 深度关联对象
1 | gl.framebufferRenderbuffer() |
step 7
检查帧缓冲区 是否正确配置
1 | gl.checkFramebufferStatus() |
step 8
在帧缓冲区中 进行绘制
1 | gl.bindFramebuffer() |
FramebufferObject
1 | let VSHADER_SOURCE = ` |
1 | // 离屏绘制尺寸 |
1 |
|
1 | // 初始化帧缓冲区对象 |
1 | // 绘制 |
1 | // 获取attribute |
渲染流程详解
创建帧冲区对象
1 | framebuffer = gl.createFramebuffer() |
创建纹理对象并设置尺寸和参数
1 | texture = gl.createTexture() // 创建纹理对象 |
创建渲染缓冲区对象
1 | depthBuffer = gl.createRenderbuffer(); |
绑定渲染缓冲区并设置其尺寸
1 | gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer) |
深度关联对象的渲染缓冲区宽高必须与纹理缓冲区一致
将纹理对象关联到帧缓冲区对象
1 | gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); // 绑定帧缓冲区 |
为什么颜色关联对象存在0后缀?
答: 在OpenGL中有很多颜色关联对象,但在WebGL中只有1个,
所以就只将序号为0的颜色关联对象分给了WebGL。
将渲染缓冲区对象关联到帧缓冲区对象
1 | gl.framebufferRenderbuffer( |
检查帧缓冲区的配置
1 | let e = gl.checkFramebufferStatus(gl.FRAMEBUFFER) |
在帧缓冲区进行绘图
1 | function draw(gl, canvas, fbo, plane, cube, angle, texture, viewProjMatrix, viewProjMatrixFBO) { |
开启消隐功能
运行FramebufferObject时,矩形正反两面都被贴上了纹理,
这是因为WebGL默认 绘制图形的正反两面。
实际上WebGL提供消隐功能(culling function),
1 | gl.enable(gl.CULL_FACE) |
这样让WebGL不再绘制图形背面,能够提高绘制速度
未开启消隐
开启了消隐