也许你会问,像素不就是颜色么,处理像素就是刷个油漆而已,屏幕上几个需要刷刷漆的点,何德何能让包括微软在内的整个图形界围着它们折腾了10年之久而且还要继续折腾下去呢?
实际像素操作可不是刷刷漆那么简单
如果一个像素被摆放在静止的空间内,周围的环境完全没有任何的变化,这个像素自然也就不会有任何变化。对于这样的像素我们甚至不用处理,直接以烘焙材质+纹理贴图的形式就可以完成表现了。但是,现实中的像素点肯定不会是这样的,如果你想要表达真实自然的颜色效果,这些像素就必然的会与光和其他像素发生关系,并在发生关系之后表现出正确的符合物理规律的颜色。而与光以及其他像素发生关系,就势必会导致复杂的处理过程,于是,也就有了这段长达10年而且可能会永远没完没了的纠葛。
以光照为例,在实际的应用中,对光照的操作有很多种方式,传统的方式大多是将光照信息直接对应到Pixel Shader指令的执行过程,比如multi-pass render及multi-light single pass render等。在Pixel Shader2.0中,这些方式被用于处理不同场景的光源对物体的影响。如multi-pass render pipeline会为每个光源创建一个单独的过程用来执行,多用于室内环境处理,而multi-light single pass render能够在一个过程中同时处理3至4个光源,可被用于室外大范围表现的环境。
不论采用哪种方式,在处理过程中都会对光线与像素的数学关系,也就是光线对像素颜色的影响以及透明度的影响,最简单的点光照改变像素颜色的公式可以写成Color = Ambient + Shadow * Att * (N.L * DiffColor * DiffIntensity * LightColor + R.V^n * SpecColor * SpecIntensity * LightColor),这个最简单的公式中最少有四个分量,即N.L、LightColor、R.V^n以及Attenuation需要处理,对每一个分量的处理都会导致最少一个单独的指令,同时还要为这些处理过程搭配对应的buffer以便能够缓冲和临时存放中间结果,另外,由于颜色是三原色组成,因此LightColor还要被拆分成LightColor.r、LightColor.g、LightColor.b三个分量分开处理,最终再将它们合并在一起。
折腾完这么一大堆的方程、变量和指令之后,你终于完成了一个像素在一个点光源照射之下的颜色变化。如果是multi-pass render pipeline,这个像素也许就算是弄完了,要是赶上multi-light single pass render,后面还有长长的其他光源以及多光源符合效应等一大堆过程在等着呢。
一个像素尚且如此,我们的屏幕中存在着2304000个像素(1920X1200),即便假设其中只有1/4需要处理,那也有576000个像素,即便所有这些像素都只被一个点光源照耀着,而且全部没有其他的交互作用关系,我们也要把上面那些步骤整体重复576000次才算完,这其中的运算量到底有多大,诸位可以想象一下。而如果对效果的要求很高,比如说将漫散反射定义成新光源,那么每个像素所要处理的运算量都将因为光源的激增而急剧加大,即便加入光照探针之类的手段,这巨大的运算量依旧会给现有的常规硬件带来很大的压力。而实际的游戏应用中,我们面对的效果显然不止漫散反射光源这么点而已,大量的像素间的交互作用一样要被处理,这种种处理需求加在一起,就构成了像素沉重的灵魂。
现在,你应该明白为什么微软带着大家忙活了十来年,也没搞定刷漆这么个简单的活了吧。