每日一语:
人从一出生,就面对无数的选择,在这无数的选择中,都面对着长时间的权衡,权衡着自己的得和失,是得多于失,还是失多于得,往往在这个权衡的过程中。就失去了机会。人都是自私的,都想获得利益最大化。这无可厚非。但现在的得不意味着以后的得,现在的失不意味着以后的失。有得就有失。所以我们应该快做决定,而且一旦做出了选择。就不应该瞻前顾后。应该勇往直前。没有谁是一次成功的,大都经过无数次的失败。我们应该勇于面对这样选择的机会,乱世出英雄,我们需要在选择中成长,选择中获得成功。
正文:
融合技术:
该技术能使我们将当前要进行光栅化的像素的颜色与先前已光栅化并处于同一位置的像素的颜色进行合成。即将正在处理的图元颜色值与存储在后台缓存中的像素颜色值进行融合。利用该技术,我们可以获得各种各样的效果,尤其是透明技术。
假定我们想赋予茶壶的一定的透明度,以使我们能够通过茶壶看到位于其后的板条箱。
我们怎样来实现这个想法了,当我们对处在半条箱前方的茶壶的三角形单元进行光栅化运算时,我们需要将计算得到的茶壶的像素颜色与板条箱的像素颜色进行融合,以使板条箱透过茶壶可以显示。这种将当前计算得到的像素颜色值与先前计算所得的像素颜色值进行合成的做法称为融合。
需要明确的一个要点是,当前要进行光栅化的三角形单元是与已写入后台缓存的像素进行融合。在例图中首先绘制的是板条箱,所以板条箱对应的像素已写入后台缓存。然后,我们再绘制茶壶,这样茶壶对应的像素与板条箱对应的像素就融合在一起了。所以在融合计算时,必须遵循以下原则:
首先绘制那些不需要融合的物体。然后将需要进行融合的物体按照相对于摄像机的深度值进行排序;如果物体已处于观察坐标系中,该运算的效率会相当高,因为此时只需对Z分量进行排序。最后,按照自后往前的顺序逐个绘制将要进行融合运算的物体。
对两个像素颜色值进行融合处理的公式如下:
OutputPixel = SourcePixel 叉乘 SourceBlendFactor + DestPixel 叉乘 DestBlendFactor
这个公式中每个变量都是一个4D颜色向量(r,g,b,a),符号叉乘表示分量逐个相乘。
OutputPixel 融合后的颜色值。
SourcePixel 当前计算得到的,用于与后台缓存中对应像素进行融合的像素颜色值。
SourceBlendFactor 源融合因子。指定了源像素的颜色值在融合中所占比例,该值在区间[0,1]内。
DestPixel 当前处于后台缓存中的像素颜色值。
DestBlendFactor目标融合因子。指定了目标像素的颜色值在融合中所占的比例,该值在区间[0,1]内。
源融合因子和目标融合因子使我们可以以各种方式修改源像素和目标像素的颜色值,从而可以获得各种不同的效果。
Direct3D中,默认是禁止融合运算的。你可以将绘制状态D3DRS_ALPHABLENDENABLE设为true,便启用了融合运算。
Device->SetRenderState(D3DRS_ALPHABLENDENABLE,true);
融合的计算开销并不低,所以应该仅在必需的场合中使用。当你绘制完需要进行融合的几何体之后,应禁止Alpha融合。在对三角形单元组进行融合时,最后进行批处理,之后应立即绘制出来,这样就可避免在每帧图像中都启用和禁止融合运算。
融合因子:
通过设定源融合因子和目标融合因子,我们可创建一系列不同的融合效果。
Device->SetRenderState(D3DRS_SRCBLEND,Source);
Deivce->SetRenderState(D3DRS_DESTBLEND,Destination);
其中,Source和Destination可取下列融合因子:
typedef enum D3DBLEND {
D3DBLEND_ZERO = 1,
D3DBLEND_ONE = 2,
D3DBLEND_SRCCOLOR = 3,
D3DBLEND_INVSRCCOLOR = 4,
D3DBLEND_SRCALPHA = 5,
D3DBLEND_INVSRCALPHA = 6,
D3DBLEND_DESTALPHA = 7,
D3DBLEND_INVDESTALPHA = 8,
D3DBLEND_DESTCOLOR = 9,
D3DBLEND_INVDESTCOLOR = 10,
D3DBLEND_SRCALPHASAT = 11,
D3DBLEND_BOTHSRCALPHA = 12,
D3DBLEND_BOTHINVSRCALPHA = 13,
D3DBLEND_BLENDFACTOR = 14,
D3DBLEND_INVBLENDFACTOR = 15,
D3DBLEND_SRCCOLOR2 = 16,
D3DBLEND_INVSRCCOLOR2 = 17,
D3DBLEND_FORCE_DWORD = 0x7fffffff
} D3DBLEND, *LPD3DBLEND;
透明度:
之前,我们忽略了顶点颜色中的Alpha分量和材质,这两个因素是我们需要重点考虑的。每个顶点颜色中的Alpha分量与颜色值类似,都是沿着三角形单元表面渐变的,但它并非用于确定某像素的颜色值。而是用于确定像素的Alpha分量。
Alpha分量主要用于指定像素的透明度。假定我们为每个像素的Alpha分量保留了8位,则该Alpha分量的合法区间中[0,255],其中[0,255]对应于透明度[0%,100%]。当像素的Alpha值为0时,该像素就是完全透明的。
为了能够用Alpha分量来描述像素的透明度,我们必须将源融合因子和目标融合因子分别设置为D3DBLEND_SRCALPHA和D3DBLEND_INVSRCALPHA.这些值恰好是融合因子的默认值。
Alpha通道:
我们并不直接使用得到的Alpha分量,而往往是从纹理的Alpha通道中获取Alpha信息。Alpha通道是保留给存储了Alpha分量的纹理元的一个额外的位集合。当纹理映射到某个图元中时,Alpha通道中的Alpha分量也进行了映射,并成为该图元中像素的Alpha分量。
指定Alpha来源:
默认状态下,如果当前设置的纹理拥有一个Alpha通道,Alpha值就取自该Alpha通道。如果没有Alpha通道,Alpha值就取自顶点的颜色。但也可以用下列绘制状态来指定Alpha值的来源(漫反射颜色值或Alpha通道)
模板缓存:
模板缓存是一个用于获得某种特效的离屏缓存。模板缓存的分辨率与后台缓存和深度缓存的分辨率完全相同,所以模板缓存中的像素与后台缓存和深度缓存中的像素是一一对应的。正如其名称一样,模板缓存的功能跟模板类似,它允许我们动态地,有针对性地决定是否将某个像素写入后台缓存。
例如,实现镜面效果时,我们只需在镜子所在的平面中绘制某个特定物体的印象。但是,我们想只在镜面所对应的子区域中显示物体的映像,这时,我们就可以使用模板缓存来阻止物体映像在非镜面区域中的绘制。
模板缓存的使用:
为了使用模板缓存,在Direct3D初始化时,我们首先必须查询当前设备是否支持模板缓存;如果设备支持模板缓存,我们还必须将其启用。将D3DRS_STENCILENABLE设置为true。
Direct 9.0中增加了双面模板的特性,该功能可通过消减绘制阴影体所需的绘制路径,从而提升阴影体的绘制速度。
模板缓存格式的查询:
模板缓存可与深度缓存一同创建。为深度缓存指定格式时,我们可以同时指定模板缓存的格式。实际上,模板缓存和深度缓存共享同一个离屏的表面缓存,而每个像素的内存段被划分为若干部分,分别与某个特定缓存相对应。例如,考虑如下3种深度模板
缓存格式:
D3DFMT_D24S8: 创建一个32位深度模板缓存,每个像素24位深度缓存,8位指定给模板缓存。
D3DFMT_D24X4S4: 创建一个32位深度模板缓存,每个像素24位深度缓存,4位指定给模板缓存,其余4位不用。
D3DFMT_D15S1: 创建一个16位深度模板缓存,每个像素15位深度缓存,1位指定给模板缓存。
模板测试:
我们可用模板缓存来阻止对后台缓存中的某些特定区域进行绘制。判定是否将某个像素写入后台缓存的决策过程称为模板测试。
(ref & mask) ComparisonOperation(value & mask)
假定模板缓存已处于启用状态,则每个像素都需进行模板测试,模板测试需要如下两个操作数:
左操作数(LHS = ref & mask),该值由应用程序定义的模板参考值ref和模板掩码mask通过按位与得到。
右操作树(RHS = value & mask),该值由当前进行测试的像素的模板缓存中的数值value和模板掩码mask通过按位与运算得到。
模板测试的下一个步骤是依据ComparisonOperation所指定的比较规则对LHS和RHS进行比较。true表示写入后台缓存,false阻止写入。一个像素不写入后台缓存,也不会被写入深度缓存。
模板测试的控制:
1,模板参考值
模板参考值ref的默认值为0,但我们可用D3DRS_STENCILREF绘制状态该值。例如,下列代码将模板参考值设置为1.
Device->SetSenderState(D3DRS_STENCILREF,0x1)
2, 模板掩码
模板掩码用于屏蔽ref和value中的某些位。其默认值为0xffffffff,表示不屏蔽任何位,我们可修改D3DRS_STENCILMASK来修
改该值。
Device->SetSenderState(D3DRS_STENCILMASK,0x0000ffff)
3, 模板值
如前所述,该值是当前待测试像素在模板缓存中的对应值。例如,假设我们正在对第i行,第j列的像素进行测试,则value就是模板缓存中的第i行,第j列的值。我们不能显示地单独设置模板值。但是我们可以对模板缓存进行清空操作。此外,我们还可以用模板的绘制状态控制将要写入模板缓存的内容。
4,比较运算
我们可以通过绘制状态D3DRS_STENCILFUNC来设置比较运算函数:
typedef enum D3DCMPFUNC {
D3DCMP_NEVER = 1,
D3DCMP_LESS = 2,
D3DCMP_EQUAL = 3,
D3DCMP_LESSEQUAL = 4,
D3DCMP_GREATER = 5,
D3DCMP_NOTEQUAL = 6,
D3DCMP_GREATEREQUAL = 7,
D3DCMP_ALWAYS = 8,
D3DCMP_FORCE_DWORD = 0x7fffffff
} D3DCMPFUNC, *LPD3DCMPFUNC;
模板缓存的更新:
除了决定一个具体的像素是否被写入后台缓存,我们还可以基于以下3种可能的情形定义模板缓存中的值如何更新。
1,第i行,第j列的像素模板测试失败。这种情况下,我们可借助绘制状态D3DRS_STENCILFAIL将模板缓存中处于相同位置的项的更新方式定义如下:
Device->SetRenderState(D3DRS_STENCILFAIL,StencilOperation);
2, 第i行,第j列的像素深度测试失败。这种情况下,我们可借助绘制状态D3DRS_STENCILZFAIL将模板缓存中处于相同位置的项的更新方式定义如下:
Device->SetRenderState(D3DRS_STENCILZFAIL,StencilOperation);
3, 第i行,第j列的像素深度测试和模板测试均成功。这种情况下,我们可借助绘制状态D3DRS_STENCILPASS将模板缓存中处于相同位置的项的更新方式定义如下:
Device->SetRenderState(D3DRS_STENCILPASS,StencilOperation);
其中StencilOperation可取以下预定义常量:
typedef enum D3DSTENCILOP {
D3DSTENCILOP_KEEP = 1,
D3DSTENCILOP_ZERO = 2,
D3DSTENCILOP_REPLACE = 3,
D3DSTENCILOP_INCRSAT = 4,
D3DSTENCILOP_DECRSAT = 5,
D3DSTENCILOP_INVERT = 6,
D3DSTENCILOP_INCR = 7,
D3DSTENCILOP_DECR = 8,
D3DSTENCILOP_FORCE_DWORD = 0x7fffffff
} D3DSTENCILOP, *LPD3DSTENCILOP;
5,模板写掩码除了前面提到的模板绘制状态,我们还可设置写掩码,该值可屏蔽我们将写入模板缓存的任何值的某些位。可用绘制状态D3DRS_STENCILWRITEMASK来设定写掩码的值,其默认值为0xffffffff,下例中对高16位实施了屏蔽:
Device->SetRenderState(D3DRS_STENCILWRITEMASK,0x0000ffff);