面试被问CPU裁剪,答不上来
前言
CPU擅长串行计算,GPU擅长并行计算
CPU绘图一次一个点,GPU绘图一次绘大量的点(像素)
二者交互时,对CPU来说代价很大,drawcall前CPU要做很多准备工作
要让他们做各自擅长的事情,且不要频繁交互
如果一方太繁忙,可能需要另一方帮忙,互相帮忙可能对双方都有好处
裁剪clipping与剔除culling的区别
你可以简单理解为:
视锥体剔除:如果在应用程序阶段,能够判断出物体 A 不在视锥体内部并且与视锥体的6个面没有交叉,那么就把物体 A 剔除,不用发给几何处理阶段
裁剪:物体 A 与视锥体的6个面有交叉,意味着物体A的某一部分 a在视锥体外,那么在几何处理阶段完成投影后,就得把 a 给裁剪掉(只保留视锥体内的图元),不用发给不用发给屏幕映射粒度不同。
视锥剔除是在object层面的,对object的aabb做处理,直接剔除aabb在视锥外的object。
裁剪是在图元(三角形)层面的,视锥剔除之后的object可能跨越视锥平面,对这种object对构成object mesh的三角形再做裁剪。
似乎只有几何阶段真正进行了裁剪,其他时候都是剔除。然而另一篇文章这样说:
裁剪(Culling)的字面意思是“从大量事物中进行删除”。在计算机图形学中,相对应的就是裁剪技术(Culling Techniques)所要做的工作——“从大量游戏事物中进行删除”。所谓的“大量事物”就是需要绘制的整个场景,删除的是对最终图像没有贡献的场景部分,然后将剩余场景发送到渲染管线。因此,在渲染方面通常使用“可见性裁剪(Visibility Culling)”这个术语。但其实,裁剪也可以用于程序的其他部分,如碰撞检测(对不可见物体进行不十分精确的计算)、物理学计算,以及人工智能(AI)领域。
理论上,裁剪操作可以发生在渲染管线的任何一个阶段
这么说在计算机图形学中裁剪和剔除应该统一成一个意思。
CPU的粗粒度剔除
应用阶段,CPU可以做粗粒度剔除(culling),包括视锥体剔除(Frustum Culling)和遮挡剔除( Occlusion Culling)两种,这样可以剔除不可见的物体,减少无效数据,移交给GPU的数据更精简。CPU做的粗粒度剔除是物体层面的。
应用程序阶段一个很重要的任务是“场景管理”。通过特定的数据结构,应用程序可以将待渲染的物体组织起来。这样在做视锥裁剪时,就可以利用加速算法,以很小的代价,剔除大量不可见的物体,它们就不会被送往渲染流水线,GPU的负载就会降低。
以CPU的少量代价来节约GPU,这样的做法很普遍。
但compute shader技术似乎可以同时降低双方的代价
我的GPU Driven Rendering,把Mesh分块后储存在显存中,直接用compute shader做粗粒度视锥剔除和遮挡剔除,直接省去了cpu消耗和提交消耗
几何阶段的裁剪
在观察空间经过投影矩阵(裁剪矩阵,2种,透视和正交)到裁剪空间的过程中,发生了视锥体裁剪,这与应用阶段的视锥体剔除基本类似,只是后者仅仅做了剔除,与视锥体相交的物体保留,没有裁剪;而前者会做视锥体剔除并且裁剪。真正的投影发生在裁剪空间到屏幕空间的过程中,这个过程中进行了标准齐次除法(透视除法),然后得到了归一化的设备坐标(DNC)。至此,视锥体变成了一个立方体。然后经过视口裁剪(视口变换)映射到屏幕坐标。
光栅化阶段的裁剪
深度测试,深度剔除,遮挡剔除
似乎是一个意思。有画家算法和Z缓冲算法等。Unity的深度测试在片元着色器之前,称为Early-Z技术。《计算机图形学基础教程》称之为消隐。
当两个物体有前后位置关系时,位于前面的物体会将后面的物体部分或全部遮挡。这时为了优化考虑,GPU不应该绘制被遮挡的片段,这种行为称为遮挡剔除。为了更好了解遮挡剔除与深度测试,我们先来看看深度缓存。深度缓存是一个只含有特定像素的深度信息而不含图像数据的表面。深度缓存为最终绘制图像中的每一个像素都保留了一个深度项。所以,当所绘制的图形的分辨率为640X480时,深度缓存中将有640X480个深度项。深度缓存用于计算每个像素的深度值并进行深度测试,深度测试的基本内容是依据深度值让处于同一位置的不同像素进行竞争,以选出该写入该位置的像素,距离摄像机最近的像素获胜,并被写入深入缓存的对应位置上。这样做是合理的,因为距离摄像机最近的像素一定会将位于其后方的像素遮挡。
正面和背面剔除
OpenGL蓝宝书说在几何阶段进行,有文章说Unity的背面剔除在光栅化阶段进行。
裁剪测试
用于渲染时限制绘制区域,通过此技术可以再屏幕(帧缓冲)指定一个矩形区域。启用剪裁测试之后,不在此矩形区域内的片元被丢弃,只有在此矩形区域内的片元才有可能进入帧缓冲。因此实际达到的效果就是在屏幕上开辟了一个小窗口,可以再其中进行指定内容的绘制。
模板测试,蒙版测试
在说明模板测试之前,我们需要先介绍一下模板缓存。
模板缓存与深度测试缓存、后台缓存(或颜色缓存,最终显示在屏幕上的缓冲区)的大小(分辨率)完全一致,模板缓存中的像素点与后台缓存的像素点是一一对应的。模板缓存允许我们动态地、有针对性地决定是否将某个像素写入后台缓存中。模板缓存用与获得某种特效,如镜面效果或阴影效果。在实现镜面效果时,我们在“镜子”这块区域中绘制某个特定物体的映像,而使用模板缓存来阻止物体映像在“非镜子”的区域中进行绘制。为了进行这种阻止,就需要使用模板测试。判断是否将某个像素写入后台缓存的决策过程,称为模板测试。
alpha测试
纹理的颜色中含有alpha分量,alpha分量主要用于指定像素的透明度。假定我们为每个像素的alpha分量保留了8位,则该alpha分量的合法区间是[0,255],其中,[0,255]对应透明度[0%,100%]。当像素的alpha值为0时,该像素是完全透明的。如果像素的alpha值为128,其透明度就是50%,而alpha值为255则表示完全不透明。alpha测试指的是将一个像素点的alpha值和一个固定值比较。如果比较的结果失败,像素将不会被写到显示输出中。
裁剪平面
除了视景体的6个裁剪平面(左、右、底、顶、近和远)之外,还可以另外再指定最多可达6个的其他裁剪平面,对视景体施加进一步的限制。每个平面都是由它的方程式Ax + By + Cz + D= 0的系数所指定的。裁剪平面会根据模型和视图矩阵自动执行适当的变换。最终的裁剪区域将是视景体与其他裁剪平面定义的所有半空间的交集。记住,OpenGL会自动对部分被裁剪的多边形的边进行正确的重构。