stonstad 发表于 2022-9-14 16:09

[Piccolo图形悦读笔记]卷6:forward渲染-平行光阴影pass(2)

本文是笔者学习王希老师的GAMES104现代游戏引擎入门课程,阅读配套课程源码Piccolo引擎的学习笔记,由于笔者在C++、数据结构、引擎图形等部分也是萌新,如有疏漏错误还请各路大佬斧正。
前情提要
卷5进入到了平行光阴影pass的内部,阅读了mesh node的重新组织,分析了引擎层面和图形api层面pass概念的不同,阅读了storage buffer的填充。
本文开始读组织draw call的过程。
遍历mesh
如下图所示,这一段的意思应该是想遍历drawcall_batch中所有mesh,根据前一篇卷5:forward渲染-平行光阴影pass(1)可以知道,material和mesh是一一对应的,而每个mesh和它的node的vector也是一一对应,只是vector中存有多个node,所以这里的第二层遍历笔者个人觉得好像没有作用,因为每个mesh_instanced其实只有一项。



遍历mesh

对于每个mesh统计其中node数,然后把描述符集和layout绑定到命令buffer。
关于descriptor set和layout的补充:(施工中)卷5.5:descriptor set及其layout装填vertex和index buffer
如下图,把存放顶点和index的buffer绑定到命令buffer,index应该是指组成三角面片的索引,空间中光有零散顶点不能构成mesh,需要index记录,比如点1、2、3构成一个三角形,2、3、5是第二个,以此类推。



装填vertext和index buffer

计算draw call数
如下图先计算draw call的storage buffer object列表长度,代表每个draw call可以处理的最大实例数,之后用实例总数取整除可以得到处理所有实例需要的draw call数。



计算draw call数

值得注意的是,计算draw call数的操作是在遍历每个mesh之内的,也就是说每个mesh都需要至少一个甚至数个draw call来绘制,那么不同类别的mesh以及内部node一多drawcall数量可能会迅速增加。
遍历每个draw call
如下图,每次draw call假设最多可以处理m个实例,那么如果剩余不小于m,就处理m个,不足m时就按实际剩余数处理。



遍历每个draw call

填充per drawcall storage buffer
这一步关于到ring buffer的操作和卷5:forward渲染-平行光阴影pass(1)中填充per frame storage buffer的操作几乎一样,不再赘述。【思考:每个draw call的storage buffer都是单独开的,原来不能公用吗】
填充per drawcall vertex blending buffer
这一步查询当前drawcall中的是否至少有一个实例有关节矩阵,如果有就在ring buffer开个vertex blending buffer填充相关信息,没有就不开。
最后绑定上文的offset到descriptor set,这样shader就能通过descriptor访问到计算出来的offset。最后把draw指令填到指令buffer,也就是这一步在gpu里shader把阴影贴图绘制到有阴影的部分。



绑定per drawcall

最后把结束pass标签填入命令buffer



结束pass

至此,平行光阴影pass就读完了。可以看到在CPU中处理pass绝大多数是处理资源,填写参数,设置绘制指令,这也好理解,因为绘制本来是给GPU做的,真正让人兴奋的硬件绘制内容大概只有读到shader才能看到了吧,不过本笔记就是想从完整引擎角度阅读绘制代码,况且对现代游戏引擎来说资源调度也是性能优化的重要一环,所以看似冗长复杂的资源准备管理也是值得关注的代码。
小结
本篇结束了平行光阴影pass,阅读了如何分配drawcall,以及每个drawcall内的资源是如何处理的。在这之后是点光源pass,笔者阅读后发现内容基本和平行光pass是一样的(思考:这也好理解,对于某个mesh顶点,通过点光源信息得到方向和强度后,之后的计算就和平行光差别不大了)。
下一篇看看平行光阴影pass的shader部分。
页: [1]
查看完整版本: [Piccolo图形悦读笔记]卷6:forward渲染-平行光阴影pass(2)