Direct3D Blending融合技术

来源:互联网 发布:js 日期与时间戳转换 编辑:程序博客网 时间:2024/04/28 15:42

融合仅仅是融合就是对将要渲染的源物体,和后台缓存中已经写入的像素进行融合,颜色(alpha和颜色都可以)融合可以设置颜色融合比例,颜色融合也可以屏蔽全部源像素写入目标后台缓存。

融合公式:OuputPixel = SoucePixel xSourceBlendFactor + DestPixel x DestBlendFactor;
每个分量都是一个4元颜色向量,alpha分量也要做这样的分量乘法,但是后面alpha值被相加了若没有启用alpha测试那么不影响结果,如果启用了那么没有启用融合前后的,alpha值符合要求的才会绘制出来。
尽管可以调节SourceBlendFactor、DestBlendFactor融合因子和融合方式(默认是D3DBLENDOP_ADD),但是融合被限定了就在后台缓存和将要绘制的物体之间进行的,所以它和丰富的高级多阶段纹理之间是不同的,多阶段纹理是在纹理缓存和顶点多边形之间的决定一个物体的颜色的。而融合,深度测试,alpha测试,模板测试都是用于在渲染物体和后台缓存(alpha值,深度缓存和模板缓存)之间,决定是否渲染物体像素和如何渲染物体的。
更多的融合操作和融合方式见:
https://msdn.microsoft.com/en-us/library/windows/desktop/bb172508%28v=vs.85%29.aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/bb172509%28v=vs.85%29.aspx
融合操作是比较影响性能的尽量批量渲染,渲染完后马上关闭,alpha测试也比较影响,模板测试却不会,多阶段纹理也不会影响性能。
融合是在不同的物体之间,为了得到融合的层次是从里到外的,需要在摄像机坐标系中(摄像机中效率更高些或者世界坐标系中)将物体进行排序,从里到外提交渲染物体的三角网格或粒子等图形数据。
 
模板测试,可以通过前面的写入模板缓存,然后通过模板测试部分的写入后台缓存(镜面镜像,阴影等)且可以防止二次融合。

alpha融合原理:
alpha混合技术,主要是用于实现半透明的效果, 假设一种不透明东西的颜色是A(即VIDEO1),另一种透明的东西的颜色是B(即OSD0),那么透过B去看A,看上去的颜色C(即attribute)就是B和A的混合颜色,可以用下面的式子来近似,设B物体的透明度为alpha(取值为0~1之间的浮点数,0为完全透明,1为完全不透明)[1]。

    R(C)=alpha*R(B)+(1-alpha)*R(A)
    G(C)=alpha*G(B)+(1-alpha)*G(A)
    B(C)=alpha*B(B)+(1-alpha)*B(A)

    alpha混合可以实现火光、烟雾、阴影、动态光源等等一切你可以想象的出来的半透明效果。
绘制先的物体称为目标像素,绘制后的物体称为源像素,只要在源像素物件上设置包含alpha的材质或者包含alpha的texture即可。
计算的时候就会用公式:
RGBOut = (1 - alpha) * destRGB + alpha * srcRGB计算最终像素。
alpha越大,越不透明,前面物体的颜色越明显。
至于设置alpha融合时候,是否需要按照深度排序下物体,再提交给渲染引擎,应该是需要排序的,以确定alpha混合的效果。

实现代码,当blendingRate(D3DBLEND_SRCALPHA)为0.5时候,其它颜色也可以用来作为alpha计算(这个是时候就不是alpha数乘颜色了,应该是叉乘颜色了),
例如D3DBLEND_SRCCOLOR时候:
float blendingRate = 0.5f;  
    private void processPixels(int[] inPixelsOne, int[] inPixelsTwo, int[] outPixelsData, int width, int height) {  
        int index = 0;  
        for(int row=0; row<height; row++) {  
            for(int col=0; col<width; col++) {  
                int ta = 0, tr = 0, tg = 0, tb = 0;  
                int rgb1 = inPixelsOne[index];  
                int rgb2 = inPixelsTwo[index];  
                ta = ((rgb1 >> 24) & 0xff) + ((rgb2 >> 24) & 0xff);  
                tr = ((rgb1 >> 16) & 0xff) + ((rgb2 >> 16) & 0xff);  
                tg = ((rgb1 >> 8) & 0xff) + ((rgb2 >> 8) & 0xff);  
                tb = (rgb1 & 0xff) + (rgb2 & 0xff);  
                 
                int a = 0, r=0, g=0, b=0;  
                a = (int)(blendingRate *(float)ta);  
                r = (int)(blendingRate *(float)tr);  
                g = (int)(blendingRate *(float)tg);  
                b = (int)(blendingRate *(float)tb);  
                 
                outPixelsData[index] = ((a << 24) & 0xFF000000)  
                | ((r << 16) & 0x00FF0000)  
                | ((g << 8) & 0x0000FF00)  
                | ((b) & 0x000000FF);  
                index++;  
            }  
        }  
         
    } 

// use alpha channel in texture for alpha
    // 1.设置纹理来源和指定alpha融合操作
    // D3DTA_DIFFUSE alpha来自于顶点颜色[指定顶点颜色或者计算顶点颜色]
    // D3DTA_TEXTURE指定顶点的颜色来自于贴图纹理的alpha分量
    Device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
    Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);


    // set blending factors so that alpha component determines transparency
    // 2.融合因子设置
    Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
    Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

// draw cube
        // 3. 融合操作性能开销比较大,开启后需要马上关闭
        Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
        if(Box)
            Box->draw(&CubeWorldMatrix, 0, CrateTex);
        Device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);

        Device->EndScene();
        Device->Present(0, 0, 0, 0);

一、基础概念

1.  融合公式

将正在提交到表面渲染的像素,和后台缓存表面已经光栅化的像素进行融合。融合的时候主要对alpha透明通道进行融合,同是其它颜色通道也会进行融合。

融合公式:

OuputPixel = SoucePixel xSourceBlendFactor + DestPixel x DestBlendFactor;


x符号是颜色向量的乘法,会对各个颜色分量分别相乘,得到结果。

SoucePixel是当前操作的像素,DestPixel是已经光栅化渲染到后台缓存表面的像素。

SourceBlendFactor和DestBlendFactor是融合因子,默认是 D3DBLEND_SRCALPH(As,As,As,As),D3DBLEND_INVSRCALPHA值是

(1-As, 1-As, 1-As, 1-As)。也可以用其它的融合因子,那么需要指定,详见MSDN。

指明融合因子:

// set blending factors sothat alpha component determines transparency

Device->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);

Device->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);

2.图像透明度和来源

Alpha分量主要用于透明度,alpha分量是[0,255]对应于透明度[0%,100%],0时候是完全透明,255时候是完全不透明。

1).来自于材质漫反射的透明度:

TeapotMtrl = d3d::RED_MTRL;

TeapotMtrl.Diffuse.a = 0.5f;// set alpha to 50% opacity,当为0.0f时候,后面渲染上去的物体将会完全看不到。

2).来自于纹理的透明度:

如果是来自于图片纹理的,那么需要将纹理转换格式为包含alpha的例如A8R8G8B8,且需要将一个alpha通道文件附加到该图片纹理上。附加上去后的图片应该只是填充了纹理图片的alpha通道(不需要考虑颜色),当这个源纹理和目标纹理融合的时候就会看到透明度,当前色和背后颜色,透明度为0就可以去掉源颜色,透明度为1可以去掉底色(目标颜色),而对于剔除组合两方颜色的用模板更加灵活.

 

我们往往不直接计算alpha通道值,而是从纹理的alpha通道获取alpha信息。如果当前设置的纹理有alpha通道,那么alpha通道就来自于alpha通道,如果没有alpha通道,那么alpha值来自于顶点的颜色,但是也可以指定alpha值的来源,来自材质漫反射值或是alpha通道:

指定来自材质漫反射:

// use alpha inmaterial's diffuse component for alpha

// 设置指定纹理层级的阶段情况(这里是融合)的第一个参数的Alpha来源

Device->SetTextureStageState(0,D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);

// 设置指定纹理层级的不透明度来自于纹理阶段情况的第一个参数

Device->SetTextureStageState(0,D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);

指定来自纹理alpha通道:

// use alpha in material'sdiffuse component for alpha

Device->SetTextureStageState(0,D3DTSS_ALPHAARG1, D3DTA_TEXTURE);

Device->SetTextureStageState(0,D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);

HRESULT SetTextureStageState(  [in] DWORD                    Stage,  [in] D3DTEXTURESTAGESTATETYPE Type,  [in] DWORD                    Value);

Stage [in]

Type: DWORD

Stage identifier of the texture for which the state value is set. Stage identifiers are zero-based. Devices can have up to eight set textures, so the maximum value allowed for Stage is 7.

Type [in]

Type: D3DTEXTURESTAGESTATETYPE

Texture state to set. This parameter can be any member of the D3DTEXTURESTAGESTATETYPE enumerated type.

Value [in]

Type: DWORD

State value to set. The meaning of this value is determined by the Type parameter.

第一个参数是需要采样属性的纹理层,纹理层从[0,7]共八层。
第二个参数D3DTEXTURESTAGESTATETYPE 
Type取值: 
typedef enum _D3DTEXTURESTAGESTATETYPE
     D3DTSS_COLOROP = 1,                       //纹理层的颜色混合方式 
     D3DTSS_COLORARG1 = 2,                          //颜色混合的第一个参数                                    
     D3DTSS_COLORARG2 = 3,                          //颜色混合的第二个参数 
 
     D3DTSS_ALPHAOP = 4,                            //指定纹理层的Alpha透明, OP是opacity不透明程度。
     D3DTSS_ALPHAARG1 = 5,                          //Alpha混合的第一个参数,ARG1是参数1,ARG2是参数2
     D3DTSS_ALPHAARG2 = 6,                          //Alpha混合的第二个参数
第三个参数是第二个参数的值。
typedef enum D3DTEXTUREOP {   D3DTOP_DISABLE                    = 1,  D3DTOP_SELECTARG1                 = 2,  D3DTOP_SELECTARG2                 = 3,
D3DTOP_DISABLE

Disables output from this texture stage and all stages with a higher index. To disable texture mapping, set this as the color operation for the first texture stage (stage 0). Alpha operations cannot be disabled when color operations are enabled. Setting the alpha operation to D3DTOP_DISABLE when color blending is enabled causes undefined behavior.

D3DTOP_SELECTARG1

Use this texture stage's first color or alpha argument, unmodified, as the output. This operation affects the color argument when used with the D3DTSS_COLOROP texture-stage state, and the alpha argument when used with D3DTSS_ALPHAOP.

Equation of this argument (S(RGBA) = Arg1)

D3DTOP_SELECTARG2

Use this texture stage's second color or alpha argument, unmodified, as the output. This operation affects the color argument when used with the D3DTSS_COLOROP texture stage state, and the alpha argument when used with D3DTSS_ALPHAOP.

Equation of this argument (S(RGBA) = Arg2)



3.开启融合运算

默认是禁用融合运算的,开启是:

// Draw the teapot

Device->SetRenderState(D3DRS_ALPHABLENDENABLE,true);

// 关闭融合计算

Device->SetRenderState(D3DRS_ALPHABLENDENABLE,false);

因为融合运算还是比较耗运算性能的,在每帧中必要的地方才开启融合,计算完后马上关闭;或者在渲染三角形单元组的时候,最好进行批处理,之后立即绘制出来,尽量避免在每帧中都开启和关闭融合(应该指渲染时候尽量开一次->批处理渲染->关一起)。

 

二、应用实例

D3DMATERIAL9 TeapotMtrl;

bool Setup()

{

    TeapotMtrl = d3d::RED_MTRL;

    TeapotMtrl.Diffuse.a = 0.5f; // 1.set alpha to 50% opacity 启用alpha分量

    BkGndMtrl = d3d::WHITE_MTRL;

 

    // Set alpha blending states.

    //

 

    // use alpha in material's diffuse component for alpha

    Device->SetTextureStageState(0, D3DTSS_ALPHAARG1,D3DTA_DIFFUSE); // 2.设置alpha来源

    Device->SetTextureStageState(0, D3DTSS_ALPHAOP,D3DTOP_SELECTARG1);

    // use alpha channel in texture for alpha

    //Device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);

    //Device->SetTextureStageState(0, D3DTSS_ALPHAOP,D3DTOP_SELECTARG1);

 

    // set blending factors so that alpha component determinestransparency

    Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); // 3.设置融合因子

    Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

}

 

bool Display()

{

        Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,0xffffffff, 1.0f, 0);

        Device->BeginScene();

 

        // Draw the background

        D3DXMATRIX W;

        D3DXMatrixIdentity(&W);

        Device->SetTransform(D3DTS_WORLD, &W);

        Device->SetFVF(Vertex::FVF);

        Device->SetStreamSource(0, BkGndQuad, 0, sizeof(Vertex));

        Device->SetMaterial(&BkGndMtrl);

        Device->SetTexture(0, BkGndTex);

        Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);

 


        // Draw the teapot

        Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true); // 4.启用

 

        D3DXMatrixScaling(&W, 1.5f, 1.5f, 1.5f);

        Device->SetTransform(D3DTS_WORLD, &W);

        Device->SetMaterial(&TeapotMtrl);

        Device->SetTexture(0, 0);

        Teapot->DrawSubset(0); 

 

        Device->SetRenderState(D3DRS_ALPHABLENDENABLE, false); // 融合完马上关闭

 

        Device->EndScene();

        Device->Present(0, 0, 0, 0);

}

0 0