华科_图形学笔记_10_有趣的测试和合并_片元操作

计算机图形学_华中科技大学_中国大学MOOC(慕课)


10.1_再看片元操作

在之前的课程中,我们讨论了片元着色的相关计算,包括光照、纹理、阴影等。今天,我们进入到管线的最后一步_片元操作。

我们首先回顾一下整个可编程管线在几何阶段的最后一步是屏幕映射。屏幕映射后,每个顶点除了在屏幕坐标系中的XY坐标的值并没有丢,而是一起传给了光栅化阶段,也就是说它还是一个立体的,不是一个平面。

还要特别强调一个问题,那就是屏幕坐标系的问题,之前的建模坐标系,观察坐标系一直都是右手系。而在屏幕坐标系,就变成了一个左手系。也就是说Z轴指向屏幕里面。

而且还把这个Z值规范化为零到一之间。Z=0,是前截面,Z=1,是后截面。

也就是说,我们认为Z越小就越靠近我们,Z越大也就越远离我们。

屏幕映射之后,就进入到了光栅化阶段,由于三角形设定和遍历是由GPU自己完成的,我们之前重点给大家讲了片元着色。通过片元着色,我们算出了每个片运的颜色质。但这实际上只是一个候选值。他是否能够最终被选中,还要看最后的片元操作怎么做了。

片元操作

片元操作_具体包括模板测试、深度测试和颜色混合三个方面的工作。

几个重要的缓存

要明白这些工作是干什么的,必须先明白几个缓冲区的概念。

这几个缓冲区分别是颜色缓存,深度缓存,模板缓存和累计缓存。

第一个是颜色缓存,他存储了每个像素点的颜色值。

第二个是模板缓存,它存储了一个模板,比如我们可以设定模板上对应点为1的像素点才会被显示出来。

这时候有点像拿这一个模板去喷漆,模板上镂空的地方,才会在后面的板子上形成一幅图案。所以模板测试,通常用于限制渲染区域

比如这里的这个模板镂空的地方,是一个T字型的。那么我们最终渲染出来的也就是一个T字形内部的内容。

第三个是深度缓冲区,它存储的是每个像素点的深度值,比如对于这个像素点P,他记录的是距离是点最近的多边形A,对应片源上相应点的深度值。

第四个是累计缓存了,他与颜色缓存很类似,同样是存储着像素点的颜色值。

累计缓存,是为了合成多幅图像而设计的,它提供了一种多重曝光的方法。从方法上看,它主要是将图像渲染多次。每一次,对场景的位置或者什么的进行微小的渐增的改变,然后把结果进行累积 ,使用累积缓存可以产生许多图像的效果。比如反走样,运动模糊等等。

比如这里就是游戏中常见的运动模糊现象,他的做法就是在累积缓存中存放多幅有微小位移的场景图,然后再合并。

有了这几个概念,我们在看片元操作应该就有不同的认识了。

首先,什么是模板测试? 这就跟刚才的模板缓存相关了。感觉模板缓存就好像一个遮罩,只让部分像素通过,只有通过了模板测试的像素点,颜色值才可能最终被显示出来。

比如这里的模板是这样的。最后渲染出来的结果,就是这个回字形区域内的内容。

接下来是深度测试,这和刚才的深度缓存密切相关。

在一个屏幕像素对应多个片元的时候,就需要根据片元上对应点的Z坐标值或者说深度值来最终决定到底选取哪个片源的颜色值作为这个像素的颜色值了。简单的来说,就是要决定我们最后能看见什么。

比如这个场景中有这几个片元,我们最终看到的就是这个五边形,这里就涉及到了一系列经典的算法

通过了模板测试和深度测试,就进入到了颜色混合了,

我们在第九章奇妙的颜色中,已经给大家进行过讲解,就是颜色混合的方法。是否要进行颜色混合,与颜色值RGBA中的A分量,也就是阿尔法分量相关了,阿尔法分量不为1,就代表它有一定的透明度,只要这两个片元中有一个有透明度,就需要进行混合。

在片元操作的最后阶段,也会遇到许多需要混合的地方,比如遇到了透明物体。

看左边的这幅图,中间的球就是透明的,透过它可以看见后面的还。

而右边的玻璃也是透明的,透过它可以看见后面的箱子,这时候显然就需要混合。

怎么混合?这里我们就要用到深度缓存了。

比如刚才的P点,根据深度值,只需要选取多边形A的表面颜色。可是现在大家看A变成半透明的了,那么透过A是不是就可以看见后面的多边形B?

所以这个时候就应该进行颜色混合了。如果B是不透明的,我们看到的就是右边这幅图的结果。

其实除了透明物体,还有一些特效的制作也需要用到混合。

比如运动模糊,累积缓存中的内容怎么合并?

也是通过颜色混合来合并的,比如这里就给出了连续多幅有微小位移的图

那么我们这里就需要用到颜色的混合模型对他进行合并了,合并之后就会产生运动模糊的效果。

再接着就是这里的泛光效果Bloom。就是这个灯给人的感觉不是左边的这种。

而是右边这种加入了泛光的效果。

左边的这幅图没有泛光的效果,我们把其中光亮度较高可以看作光源的部分提取出来的,对它进行一个模糊处理,比如可以对他进行高斯模糊。

就得到这样的结果了,我们把原图和高斯模糊后的图进行合并。这时候就可以看到明显的泛光效果了。

到这里,也就结束了整个渲染管线。显然,只有到了片元操作,才会真正改变帧缓存中每个像素的颜色值,从而在显示设备上看到我们最终的结果。

这里还有一点需要说明,那就是一般GPU采用一种双重缓冲Double_Buffering的策略,一般把渲染结果写在后置缓冲区(或者有时叫黎平缓冲区中)。之后再与前置缓冲区进行交换,这个前置缓冲区就直接与显示设备关联,之后这个设备上就显示出这样的一幅图像。


10.2_谁罩住了我?

在真实感图形的显示中,还有一个重要的问题,那就是在给定视点和视线方向之后,决定场景中哪些物体的表面是可见的,哪些是被遮挡不可见的。这个问题,在传统图形学理论中被称为消隐。

看这里的两个物体,首先我们会发现这个立方体遮挡了圆柱体的部分,这就是别人挡住了我。

这里就需要用到上一节提到的深度测试的方法对其进行判定。可是这个最前面的立方体也不是六个面都可见。

想想,是不是任何时候,无论从哪个角度.都只能最多见到它三个面,这就是自己遮住了自己。

怎么剔除那些看不见的面?

在图形学中,就用面剔除来解决这个问题。因此,这一节实际上我们要从面剔除和深度测试两个方面进行学习。

首先就是面剔除,面剔除首先是基于面的正反面定义。

在之前规则形体边界表示中,我们给出了一句多边形顶点序列来计算正面和反面的方法。

那么这样一来刚才的立方体,每个面都可以算出一个正面,很多人都有一个误解,认为面剔除就是剔除反面。其实这个理解是错误的。

怎样进行面剔除?我们这里给出了一个后向面判别的方法。

表面的正面和反面在这里,仍然十分重要。

因为我们需要用到这个平面的法向量。

我们把这个方向记为N,而N也就是指向它的正向。

这时候观察的方向是Vview和这个N点乘是大于0的,这个面就是后向面,是不可见的面。

例如看这幅图,对于这个立方体,首先看最上面的这个面,也就是用红色标识的。这时候,它们俩相乘,是小于零的,很显然这个面是前向面是可见的。再看看。最前面的这个面也就是用蓝色标识的,同样对于这个视点来讲,他也是可见面。可是对于这个右侧,这时候我们发现,这两个向量的夹角已经大于90度了,因此,他们俩点乘市大于零的。显然,这个面是后向面,是不可见的。

通过这种方法进行判断,并且进行面剔除是一种高效的做法。

在可编程渲染管线中,我们通常在什么时候做这件事儿?实际上我们通常在光栅化阶段开始的时候,也就是在片元着色之前。就可以做这个事儿了。

当然也就是在进行深度测试之前进行的,这样的做法可以帮助GPU节省不少计算资源。

因为剔除的面是不需要再进行着色计算的。

如果绘制的时候不对物体的深度进行判断,就会给人一种错觉,

比如。这里上面的这幅图。

这里其实需要计算这些物体之间的位置关系,从而进行正确的消隐那么这里

就涉及到一系列的深度测试算法,主要包括深度缓存器算法和深度排序算法。

首先是深度缓存器算法。

这个算法就用到了上一节给大家讲到的深度缓存

算法的思想对每个像素点找到距视点最近的片元,对于屏幕空间来说,也就是Z值最小的片元。这个片源的颜色值就是这个像素点的颜色值

比如这里的A就是离视点最近的。

它的过程是这样的,第一步是初始化,也就是将深度缓存与帧缓存所有单元进行初始化。

深度缓存中每一个像素单元的值置为Z的最大值一。然后帧缓存中各个像素单元的颜色值置为背景色。

接着,第二步处理场景中的每一个多边形了,每次一个计算多边形上这个点的深度值,如果这个Z比刚才的这个深度缓冲区的这个对应值要小。

那么也就是说这个点更靠近屏幕,那么我们就需要把Depth_Buffer里面这个点的深度值置为Z了。

同时,要取得这个多边形表面的颜色值,SurfColor把它的值放在这个Frame_Buffer对应的这个像素点处。

否则,就不更改,当遍历过场景中所有的多边形之后,Framebuffer中每个像素点就是所有可见面的颜色值了。

第二个算法是深度排序算法,又叫画家算法,他的思想,来源于画家画画的过程,画家在创作一幅油画的时候,总是先画背景。在画较远处的场景。然后,是近一点的,最后才画最近的景物。

具体到算法中怎么处理?

这里就用到两个队列

一个是用于存储所有多边形的队列M

一个是根据深度排序得到的优先级队列N

从步骤上它包括两步

第一个是深度排序,就是将这个多边形,按深度优先级进行排序,结果就存在队列N中

什么叫深度优先级?我们认为距视点近的优先级高,距视点远的优先级低。

第二个步骤是扫描转换,从队列N种取出多边形。进行绘制。那么其实,也就是由优先级低到高,逐个对多边形进行扫描转换。

这其中,深度排序又分为三

第一步,是初始化。

就是将场景中所有的多边形按Zmax由大到小的顺序存入到一个先进先出的队列M中。同时,初始化一个空的队列N。

第二步,我们就处理只有一个多边形的情况,比如说M中只有一个多边形,很显然把它直接加入到N中就可以了

否则,就进入第三步,也就是有多个多边形了。

这时候从M中要取出一个多边形B,对AB要进行比较了。如果是左边这个图的情形A,说明,A是M中所有多边形中深度最深的,他根本不会遮挡别人,所以,按先进先出的原则,加入到N中,在最前面。否则,就说明会存在着一个多边形,A和B之间可能有深度重叠,这时候就要进行这样的判断。

如果是左图的这个情况。A和B,他们在Xoy平面上投影的包围和都没有重叠。

那么也就是说,A和B在队列中的顺序无关紧要,这时候也可以把A先放在N中。

可是如果是左边这幅图的情况。就需要进一步判别了。要看平面A是否完全位于B上与A重叠的部分之后。

比如这里我们把重叠的部分用红色标记了,而A完全在后面,所以我们可以把A直接加入到N中。

否则就说明A有部分不在这个重叠面之后,比如他的这个蓝色部分。这时候我们就需要进一步判别了,我们可以反过来看B

看B上,这个平面A与B的重叠的这个部分是不是完全位于A之前?如果是的?也可以把A先加入到队列N中。

当AB排好序了,我们就可以继续处理其他的多边形了。比如我们又取出来一个C。通过判别,我们发现B遮挡了C的一部分。因此,C的优先级是最低的。最后我们的优先级照这幅图所表现的情况,由低到高就是CB和A。之后就会按照这个顺序进行扫描转换,当然先画C,在画B,最后画A,对应的结果当然是正确的,以上就是深度排序的三个步骤,排序完成以后,得到了队列N。

接下来就是扫描转换了,从N中按照优先级顺序取出多边形进行绘制。

还是刚才的这幅图的情况,我们先取出C进行绘制,之后C出列,然后取出队列最开始的B进行绘制,接着B出列,最后就是A的绘制和出列了。显然我们会得到一个正确的结果。

以上,就是两个经典的深度测试算法。大家应该感觉还是挺复杂的。

但是值得我们庆幸的是,在Opengl中,进行消隐显得比较简单,比如面剔除。在Opengl中,可以直接开启多边形剔除。

比如这里的glEnable,然后就可以调glCullFace这个函数了,  通过对这里的mode的值的设定,可以得到不同的结果。

而深度测试,原本在Opengl中默认的状态是未开启的,所以如果要启用深度测试,我们也得通过,只有glEnable(GL_DEPTH_TEST)来开启深度测试。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值