作者:星陨
从泥巴到工艺品
先来举一个 玩泥巴
的例子,也就是我国的传统工艺 —— 陶瓷工艺
。
以我国传统的陶瓷工艺为例,先看看伟大的工匠们是如何把泥巴变成了精美的艺术品。
上面是一张粗略的示意图,详细的陶瓷制作流程可以自行网上搜索,或者 参考这里。
小时候大家肯定都玩过泥巴啦,沾一点水之后可以捏成各种各样的形状,没玩过的话,橡皮泥也行。
现假设我们要用泥巴做一个陶瓷的彩色方块。
那么首先就得从一堆稀泥里面捏出一个方方正正的小块,注意把小块的六个面都打磨均匀了。
然后再放在火窑经过高温烧制,七七四十九小时之后,就可以出炉了,此时小方块就已经变硬了,而且它的形状就是我们捏完后的样子。
待冷却之后就可以给它上色了,掏出画笔,涂上代表自己心情的颜色,这样就制作完成了一个陶瓷的彩色方块(是不是很简单呢(づ。◕‿‿◕。)づ)。
OpenGL 的渲染
OpenGL 的渲染流程和陶瓷工艺在某些地方是有着相同之处的。
首先,OpenGL 的渲染是讲究先有形后有色。也就是说用 OpenGL 去渲染绘制二维或三维的物体,你得先把物体给勾勒描绘出来。就好比上面提到的陶瓷小方块,在进行高温烧制之前,也是要先把物体的形状给捏出来的。
在 OpenGL 中提供了几种基本的绘制图形,比如点、线、三角形等,就是使用这几个基本图元去描绘这万千世界,比如绘制一个矩形,可以用两个三角形拼接起来。
在陶瓷小方块的例子中,捏完形状之后就可以高温烧制了,然后再涂色。在 OpenGL 中也有类似高温烧制的一步,叫做 光栅化
。
强调一下这只是类似的对比,因为这两者还是有差别的。
当我们在构建定义物体形状时,是在三维的坐标系里面,而显示的屏幕确实二维坐标系,这里还涉及到一个三维坐标到二维坐标的转换,待会再说。
假设现在已经执行完了坐标转换,物体的坐标转换成了二维的,那么就需要通过 光栅化
来映射到屏幕的二维坐标上。
以上图作为示例,先定义了三个顶点代表三角形基本图元,再把经过坐标转换之后的三角形映射到屏幕上。而屏幕是由一个一个像素点组成的,光栅化的过程就是要把三角形分解成许多个小的片段,再把这些片段映射到屏幕对应位置上,这样就可以让屏幕显示坐标转换后的物体了。
给三角形显示来个特写如下:
光栅化之后的三角形与屏幕显示的关系。实际上屏幕像素并不是小方格而是像素点,肯定不会有这么夸张的锯齿。
当光栅化操作之后,就可以对它进行着色了。这又回到了上面的例子,陶瓷小方块的涂色阶段,在这个阶段,数字图像处理学科开始发挥它巨大的作用了,各种各样的滤镜、图像效果都是在这个阶段进行的。
用上面的图总结一下目前的渲染流程,这个流程比较简化,理解起来就和上面的陶瓷制作相似,也比较形象的阐述了 OpenGL 渲染从 有形
到 有色
的过程。
现在再来看看坐标转换,坐标转换是在光栅化之前的,如下图:
这里会经过几个坐标的转换,分别是局部坐标、世界坐标、观察坐标、裁剪坐标。最后才是显示到屏幕的坐标。
具体的详细分析可以参考之前写过的相关 博客文章。
简单说就是我们有了一个立方体形状,那么我们在不同地方、不同角度看到的物体的不一样的,这是人相对于物体的移动变化。另外还有物体相对人的移动变化,物体移出了我们的视线、物体侧放着、物体反过来对着我们,得到的观察结果也是不一样的。
而坐标转换就是约定好观察者与被观察者之间的位置、角度,这样才能确定看到的结果是什么,对这个结果进行光栅化处理,再映射到屏幕上。
写到这,你以为你就懂了 OpenGL 的渲染流程了嘛?想要更好地发挥 OpenGL 的作用,还需要了解它的各种 测试 和 颜色混合
。
- 裁剪测试(Scissor Test)
在经过光栅化和片段着色器处理之后,通过裁剪测试可以选择只在某一个矩形区域内进行绘制,不需要绘制整个屏幕范围。这样的话,就可以在同一屏内分别绘制不同的内容。
- 模板测试(Stencil Test)
模板测试能够让我们预先定义一个任意模板范围,然后在指定的任意范围内绘制。相比裁剪测试只能在矩形范围内,模板测试更加灵活了。对于某些场合实现效果还是很有帮助的。
- 深度测试(Depth Test)
深度测试是指在三维坐标里面,距离观察点较远的片段会被前面较近的片段挡住,因此就不需要绘制那些被遮挡看不到的内容了,优化绘制。如果只是绘制平面二维的内容,那么深度测试开不开都没太大影响。
- 混合测试(Alpha Blending)
混合测试指定就是在原有颜色的基础上用另外的颜色绘制时,如何确定这两种颜色混合后的最终颜色。如果是做贴纸相关应用,要特别注意透明度的问题,看到过一些同学透明度没处理好导致贴纸效果不理想。
经过这些个步骤之后,绘制的内容就会映射到指定的大小范围内,也就是 视口(ViewPort)
的大小,一般都是通过代码来指定这个大小。
而装载绘制内容的就是 帧缓冲(FrameBuffer)
,绘制的结果就是输出到这了。通过绑定到不同的帧缓冲,可以将绘制结果输出到不同的帧缓冲里。而 OpenGL 中,屏幕就是那个默认的帧缓冲,所以一般就直接输出显示到屏幕上了。当然也可以自己创建一个帧缓冲,用来做离屏渲染等操作。
用 维基百科 上的图来做个总结:
这个流程图就和上文内容提到的大致相同了,如果你明白了渲染流程,那么看懂这个图应该也不是难事了~
以上就是关于 OpenGL 渲染流程的一个看法和理解,如果有什么不足之处欢迎指出~~
Vulkan 的渲染
接下来看看 Vulkan 的渲染。
先上一张 英伟达(NVIDIA) 公司给出的 Vulkan 组件图。
由于本篇只关注渲染流程,所以组件图中很多是现在不用太过关心的,比如 Image、Command-buffer 等,后面的文章会再做讲解。
看到图中有一个 Graphics pipeline
的组件,这个组件里面就包含 Vulkan 的渲染流程。
Pipeline 组件里面都有哪些操作?看下图~
看到图中中的左边内容是不是有似曾相识的感觉,就和 OpenGL 的渲染流程一样的,也是从顶点 -> 到光栅化 -> 到着色器 -> 到帧缓冲,再加上各种测试。
Vulkan 的 Pipeline 组件就是对应了 OpenGL 的渲染流程,或者说就是图形学里面的渲染流程,因为这个流程、这个原理是不会变的,Vulkan 也没有改变它,只是调用的逻辑结构发生了变化,但万变不离其宗,这也是为什么说要先学会 OpenGL 再来看 Vulkan 。
在 Vulkan 中,Pipeline 组件是可以复用的,通过绑定不同的资源而实现。可以预先创建几个 Pipeline ,在使用时选择对应的 Pipeline ,另外 Pipeline 还可以缓存起来,关于 Pipeline 的具体细节等到使用时再详细阐述。
渲染和 Vulkan 的调用是分开的。Vulkan 的调用很复杂,但是很多和图形学并没有关系,比如多线程、比如资源分配等。
只节选了组件图的一小部分来讲解,更多讲解敬请期待后续文章。
当然,这只是个人的学习经验,仅供参考,有讲的不对之处,欢迎指出,也可以加我微信一起交流学习: zh_ying_13 。
系列文章的代码地址:
https://github.com/glumes/vulkan_tutorial
参考
这里有一些不错的参考链接:
https://www.toutiao.com/a6457289342741119502/
https://zhuanlan.zhihu.com/p/20712354
https://zhuanlan.zhihu.com/p/49112352
https://www.khronos.org/opengl/wiki/Rendering_Pipeline_Overview
「视频云技术」你最值得关注的音视频技术公众号,每周推送来自阿里云一线的实践技术文章,在这里与音视频领域一流工程师交流切磋。