zoukankan      html  css  js  c++  java
  • (转)【D3D11游戏编程】学习笔记十五:混合(Blending)

     (注:【D3D11游戏编程】学习笔记系列由CSDN作者BonChoix所写,转载请注明出处:http://blog.csdn.net/BonChoix,谢谢~)

            

           在D3D11中,“混合”发生在像素着色器阶段的下一阶段,即Output Merger Stage。整个场景在全部经历过像素着色器阶段后,对应于屏幕上每一点像素,可能有多个片段(Fragment)。如下图所示:

            该图中,场景中有三个点P1,P2,P3投影在屏幕上同一个点P。这样在像素着色器阶段后,针对P1,P2,P3将有三个片段与像素P对应。默认情况下,在渲染管线中,混合是被关闭的。这时为了确定像素P的最终显示颜色,主要依据是深度测试(这时暂不考虑模板测试等其他因素)。通过深度测试的片段将自身颜色替代后缓冲区中P点的当前颜色,未通过的片段被抛弃。

           当混合功能被打开时,决定最终颜色的方法有所不同。当一个片段通过深度测试后,并不是直接取代后缓冲区中P点的当前颜色,而是通过一定的比例因子与之进行插值(混合),并将结果作为P点的当前值。当然,未通过深度测试的片段依然被抛弃。这个是“混合”的一种最简单例子。除此之外,D3D11针对混合阶段有非常多的配置,从而实现各种特殊效果。

           1. 混合方程

           学习混合的第一步,就是要了解混合方程,方程如下:

           

           该方程针对每个像素逐一进行。方程左边的C为混合结果,右边Csrc(我们称为源颜色)和Cdst(我们称为目标颜色)分别为即将要处理的片段的颜色和后缓冲区中该像素当前的颜色;Fsrc和Fdst分别是两个颜色对应的混合因子。注意方程中的在这里为“分量相乘”(Componentwise multiplication),即针对颜色值中的R、G、B三种分量分别进行相乘。源颜色和目标颜色分别与相应的混合因子分量相乘后,将结果进行“op"(混合操作)操作,作为当前片段处理的最终颜色,并替换后缓冲区中该像素处的颜色。

           以上计算公式只适合于颜色值中RGB三个分量。此外,Alpha分量的计算公式完全一致:

           

           2. 混合操作

    针对上述公式中的混合操作”op",在D3D11中定义在如下枚举类型中:

    [cpp] view plain copy
    1. typedef enum D3D11_BLEND_OP {  
    2.   D3D11_BLEND_OP_ADD            = 1,  
    3.   D3D11_BLEND_OP_SUBTRACT       = 2,  
    4.   D3D11_BLEND_OP_REV_SUBTRACT   = 3,  
    5.   D3D11_BLEND_OP_MIN            = 4,  
    6.   D3D11_BLEND_OP_MAX            = 5   
    7. } D3D11_BLEND_OP;  

    我们省略各个变量的前缀D3D11_BLEND_OP_。

    ADD表示相加操作,即

    SUBTRACT表示相关(目标-源),即

    REV_SUBTRACT表示反射的相关(源-目标),即

    MIN表示取源、目标颜色中较小值,即

    MAX表示取源、目标颜色中较大值,即

    注意:MIN和MAX操作与混合因子无关。

    同样,所有这些操作也适合于计算Alpha值。

    3. 混合因子

    针对方程中的混合因子F,在D3D11中有如下几种:

    D3D11_BLEND_ZERO:此外针对颜色混合,F为(0,0,0),针对alpha值,F为0。

    D3D11_BLEND_ONE:针对颜色混合为(1,1,1),针对alpha值为1;

    D3D11_BLEND_SRC_COLOR:针对颜色混合为(Rs,Gs, Bs),针对alpha值为As;

    D3D11_BLEND_INV_SRC_COLOR:针对颜色混合为(1-Rs,1-Gs,1-Bs),针对alpha值为1-As;

    D3D11_BLEND_SRC_ALPHA:针对颜色混合为(As,As,As),针对alpha值为As;

    D3D11_BLEND_INV_SRC_ALPHA:针对颜色混合为(1-As,1-As,1-As),针对alpha值为1-As;

    D3D11_BLEND_DEST_COLOR:针对颜色混合为(Rd,Gd,Bd),针对alpha值为Ad;

    D3D11_BLEND_INV_DESC_COLOR:针对颜色混合为(1-Rd,1-Gd,1-Bd),针对alpha值为1-Ad;

    D3D11_BLEND_DEST_ALPHA:针对颜色混合为(Ad,Ad,Ad),针对alpha值为Ad;

    D3D11_BLEND_INV_DEST_ALPHA:针对颜色混合为(1-Rd,1-Rd,1-Rd),针对alpha值为1-Ad;

    D3D11_BLEND_BLEND_FACTOR:此时的混合因子为程序员指定的颜色值(R,G,B,A),针对颜色混合为(R,G,B),针对alpha值为A。该颜色值通过函数ID3D11DeviceContext::OMSetBlendState来指定;

    D3D11_BLEND_INV_BLEND_FACTOR:同上,为程序员指定颜色值,针对颜色混合为(1-R,1-G,1-B),针对alpha值为1-A。

    4. 混合状态

    D3D11中,设置混合状态前要先创建相应的混合状态接口ID3D11BlendState,创建函数如下:

    [cpp] view plain copy
    1. HRESULT CreateBlendState(  
    2.   [in]   const D3D11_BLEND_DESC *pBlendStateDesc,  
    3.   [out]  ID3D11BlendState **ppBlendState  
    4. );  

    第二个参数为要创建的接口的地址,第一个参数为一个用来描述混合状态参数的结构,定义如下:

    [cpp] view plain copy
    1. typedef struct D3D11_BLEND_DESC {  
    2.   BOOL                           AlphaToCoverageEnable;  
    3.   BOOL                           IndependentBlendEnable;  
    4.   D3D11_RENDER_TARGET_BLEND_DESC RenderTarget[8];  
    5. } D3D11_BLEND_DESC;  


    第一个参数设置是否打开AlphaToCoverage,AlphaToCoverage在后面会详细介绍,暂时先不用,设置为false;

    第二个参数设置是否针对不同的RenderTarget使用不同的混合状态。在D3D11中,一共可以支持多达8个的渲染对象(RenderTarget),如果针对不同的对象想使用不同的混合方式,则设置为true。由于我们暂时用不到,因此设置为false;

    第三个参数为针对8个RenderTarget分别指定的混合状态参数,当第二个参数为false时,这里我们只需要设置数组中第一个元素即可。

    D3D11_RENDER_TARGET_BLEND_DESC结构定义如下:

    [cpp] view plain copy
    1. typedef struct D3D11_RENDER_TARGET_BLEND_DESC {  
    2.   BOOL           BlendEnable;  
    3.   D3D11_BLEND    SrcBlend;  
    4.   D3D11_BLEND    DestBlend;  
    5.   D3D11_BLEND_OP BlendOp;  
    6.   D3D11_BLEND    SrcBlendAlpha;  
    7.   D3D11_BLEND    DestBlendAlpha;  
    8.   D3D11_BLEND_OP BlendOpAlpha;  
    9.   UINT8          RenderTargetWriteMask;  
    10. } D3D11_RENDER_TARGET_BLEND_DESC;  

    第一个需要设置为true,以开启混合状态;

    后面的参数即我们的混合方程中的参数:

    SrcBlend、DestBlend分别为源、目标颜色混合因子;

    BlendOp为源、目标颜色的混合操作;

    SrcBlendAlpha、DestBlendAlpha为源、目标alpha值的混合因子;

    BlendOpAlpha为源、目标alpha值的混合操作;

    RenderTargetWriteMask为最终混合结果在写到缓冲区时的掩码,即用来指定哪些位写进去,哪些们不能写。针对该参数有如下枚举类型:

    [cpp] view plain copy
    1. typedef enum D3D11_COLOR_WRITE_ENABLE {  
    2.   D3D11_COLOR_WRITE_ENABLE_RED     = 1,  
    3.   D3D11_COLOR_WRITE_ENABLE_GREEN   = 2,  
    4.   D3D11_COLOR_WRITE_ENABLE_BLUE    = 4,  
    5.   D3D11_COLOR_WRITE_ENABLE_ALPHA   = 8,  
    6.   D3D11_COLOR_WRITE_ENABLE_ALL     =   
    7.       ( D3D11_COLOR_WRITE_ENABLE_RED | D3D11_COLOR_WRITE_ENABLE_GREEN |    
    8.         D3D11_COLOR_WRITE_ENABLE_BLUE | D3D11_COLOR_WRITE_ENABLE_ALPHA )   
    9. } D3D11_COLOR_WRITE_ENABLE;  

    其中,RED、GREEN、BLUE和ALPHA分别表示只允许写入R、G、B、A部分的值。默认情况下,我们把带个颜色值替换缓冲区中的值,因此该参数我们指定为D3D11_COLOR_WRITE_ENABLE_ALL。

    创建好ID3D11BlendState接口后,通过以下函数来设置为指定的状态:

    [cpp] view plain copy
    1. void OMSetBlendState(  
    2.   [in]  ID3D11BlendState *pBlendState,  
    3.   [in]  const FLOAT* BlendFactor,  
    4.   [in]  UINT SampleMask  
    5. );  

    第一个参数即创建的接口;

    第二个参数为程序员手动指定的混合因子,即刚介绍混合因子时,如果指定参数为D3D11_BLEND_BLEND_FACTOR或D3D11_BLEND_INV_BLEND_FACTOR,则使用第二个参数指定的颜色值为了混合因子;

    第三个参数为采样点掩码。D3D11中的多重采样可以支持32个采样点,该参数用来决定”使用/丢弃"哪些采样点。该参数类型为UINT,32位,其中从最低位到最高位分别代表一个采样点。比如,如果第5位指定为0,则第5个采样点将被丢弃。当然,只有当开启至少5重采样时该设定才有效。如果当前设置为单个采样点,则只有最低位才对我们有用。默认情况下,该参数值为0xFFFFFFFF,即对所有采样点有效。

    Voila,使用混合的所有步骤就这些~ 初次接触是不是觉得有点头晕?有太多的结构要填,参数名又非常长! 其实一点也不难,参数名长有一个很大的好处,就是“自解释”,看到名字一下子就能明白其用意,而且还很容易记住。只要理解了混合的基本原理,这些过程其实是一气呵成的事。以下是一个开启混合的例子:

    [cpp] view plain copy
    1. //开启透明  
    2. D3D11_BLEND_DESC transDesc;  
    3. //先创建一个混合状态的描述  
    4. transDesc.AlphaToCoverageEnable = false;        //关闭AlphaToCoverage  
    5. transDesc.IndependentBlendEnable = false;       //不针对多个RenderTarget使用不同的混合状态  
    6. //因此只设置第一个数组元素即可  
    7. transDesc.RenderTarget[0].BlendEnable = true;  
    8. transDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;  
    9. transDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;  
    10. transDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;  
    11. transDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;  
    12. transDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;  
    13. transDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;  
    14. transDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;  
    15. //创建ID3D11BlendState接口  
    16. device->CreateBlendState(&transDesc,&TransparentBS);  
    17.   
    18. //现在可以设置混合状态了  
    19. //先指定混合因子,一般不用它,除非在上面混合因子指定为使用blend factor  
    20. float factor[4] = {1.f,1.f,1.f,1.f};  
    21. //使用该状态  
    22. deviceContext->OMSetBlendState(TransparentBS,factor,0xffffffff); //32个采样点都有效  

    渲染完后,一般要恢复默认状态,这时使用NULL参数即可,如下:

    [cpp] view plain copy
    1. deviceContext->OMSetBlendState(0,factor,0xffffffff);  


    5. 几种混合的例子

    在D3D中通过合理地设置不同的混合状态,可以实现各种各样的效果。以下是几个常见的例子:

    5.1 禁止颜色写入

    有时候,在渲染过程中,我们只希望修改深度/模板缓冲区部分,而且希望保持后缓冲区中的原有颜色值,这时修,我们需要“禁止颜色写入”。

    一种方法是把目标混合因子设为D3D11_BLEND_ONE,把源混合因子设为D3D11_BLEND_ZERO,这样混合方程中源部分相乘后结果为0,目标部分相乘后保持原样,相加结果仍为原来的颜色,这样即禁止了颜色的写入;还有另一种更为直观的方法是,直接把描述中D3D11_RENDER_TARGET_BLEND_DESC::RenderTargetWriteMask成员设为0,即任何一位都无法写入。

    5.2 把颜色相加、相减

    在处理片段时,如果我们希望把片段颜色与后缓冲区中当前颜色值相加。这时,可以通过把源、目标混合因子全部设为D3D11_BLEND_ONE。这样,在乘以相应的混合因子后,源、目标颜色保持不变。对于混合操作,如果是实现相加,可以设置为D3D11_BLEND_OP_ADD,如果为相减,可以设置为D3D11_BLEND_OP_SUBTRACT或D3D11_BLEND_OP_REV_SUBTRACT(取决于源颜色值减目标颜色值,还是相反)。

    5.3 把颜色相乘

    如果希望把片段颜色值与后缓冲区中对应的当前值相乘,可以设置目标混合因子为D3D11_BLEND_SRC_COLOR,而把源混合因为设为D3D11_BLEND_ZERO。这样,混合方程中源部分变为0,对于目标部分,由于混合因子是片段的颜色值,因为目标颜色乘以混合因子,实际上就是目标颜色与源颜色相乘了。至于混合操作,设置为相加即可,即D3D11_BLEND_OP_ADD。

    5.4 透明效果

    有时候,我们需要渲染透明的物体,比如玻璃。透过玻璃,我们可以看见其后面的物体。一般情况下,实现透明效果时,我们需要用到该物体的alpha值。比如对于alpha为0.4,意思是该物体60%透明,即最终我们观察到的颜色40%来自该物体,60%来自其后面的物体。要实现这种效果,我们可以为源颜色指明混合因子为D3D11_BLEND_SRC_ALPHA,即该片段对应的alpha值,目标因子设为D3D11_BLEND_INV_ALPHA。混合操作设置相加:D3D11_BLEND_OP_ADD。这时,比如透明物体的alpha值为0.25,即源混合因子为0.25,目标混合因子为0.75。

    6. 使用“透明”效果时的注意事项

    在场景中包含透明物体时需要额外注意的是,要先渲染不透明物体,然后把透明物体由远到近逐个渲染。

    原因很简单,如果很渲染了透明物体,这样当渲染该透明物体后面的物体时,将无法通过深度测试而被丢弃,从而导致无法再看到透明物体后面的物体。

     

    7. 示例程序

    在本节的示例程序中,演示的是水面的透明效果。场景为一个很大的水池,透过水面,可以看到水池底部以及水中的物体。当然,这里的水面仅仅是静态的水面,没有任何波动效果。以下是程序截图:

    操作说明:鼠标左键按下旋转屏幕,右键按下调整镜头远近。

    以下是示例程序源代码:

    D3D11“透明"效果示例程序

  • 相关阅读:
    天梯赛5-12 愿天下有情人都是失散多年的兄妹 【dfs】
    poj2718 Smallest Difference【贪心】
    HDU problem 5635 LCP Array【思维】
    codeforces 782C Andryusha and Colored Balloons【构造】
    HDU 4278 Faulty Odometer【进制转换】
    codeforces B. The Meeting Place Cannot Be Changed【二分】
    POJ 3264 Balanced Lineup 【线段树】
    HDU 1850
    CodeForces-714C
    HDU Problem 1247 Hat's Words 【字典树】
  • 原文地址:https://www.cnblogs.com/wodehao0808/p/6603925.html
Copyright © 2011-2022 走看看