`

深度测试与alpha混合

 
阅读更多

from: http://www.opengpu.org/forum.php?mod=viewthread&tid=479

 

深度测试与alpha混合(1)

在绘制复杂的三维场景时,不可避免地会出现物体间的相互遮挡,在这种情况下,为了正确地绘制场景需要使用深度测试。半透明 物体的绘制不同于不透明物体,Direct3D通过alpha混合实现半透明物体的绘制。深度测试可以简化复杂场景的绘制,alpha混合可以使绘制的三 维场景更完整、更逼真。

在复杂的场景中,通常有多个物体需要绘制,这些物体之间通常会存在遮挡关系,离观察点较远的物体会因为近处物体的者的遮挡而不可见或只有部分可见,Direct3D图形系统提供了深度测试功能来实现这种效果。

 

深度缓冲区与深度测试

要理解深度测试,首先需要理解深度缓冲区。深度缓冲区是Direct3D用来存储绘制到屏幕上的每个像素点的深度信息的一块内存缓冲区。当 Direct3D将一个场景渲染到目标表面上时,它使用深度缓冲区来决定光栅化后各个多边形的像素的前后遮挡关系,最终决定哪个颜色值被绘制出来。也就是 说,Direct3D通过比较当前绘制的像素点的深度和对应深度缓冲区的点的深度值来决定是否绘制当前像素。如果深度测试结果为TRUE,则绘制当前像 素,并用当前像素点的深度来更新深度缓冲区,反之则不予绘制。通常情况下,深度缓冲区对应于屏幕大小的一块二维区域。

对一个启用了深度缓冲区的场景进行光栅化操作时,渲染表面上的每个点都要进行深度测试。在深度测试开始时,深度缓冲区的深度值被设置为该场景可能出现的最 大值,渲染表面上的颜色值被设置为背景颜色值。然后测试场景内即将绘制的每个多边形,看它是否小于存储在深度缓冲区中的深度值,如果该多边形的深度值更 小,则该深度值被更新到深度缓冲区中,并将渲染表面上当前点的颜色值替换为该多边形的颜色。如果多边形在这一点的深度值更大,将继续测试列表中的下一个多 边形。

 

创建深度缓冲区

若要在Direct3D图形程序中应用深度测试,首先必须在创建Direct3D渲染设备时创建深度缓冲区,示例代码如下:

 

 

D3DPRESENT_PARAMETERS d3dpp;ZeroMemory(&d3dpp, sizeof(d3dpp));

d3dpp.Windowed  = TRUE;d3dpp.SwapEffect                        
                           = D3DSWAPEFFECT_DISCARD;d3dpp.BackBufferFormat               
                           = D3DFMT_UNKNOWN;d3dpp.EnableAutoDepthStencil        
                           = TRUE;                   // 表示由Direct3D创建并管理一个深度缓冲区d3dpp.AutoDepthStencilFormat        
                           = D3DFMT_D16;     // 表示深度缓冲区中每一个像素的深度值由16位的二进制数表示

if(FAILED(g_d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,
                                               D3DCREATE_SOFTWARE_VERTEXPROCESSING,                                                                  
                                               &d3dpp, &g_device))){
    return false;
}

 

 

激活深度测试

深度缓冲区随Direct3D渲染设备创建好后,调用Direct3D渲染状态设置函数IDirect3DDevice9::SetRenderState(),将第一个参数设为D3DRS_ZENABLE,第二个参数设为TRUE,激活深度测试:

g_device->SetRenderState(D3DRS_ZENABLE,    TRUE);

 

设置深度测试函数

接下来依然调用IDirect3DDevice9::SetRenderState()函数设置深度测试函数,第一个参数设置为D3DRS_ZFUNC:

D3DRS_ZFUNC

One member of the D3DCMPFUNC enumerated type. The default value is D3DCMP_LESSEQUAL. This member enables an application to accept or reject a pixel, based on its distance from the camera.

The depth value of the pixel is compared with the depth-buffer value. If the depth value of the pixel passes the comparison function, the pixel is written.

The depth value is written to the depth buffer only if the render state is TRUE.

Software rasterizers and many hardware accelerators work faster if the depth test fails, because there is no need to filter and modulate the texture if the pixel is not going to be rendered.

 

第二个参数设置为想要设置的深度测试函数,它属于D3DCMPFUNC枚举类型,定义如下:

Defines the supported compare functions.

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;


Constants

D3DCMP_NEVER
Always fail the test.

D3DCMP_LESS
Accept the new pixel if its value is less than the value of the current pixel.

D3DCMP_EQUAL
Accept the new pixel if its value equals the value of the current pixel.

D3DCMP_LESSEQUAL
Accept the new pixel if its value is less than or equal to the value of the current pixel.

D3DCMP_GREATER
Accept the new pixel if its value is greater than the value of the current pixel.

D3DCMP_NOTEQUAL
Accept the new pixel if its value does not equal the value of the current pixel.

D3DCMP_GREATEREQUAL
Accept the new pixel if its value is greater than or equal to the value of the current pixel.

D3DCMP_ALWAYS
Always pass the test.

D3DCMP_FORCE_DWORD
Forces this enumeration to compile to 32 bits in size. Without this value, some compilers would allow this enumeration to compile to a size other than 32 bits. This value is not used.
Remarks

The values in this enumerated type define the supported compare functions for the D3DRS_ZFUNC, D3DRS_ALPHAFUNC, and D3DRS_STENCILFUNC render states.

通常情况下,深度测试函数设置为D3DCMP_LESS,表示当测试点深度值小于深度缓冲区中相应值时,通过深度测试并绘制相关像素,这样没有被遮挡的物体才显示,而被遮挡的物体就不显示。示例代码如下:

g_device->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESS);

更新深度缓冲区

设置了深度测试函数后,还需要设置深度测试成功时对深度缓冲区如何操作,是保持原来的深度值,还是用当前像素的深度值更新对应的数值。

D3DRS_ZWRITEENABLE

TRUE to enable the application to write to the depth buffer. The default value is TRUE. This member enables an application to prevent the system from updating the depth buffer with new depth values. If FALSE, depth comparisons are still made according to the render state D3DRS_ZFUNC, assuming that depth buffering is taking place, but depth values are not written to the buffer.


示例代码如下:

g_device->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);

表示如果通过测试,则用当前像素的深度值更新深度缓冲区中对应的数值,这是最常用的设置,也是默认设置。

示例程序

该示例程序绘制了一个矩形和一个坦克模型,其中先绘制矩形,再绘制坦克模型,而且坦克模型在矩形的后面被遮挡住。如果按下"1"键,则禁用深度测试,这时 坦克虽然被矩形遮挡住,但仍然被绘制出来了;如果按下"2"键,则启用深度测试,这时坦克模型被矩形遮挡住,没有绘制出来。

 



 按下数字键"1",禁用深度测试,被矩形部分遮住的坦克被绘制出来。

 



 按下数字键"2",启用深度测试,被矩形部分遮住的坦克没有被绘制出来。

 

 

#include <d3dx9.h>

#pragma warning(disable : 4127)

#define CLASS_NAME    "GameApp"

#define release_com(p)    do { if(p) { (p)->Release(); (p) = NULL; } } while(0)

IDirect3D9*                g_d3d;
IDirect3DDevice9*        g_device;

ID3DXMesh*                g_mesh;
D3DMATERIAL9*            g_mesh_materials;
IDirect3DTexture9**        g_mesh_textures;
DWORD                    g_num_materials;
IDirect3DVertexBuffer9* g_shutter_vb;

D3DXMATRIX                g_mat_tank;
D3DXMATRIX                g_mat_shutter;

struct sCustomVertex
{   
    float x, y, z;   
    DWORD color;     
};

#define D3DFVF_CUSTOM_VERTEX   (D3DFVF_XYZ | D3DFVF_DIFFUSE)

void setup_matrices()
{
    // setup world matrix
    D3DXMATRIX mat_world;
    D3DXMatrixIdentity(&mat_world);
    g_device->SetTransform(D3DTS_WORLD, &mat_world);

    // setup view matrix

    D3DXVECTOR3 eye(0.0f, 2.0f, -30.0f);
    D3DXVECTOR3 at(0.0f,  2.0f,   0.0f);
    D3DXVECTOR3 up(0.0f,  1.0f,   0.0f);

    D3DXMATRIX mat_view;
    D3DXMatrixLookAtLH(&mat_view, &eye, &at, &up);
    g_device->SetTransform(D3DTS_VIEW, &mat_view);

    // setup projection matrix
    D3DXMATRIX mat_proj;
    D3DXMatrixPerspectiveFovLH(&mat_proj, D3DX_PI/4, 1.0f, 1.0f, 500.0f);
    g_device->SetTransform(D3DTS_PROJECTION, &mat_proj);
}

bool init_geometry()
{
    ID3DXBuffer* material_buffer;

    /*
     D3DXLoadMeshFromXA(
        LPCSTR pFilename,
        DWORD Options,
        LPDIRECT3DDEVICE9 pD3DDevice,
        LPD3DXBUFFER *ppAdjacency,
        LPD3DXBUFFER *ppMaterials,
        LPD3DXBUFFER *ppEffectInstances,
        DWORD *pNumMaterials,
        LPD3DXMESH *ppMesh);
    */

    if(FAILED(D3DXLoadMeshFromX("tank.x", D3DXMESH_SYSTEMMEM, g_device, NULL, &material_buffer, NULL,
                                &g_num_materials, &g_mesh)))
    {
        MessageBox(NULL, "Could not find tank.x", "ERROR", MB_OK);
        return false;
    }

    D3DXMATERIAL* xmaterials = (D3DXMATERIAL*) material_buffer->GetBufferPointer();

    g_mesh_materials = new D3DMATERIAL9[g_num_materials];
    g_mesh_textures     = new IDirect3DTexture9*[g_num_materials];

    for(DWORD i = 0; i < g_num_materials; i++)
    {
        g_mesh_materials = xmaterials.MatD3D;

        // set ambient reflected coefficient, because .x file do not set it.
        g_mesh_materials.Ambient = g_mesh_materials.Diffuse;

        g_mesh_textures = NULL;

        if(xmaterials.pTextureFilename != NULL && strlen(xmaterials.pTextureFilename) > 0)   
            D3DXCreateTextureFromFile(g_device, xmaterials.pTextureFilename, &g_mesh_textures);   
    }

    material_buffer->Release();

    // create shutter vertex buffer

    sCustomVertex vertices[] =
    {
        { -8,   -2,  -10.0f,   0xFFFFFF00},     
        { -8,    6,  -10.0f,   0xFFFFFF00},   
        {  8,   -2,  -10.0f,   0xFFFFFF00},   
        {  8,    6,  -10.0f,   0xFFFFFF00}
    };

    g_device->CreateVertexBuffer(sizeof(vertices), 0, D3DFVF_CUSTOM_VERTEX, D3DPOOL_DEFAULT, &g_shutter_vb, NULL);
   
    void* ptr;
    g_shutter_vb->Lock(0, sizeof(vertices),  &ptr, 0);
    memcpy(ptr, vertices, sizeof(vertices));
    g_shutter_vb->Unlock();

    return true;
}

bool init_d3d(HWND hwnd)
{
    g_d3d = Direct3DCreate9(D3D_SDK_VERSION);

    if(g_d3d == NULL)
        return false;

    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory(&d3dpp, sizeof(d3dpp));

    d3dpp.Windowed                        = TRUE;
    d3dpp.SwapEffect                       = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat              = D3DFMT_UNKNOWN;
    d3dpp.EnableAutoDepthStencil     = TRUE;
    d3dpp.AutoDepthStencilFormat    = D3DFMT_D16;

    if(FAILED(g_d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,
                                                   D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                                   &d3dpp, &g_device)))
    {
        return false;
    }
   
    if(! init_geometry())
        return false;

    setup_matrices();   

    g_device->SetRenderState(D3DRS_ZENABLE,  TRUE);
    g_device->SetRenderState(D3DRS_ZFUNC,   D3DCMP_LESS);
    g_device->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);

    g_device->SetRenderState(D3DRS_AMBIENT, 0xFF00BB00);

    // rotate tank

    D3DXQUATERNION quat;
    D3DXMATRIX mat_rotation;

    D3DXMatrixIdentity(&g_mat_tank);

    D3DXQuaternionRotationYawPitchRoll(&quat, D3DX_PI/4, -D3DX_PI/4, 0.0f);
    D3DXMatrixRotationQuaternion(&mat_rotation, &quat);
    D3DXMatrixMultiply(&g_mat_tank, &mat_rotation, &g_mat_tank);

    D3DXMatrixIdentity(&g_mat_shutter);
   
    return true;
}

void cleanup()
{
    delete[] g_mesh_materials;

    if(g_mesh_textures)
    {
        for(DWORD i = 0; i < g_num_materials; i++)
            release_com(g_mesh_textures);
            delete[] g_mesh_textures;
    }
   
    release_com(g_mesh);
    release_com(g_shutter_vb);
    release_com(g_device);
    release_com(g_d3d);
}

void render()
{
    g_device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(5, 5, 5), 1.0f, 0);

    g_device->BeginScene();

    // render shutter
    g_device->SetRenderState(D3DRS_LIGHTING, FALSE);
    g_device->SetTransform(D3DTS_WORLD, &g_mat_shutter);
    g_device->SetStreamSource(0, g_shutter_vb, 0, sizeof(sCustomVertex));
    g_device->SetFVF(D3DFVF_CUSTOM_VERTEX);
    g_device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);

    // render tank
    g_device->SetRenderState(D3DRS_LIGHTING, TRUE);
    g_device->SetTransform(D3DTS_WORLD, &g_mat_tank);

    for(DWORD i = 0; i < g_num_materials; i++)
    {
        g_device->SetMaterial(&g_mesh_materials);
        g_device->SetTexture(0, g_mesh_textures);

        g_mesh->DrawSubset(i);
    }        
   
    g_device->EndScene();

    g_device-> Present(NULL, NULL, NULL, NULL);
}

LRESULT WINAPI WinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {
    case WM_KEYDOWN:
        switch(wParam)
        {
        case 49:                   // press key "1", disable depth test.
            g_device->SetRenderState(D3DRS_ZENABLE, FALSE);
            break;

        case 50:                   // press key "2", enable depth test.
            g_device->SetRenderState(D3DRS_ZENABLE, TRUE);
            break;

        case VK_ESCAPE:
            DestroyWindow(hwnd);
            break;
        }   

        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }

    return DefWindowProc(hwnd, msg, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE inst, HINSTANCE, LPSTR, INT)
{
    WNDCLASSEX wc;

    wc.cbSize                  = sizeof(WNDCLASSEX);
    wc.style                     = CS_CLASSDC;
    wc.lpfnWndProc         = WinProc;
    wc.cbClsExtra            = 0;
    wc.cbWndExtra          = 0;
    wc.hInstance             = inst;
    wc.hIcon                   = NULL;
    wc.hCursor                = NULL;
    wc.hbrBackground      = NULL;
    wc.lpszMenuName      = NULL;
    wc.lpszClassName      = CLASS_NAME;
    wc.hIconSm               = NULL;

    if(! RegisterClassEx(&wc))
        return -1;

    HWND hwnd = CreateWindow(CLASS_NAME, "Direct3D App", WS_OVERLAPPEDWINDOW,
                                                 200, 100, 640, 480, NULL, NULL, wc.hInstance, NULL);   

    if(hwnd == NULL)
        return -1;

    if(init_d3d(hwnd))
    {
        ShowWindow(hwnd, SW_SHOWDEFAULT);
        UpdateWindow(hwnd);

        MSG msg;
        ZeroMemory(&msg, sizeof(msg));

        while(msg.message != WM_QUIT)
        {
            if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
               
            render();
        }
    }

    cleanup();
    UnregisterClass(CLASS_NAME, wc.hInstance);   

    return 0;
}

 

 

 

深度测试与alpha混合(2)

 

前面的一些例子中,已经遇到了alpha值的概念,如结构体D3DCOLORVALUE中的成员变量a,但它并没有被使 用,因为还没有激活alpha混合(alpha blending),并且alpha总是被赋值为1.0f(默认渲染状态下alpha为1.0f,表示完全不透明)。

 

在前面介绍的示例程序中,绘制图形的颜色总是替换当前颜色缓冲区中存在的颜色,这样后面的物体总是覆盖在原有的物体上。但 是当想要绘制类似于玻璃、水等具有透明效果的物体时,这种方法显然满足不了要求。通过定义一个表示物体半透明度的alpha值和一个半透明计算公式,可以 将要绘制的物体颜色与颜色缓冲区中存在的颜色相混合,从而绘制出具有半透明效果的物体。Direct3D计算alpha颜色混合的方法如下:

          color = (RGBsrc * Ksrc) OP (RGBdst * Kdst)

其中color表示alpha混合后的颜色值,RGBsrc表示源颜色值,即将要绘制的图元的颜色值;Ksrc表示源混合系数,通常赋值为表示半透明程度 的alpha值,也可以是属于枚举类型D3DBLEND的任意值,用来和RGBsrc相乘。RGBdst表示目标颜色值,即当前颜色缓冲区中的颜色 值,Kdst表示目标混合系数,可以是属于枚举D3DBLEND的任意值,用来和RGBdst相乘。OP表示源计算结果与颜色缓冲区计算结果的混合方法, 默认状态下OP为D3DBLEND_ADD,即源计算结果与颜色缓冲区计算结果相加。

图形显示中,对alpha混合最普遍的用法是:把Ksrc赋值为D3DBLEND_SRCALPHA,即当前绘制像素的alpha值;把Kdst赋值为 D3DBLEND_INVSRCALPHA,即1减去当前绘制像素的alpha值;把OP赋值为D3DBLEND_ADD,使源计算结果和颜色缓冲区计算 结果相加,这样一来,alpha混合颜色的公式变为:

         color = (RGBsrc * Ksrc) + (RGBdst * Kdst)

上面的设置可以较好地模拟大多数半透明物体的效果。

 

启用alpha混合

想要绘制半透明物体,首先需要激活Direct3D的alpha混合运算,调用Direct3D渲染状态设置函数 IDirect3DDevice9:::SetRenderState(),将第一个参数设置为D3DRS_ALPHABLENDENABLE,第二个参 数设置为TRUE,可以激活alpha混合,代码如下:

g_device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);

 

置alpha混合系数

在上面介绍的alpha混合原理中提到的源混合系数和目标混合系数,也是通过Direct3D渲染状态设置函数 IDirect3DDevice9::SetRenderState()设置的。若将第一个参数设置为D3DRS_SRCBLEND,则用于设置源混合系 数,若将第一个参数设置为D3DRS_DESTBLEND,则用于设置目标混合系数,第二个参数可以设置为D3DBLEND枚举常量,各具体枚举常量的含 义如下:

Defines the supported blend mode.

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;


Constants

D3DBLEND_ZERO
Blend factor is (0, 0, 0, 0).

D3DBLEND_ONE
Blend factor is (1, 1, 1, 1).

D3DBLEND_SRCCOLOR
Blend factor is (Rs, Gs, Bs, As).

D3DBLEND_INVSRCCOLOR
Blend factor is (1 - Rs, 1 - Gs, 1 - Bs, 1 - As).

D3DBLEND_SRCALPHA
Blend factor is (As, As, As, As).

D3DBLEND_INVSRCALPHA
Blend factor is ( 1 - As, 1 - As, 1 - As, 1 - As).

D3DBLEND_DESTALPHA
Blend factor is (Ad Ad Ad Ad).

D3DBLEND_INVDESTALPHA
Blend factor is (1 - Ad 1 - Ad 1 - Ad 1 - Ad).

D3DBLEND_DESTCOLOR
Blend factor is (Rd, Gd, Bd, Ad).

D3DBLEND_INVDESTCOLOR
Blend factor is (1 - Rd, 1 - Gd, 1 - Bd, 1 - Ad).

D3DBLEND_SRCALPHASAT
Blend factor is (f, f, f, 1); where f = min(As, 1 - Ad).

D3DBLEND_BOTHSRCALPHA
Obsolete. Starting with DirectX 6, you can achieve the same effect by setting the source and destination blend factors to D3DBLEND_SRCALPHA and D3DBLEND_INVSRCALPHA in separate calls.

D3DBLEND_BOTHINVSRCALPHA
Source blend factor is (1 - As, 1 - As, 1 - As, 1 - As), and destination blend factor is (As, As, As, As); the destination blend selection is overridden. This blend mode is supported only for the D3DRS_SRCBLEND render state.

D3DBLEND_BLENDFACTOR
Constant color blending factor used by the frame-buffer blender. This blend mode is supported only if D3DPBLENDCAPS_BLENDFACTOR is set in the SrcBlendCaps or DestBlendCaps members of D3DCAPS9.

D3DBLEND_INVBLENDFACTOR
Inverted constant color-blending factor used by the frame-buffer blender. This blend mode is supported only if the D3DPBLENDCAPS_BLENDFACTOR bit is set in the SrcBlendCaps or DestBlendCaps members of D3DCAPS9.

D3DBLEND_SRCCOLOR2
Blend factor is (PSOutColor[1]r, PSOutColor[1]g, PSOutColor[1]b, not used). See Render Target Blending. Differences between Direct3D 9 and Direct3D 9Ex:

This flag is available in Direct3D 9Ex only.


D3DBLEND_INVSRCCOLOR2
Blend factor is (1 - PSOutColor[1]r, 1 - PSOutColor[1]g, 1 - PSOutColor[1]b, not used)). See Render Target Blending. Differences between Direct3D 9 and Direct3D 9Ex:

This flag is available in Direct3D 9Ex only.


D3DBLEND_FORCE_DWORD
Forces this enumeration to compile to 32 bits in size. Without this value, some compilers would allow this enumeration to compile to a size other than 32 bits. This value is not used.

Remarks

In the preceding member descriptions, the RGBA values of the source and destination are indicated by the s and d subscripts.

The values in this enumerated type are used by the following render states:

D3DRS_DESTBLEND
D3DRS_SRCBLEND
D3DRS_DESTBLENDALPHA
D3DRS_SRCBLENDALPHA
See D3DRENDERSTATETYPE

Render Target Blending

Direct3D 9Ex has improved text rendering capabilities. Rendering clear-type fonts would normally require two passes. To eliminate the second pass, a pixel shader can be used to output two colors, which we can call PSOutColor[0] and PSOutColor[1]. The first color would contain the standard 3 color components (RGB). The second color would contain 3 alpha components (one for each component of the first color).

These new blending modes are only used for text rendering on the first render target.

设置alpha混合系数的代码示例如下:

g_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
g_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

 

置alpha混合方法

alpha混合方法指定源颜色和目标颜色的混合方法,通过Direct3D渲染状态设置函数 IDirect3DDevice9::SetRenderState()设置,其中第一个参数设置为 D3DRS_BLENDOP,第二个参数设置为D3DBLENDOP枚举常量,各常量的含义如下:

Defines the supported blend operations. See Remarks for definitions of terms.

typedef enum D3DBLENDOP{
    D3DBLENDOP_ADD = 1,
    D3DBLENDOP_SUBTRACT = 2,
    D3DBLENDOP_REVSUBTRACT = 3,
    D3DBLENDOP_MIN = 4,
    D3DBLENDOP_MAX = 5,
    D3DBLENDOP_FORCE_DWORD = 0x7fffffff,
} D3DBLENDOP, *LPD3DBLENDOP;


Constants

D3DBLENDOP_ADD
The result is the destination added to the source. Result = Source + Destination

D3DBLENDOP_SUBTRACT
The result is the destination subtracted from to the source. Result = Source - Destination

D3DBLENDOP_REVSUBTRACT
The result is the source subtracted from the destination. Result = Destination - Source

D3DBLENDOP_MIN
The result is the minimum of the source and destination. Result = MIN(Source, Destination)

D3DBLENDOP_MAX
The result is the maximum of the source and destination. Result = MAX(Source, Destination)

D3DBLENDOP_FORCE_DWORD
Forces this enumeration to compile to 32 bits in size. Without this value, some compilers would allow this enumeration to compile to a size other than 32 bits. This value is not used.

Remarks

Source, Destination, and Result are defined as:
      Term      Type                             DescriptionSourceInputColor of the source pixel before the operation.DestinationInputColor of the pixel in the destination buffer before the operation.ResultOutputReturned value that is the blended color resulting from the operation.

This enumerated type defines values used by the following render states:

D3DRS_BLENDOP
D3DRS_BLENDOPALPHA


示例程序:

该示例程序模拟了直升飞机螺旋桨的半透明效果。在程序的初始化阶段,载入Heli.x文件,它是一个包含直升飞机的三维模型文件,其中螺旋桨的材质漫反射 属性为(R, G, B, A) = (0.183700; 0.183700; 0.183700; 0.500000; ),可以用文本方式打开Heli.x查看它的材质属性,Heli.x是一个文本格式的.x文件。

示例程序中没有设置alpha混合方法,所以应用程序将采用默认的alpha混合方法D3DBLEND_ADD,即将源计算结果与颜色缓冲区计算结果相加。由于直升机螺旋桨的材质的alpha值为0.5f,因此它的最终颜色就是50%的玻璃颜色加上50%的背景颜色。



 

按下数字键"1",激活alpha混合。



 
按下数字键"0",禁用alpha混合



 

设置alpha混合方法为D3DBLENDOP_SUBTRACT的效果图,启用alpha混合时。

g_device->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_SUBTRACT);



 

设置以下代码时的效果图,可以看到直升机变亮了,启用alpha混合:

g_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
g_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_DESTCOLOR);

 

 



 

 

设置以下代码时的效果图,可以看到直升机变暗了,启用alpha混合:

g_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
g_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVDESTCOLOR);



可以根据需要自己修改设置,以实现不同的颜色混合效果。



由于alpha混合是当前绘制的像素颜色与颜色缓冲区中存在的颜色的混合运算,因此,在绘制半透明物体前,必须保证位于半透明物体后的物体先于半透明物体绘制,也就是说,先绘制不透明物体,再绘制半透明物体。

有些计算机硬件由于功能的限制,可能不支持某些混合方法,这时Direct3D会自动使用alpha混合方法D3DBLENDOP_ADD。



源程序:

#include <d3dx9.h>

#pragma warning(disable : 4127)

#define CLASS_NAME    "GameApp"

#define release_com(p)    do { if(p) { (p)->Release(); (p) = NULL; } } while(0)

IDirect3D9*                g_d3d;
IDirect3DDevice9*        g_device;

ID3DXMesh*                g_mesh;
D3DMATERIAL9*            g_mesh_materials;
IDirect3DTexture9**        g_mesh_textures;
DWORD                    g_num_materials;

void setup_world_matrix()
{
    D3DXMATRIX mat_world;
    D3DXMatrixRotationY(&mat_world, timeGetTime() / 1000.0f);
    g_device->SetTransform(D3DTS_WORLD, &mat_world);
}

void setup_view_proj_matrices()
{
    // setup view matrix

    D3DXVECTOR3 eye(0.0f, 15.0f, -20.0f);
    D3DXVECTOR3 at(0.0f,  0.0f,   0.0f);
    D3DXVECTOR3 up(0.0f,  1.0f,   0.0f);

    D3DXMATRIX mat_view;
    D3DXMatrixLookAtLH(&mat_view, &eye, &at, &up);
    g_device->SetTransform(D3DTS_VIEW, &mat_view);

    // setup projection matrix
    D3DXMATRIX mat_proj;
    D3DXMatrixPerspectiveFovLH(&mat_proj, D3DX_PI/4, 1.0f, 1.0f, 500.0f);
    g_device->SetTransform(D3DTS_PROJECTION, &mat_proj);
}

bool init_geometry()
{
    ID3DXBuffer* material_buffer;

    /*
     D3DXLoadMeshFromXA(
        LPCSTR pFilename,
        DWORD Options,
        LPDIRECT3DDEVICE9 pD3DDevice,
        LPD3DXBUFFER *ppAdjacency,
        LPD3DXBUFFER *ppMaterials,
        LPD3DXBUFFER *ppEffectInstances,
        DWORD *pNumMaterials,
        LPD3DXMESH *ppMesh);
    */

    if(FAILED(D3DXLoadMeshFromX("heli.x", D3DXMESH_SYSTEMMEM, g_device, NULL,
                                                    &material_buffer, NULL,&g_num_materials, &g_mesh)))
    {
        MessageBox(NULL, "Could not find heli.x", "ERROR", MB_OK);
        return false;
    }

    D3DXMATERIAL* xmaterials = (D3DXMATERIAL*) material_buffer->GetBufferPointer();

    g_mesh_materials = new D3DMATERIAL9[g_num_materials];
    g_mesh_textures     = new IDirect3DTexture9*[g_num_materials];

    for(DWORD i = 0; i < g_num_materials; i++)
    {
        g_mesh_materials = xmaterials.MatD3D;

        // set ambient reflected coefficient, because .x file do not set it.
        g_mesh_materials.Ambient = g_mesh_materials.Diffuse;

        g_mesh_textures = NULL;

        if(xmaterials.pTextureFilename != NULL && strlen(xmaterials.pTextureFilename) > 0)   
            D3DXCreateTextureFromFile(g_device, xmaterials.pTextureFilename, &g_mesh_textures);   
    }

    material_buffer->Release();

    return true;
}

bool init_d3d(HWND hwnd)
{
    g_d3d = Direct3DCreate9(D3D_SDK_VERSION);

    if(g_d3d == NULL)
        return false;

    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory(&d3dpp, sizeof(d3dpp));

    d3dpp.Windowed                       = TRUE;
    d3dpp.SwapEffect                      = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat             = D3DFMT_UNKNOWN;
    d3dpp.EnableAutoDepthStencil    = TRUE;
    d3dpp.AutoDepthStencilFormat    = D3DFMT_D16;

    if(FAILED(g_d3d->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,
                                                    D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_device)))
    {
        return false;
    }
   
    if(! init_geometry())
        return false;

    setup_view_proj_matrices();   

    g_device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
    g_device->SetRenderState(D3DRS_SRCBLEND,  D3DBLEND_SRCALPHA);
    g_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);   

    //g_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
    //g_device->SetRenderState(D3DRS_DESTBLEND,  D3DBLEND_INVDESTCOLOR);   

    //g_device->SetRenderState(D3DRS_BLENDOP,  D3DBLENDOP_SUBTRACT);        

    g_device->SetRenderState(D3DRS_ZENABLE,  TRUE);
    g_device->SetRenderState(D3DRS_ZFUNC,  D3DCMP_LESS);
    g_device->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);

    g_device->SetRenderState(D3DRS_AMBIENT, 0xFFFFBB55);
   
    return true;
}

void cleanup()
{
    delete[] g_mesh_materials;

    if(g_mesh_textures)
    {
        for(DWORD i = 0; i < g_num_materials; i++)
            release_com(g_mesh_textures);

        delete[] g_mesh_textures;
    }
   
    release_com(g_mesh);
    release_com(g_device);
    release_com(g_d3d);
}

void render()
{
    g_device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(5, 5, 5), 1.0f, 0);

    g_device->BeginScene();

    setup_world_matrix();

    // render opaque object first
    for(DWORD i = 0; i < g_num_materials; i++)
    {
        if(g_mesh_materials.Diffuse.a == 1.0f)
        {
            g_device->SetMaterial(&g_mesh_materials);
            g_device->SetTexture(0, g_mesh_textures);

            g_mesh->DrawSubset(i);
        }        
    }        

    // render transparent object second
    for(DWORD i = 0; i < g_num_materials; i++)
    {
        if(g_mesh_materials.Diffuse.a != 1.0f)
        {
            g_device->SetMaterial(&g_mesh_materials);
            g_device->SetTexture(0, g_mesh_textures);

            g_mesh->DrawSubset(i);
        }        
    }        
   
    g_device->EndScene();

    g_device-> Present(NULL, NULL, NULL, NULL);
}

LRESULT WINAPI WinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {
    case WM_KEYDOWN:
        switch(wParam)
        {
        case 48: // press key "0", disable alpha blend.
            g_device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
            break;

        case 49: // press key "1", enable alpha blend.
            g_device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
            break;

        case VK_ESCAPE:
            DestroyWindow(hwnd);
            break;
        }   

        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }

    return DefWindowProc(hwnd, msg, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE inst, HINSTANCE, LPSTR, INT)
{
    WNDCLASSEX wc;

    wc.cbSize                 = sizeof(WNDCLASSEX);
    wc.style                   = CS_CLASSDC;
    wc.lpfnWndProc        = WinProc;
    wc.cbClsExtra           = 0;
    wc.cbWndExtra         = 0;
    wc.hInstance            = inst;
    wc.hIcon                  = NULL;
    wc.hCursor               = NULL;
    wc.hbrBackground     = NULL;
    wc.lpszMenuName     = NULL;
    wc.lpszClassName     = CLASS_NAME;
    wc.hIconSm             = NULL;

    if(! RegisterClassEx(&wc))
        return -1;

    HWND hwnd = CreateWindow(CLASS_NAME, "Direct3D App", WS_OVERLAPPEDWINDOW, 200, 100, 640, 480,
                             NULL, NULL, wc.hInstance, NULL);   

    if(hwnd == NULL)
        return -1;

    if(init_d3d(hwnd))
    {
        ShowWindow(hwnd, SW_SHOWDEFAULT);
        UpdateWindow(hwnd);

        MSG msg;
        ZeroMemory(&msg, sizeof(msg));

        while(msg.message != WM_QUIT)
        {
            if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
               
            render();
        }
    }

    cleanup();
    UnregisterClass(CLASS_NAME, wc.hInstance);   

    return 0;
}

 

深度测试与alpha混合(3)

alpha源混合系数通常设置为D3DBLEND_SRCALPHA,即当前绘制像素的alpha值。目标混合系数设置为 D3DBLEND_INVSRCALPHA,即1减去当前绘制像素的alpha值。那么当前绘制像素的alpha值又是如何得到的呢?如果没有使用材质和 纹理,当前绘制像素的alpha值来自每个顶点颜色设置的alpha值;如果使用光照和材质,则当前像素的alpha值来自物体表面材质;如果为物体表面 使用了纹理,则alpha值还与纹理有关。



顶点alpha

如果在程序中直接指定每个顶点的颜色,则可以直接给出每个顶点颜色的 alpha值,可以在定义顶点时直接声明该顶点的alpha值,也可以在程序运行时动态地修改顶点的alpha值。有了顶点的alpha值,渲染对象中每 个像素的alpha值由该对象的alpha值和着色模式决定。当着色模式为FLAT着色模式时,构成对象的各个多边形中所有像素的alpha都等于该多边 形的第一个顶点的alpha值。当着色模式为GOURAUD着色模式时,每个多边形面上的像素的alpha值由它的各个顶点的alpha值进行线性插值得 到的。



示例程序:

圆筒在不断的绕x, y, z轴旋转。

 



 按下数字键"1",启用alpha顶点混合


按下数字键"0",禁用alpha顶点混合

 

#include <d3dx9.h>

#pragma warning(disable : 4127)    // disable warning: conditional expression is constant

#define CLASS_NAME    "GameApp"

#define release_com(p)    do { if(p) { (p)->Release(); (p) = NULL; } } while(0)

typedef unsigned char uchar;

IDirect3D9*                    g_d3d;
IDirect3DDevice9*           g_device;
IDirect3DVertexBuffer9*   g_vertex_buffer;

struct sCustomVertex
{
    float x, y, z;
    DWORD color;
};

#define D3DFVF_CUSTOM_VERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE)

void setup_world_matrix()
{
    D3DXMATRIX mat_world;
    D3DXMatrixIdentity(&mat_world);

    float angle = (timeGetTime() % 1000) * (2 * D3DX_PI) / 1000.0f;

    D3DXQUATERNION quat;
    D3DXMATRIX mat_rotation;

    D3DXQuaternionRotationYawPitchRoll(&quat, angle, angle, angle);
    D3DXMatrixRotationQuaternion(&mat_rotation, &quat);
    D3DXMatrixMultiply(&mat_world, &mat_rotation, &mat_world);

    g_device->SetTransform(D3DTS_WORLD, &mat_world);
}

void setup_view_proj_matrices()
{
    // setup view matrix

    D3DXVECTOR3 eye(0.0f, 3.0f, -5.0f);
    D3DXVECTOR3 at(0.0f,  0.0f,   0.0f);
    D3DXVECTOR3 up(0.0f,  1.0f,   0.0f);

    D3DXMATRIX mat_view;
    D3DXMatrixLookAtLH(&mat_view, &eye, &at, &up);
    g_device->SetTransform(D3DTS_VIEW, &mat_view);

    // setup projection matrix
    D3DXMATRIX mat_proj;
    D3DXMatrixPerspectiveFovLH(&mat_proj, D3DX_PI/4, 1.0f, 1.0f, 100.0f);
    g_device->SetTransform(D3DTS_PROJECTION, &mat_proj);
}

void init_vb()
{   
    g_device->CreateVertexBuffer(50 * 2 * sizeof(sCustomVertex), 0, D3DFVF_CUSTOM_VERTEX,
                                                D3DPOOL_DEFAULT, &g_vertex_buffer, NULL);

    sCustomVertex* vertices;

    g_vertex_buffer->Lock(0, 0, (void**)&vertices, 0);

    for(int i = 0; i < 50; i++)
    {
        float theta = (2 * D3DX_PI * i) / (50 - 1);

        vertices[2 * i + 0].x      = sin(theta);
        vertices[2 * i + 0].y      = -1.0f;
        vertices[2 * i + 0].z      = cos(theta);            
        vertices[2 * i + 0].color = 0x88FF0000;

        vertices[2 * i + 1].x      = sin(theta);
        vertices[2 * i + 1].y      = 1.0f;
        vertices[2 * i + 1].z      = cos(theta);            
        vertices[2 * i + 1].color = 0x8844FF00;
    }
   
    g_vertex_buffer->Unlock();
}

bool init_d3d(HWND hwnd)
{
    g_d3d = Direct3DCreate9(D3D_SDK_VERSION);

    if(g_d3d == NULL)
        return false;

    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory(&d3dpp, sizeof(d3dpp));

    d3dpp.Windowed              = TRUE;
    d3dpp.SwapEffect             = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat    = D3DFMT_UNKNOWN;

    if(FAILED(g_d3d->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,
                                                    D3DCREATE_SOFTWARE_VERTEXPROCESSING,&d3dpp, &g_device)))
    {
        return false;
    }

    init_vb();
    setup_view_proj_matrices();

    g_device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
    g_device->SetRenderState(D3DRS_LIGHTING, FALSE);

    g_device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
    g_device->SetRenderState(D3DRS_SRCBLEND,  D3DBLEND_SRCALPHA);
    g_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

    return true;
}

void cleanup()
{
    release_com(g_vertex_buffer);
    release_com(g_device);
    release_com(g_d3d);
}

inline void extract_argb(D3DCOLOR color, uchar* alpha, uchar* red, uchar* green, uchar* blue)
{
    // Extract alpha, red, green, blue from D3D color value.

    if(alpha != NULL) *alpha  = uchar((color >> 24) & 0xff);
    if(red   != NULL) *red      = uchar((color >> 16) & 0xff);
    if(green != NULL) *green = uchar((color >> 8) & 0xff);
    if(blue  != NULL) *blue    = uchar(color & 0xff);
}

void render()
{
    g_device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(5, 5, 5), 1.0f, 0);   

    g_device->BeginScene();

    setup_world_matrix();

    g_device->SetStreamSource(0, g_vertex_buffer, 0, sizeof(sCustomVertex));
    g_device->SetFVF(D3DFVF_CUSTOM_VERTEX);
    g_device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2 * 50 - 2);

    // change all vertices's color and alpha

    sCustomVertex* vertex;

    g_vertex_buffer->Lock(0, 50 * 2 * sizeof(sCustomVertex), (void**)&vertex, 0);

    for(int i = 0; i < 50 * 2; i++)
    {   
        uchar alpha, red, green, blue;
        extract_argb(vertex->color, &alpha, &red, &green, &blue);

        if(++alpha > 255)    alpha = 0;
        if(++red > 255)        red = 0;
        if(++green > 255)    green = 0;
        if(++blue > 255)    blue = 0;

        vertex->color = D3DCOLOR_RGBA(red, green, blue, alpha);

        vertex++;
    }   

    g_vertex_buffer->Unlock();

    g_device->EndScene();

    g_device-> Present(NULL, NULL, NULL, NULL);
}

LRESULT WINAPI WinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {
    case WM_KEYDOWN:
        switch(wParam)
        {
        case VK_ESCAPE:
            DestroyWindow(hwnd);
            break;

        case 48:    // press key "0", disable alpha blend.
            g_device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
            break;

        case 49:    // press key "1", enable alpha blend.
            g_device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
            break;
        }        
            
        break;

    case WM_DESTROY:        
        PostQuitMessage(0);
        return 0;
    }

    return DefWindowProc(hwnd, msg, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE inst, HINSTANCE, LPSTR, INT)
{
    WNDCLASSEX wc;

    wc.cbSize                = sizeof(WNDCLASSEX);
    wc.style                  = CS_CLASSDC;
    wc.lpfnWndProc       = WinProc;
    wc.cbClsExtra          = 0;
    wc.cbWndExtra        = 0;
    wc.hInstance           = inst;
    wc.hIcon                 = NULL;
    wc.hCursor              = NULL;
    wc.hbrBackground    = NULL;
    wc.lpszMenuName    = NULL;
    wc.lpszClassName    = CLASS_NAME;
    wc.hIconSm            = NULL;

    if(! RegisterClassEx(&wc))
        return -1;

    HWND hwnd = CreateWindow( CLASS_NAME, "Direct3D App", WS_OVERLAPPEDWINDOW, 200, 100, 600, 500,
                                                 NULL, NULL, wc.hInstance, NULL);

    if(hwnd == NULL)
        return -1;

    if(init_d3d(hwnd))
    {
        ShowWindow(hwnd, SW_SHOWDEFAULT);
        UpdateWindow(hwnd);

        MSG msg;
        ZeroMemory(&msg, sizeof(msg));

        while(msg.message != WM_QUIT)
        {
            if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
               
            render();
            Sleep(10);
        }
    }

    cleanup();
    UnregisterClass(CLASS_NAME, wc.hInstance);   

    return 0;
}

 

深度测试与alpha混合(4)

材质alpha

顶点alpha是没有使用光照和材质的情况,如果对场景内的物体添加光照和材质而没有添加纹理时,顶点alpha值取决于材质属性中漫反射颜色的 alpha系数和灯光颜色中的alpha系数,顶点alpha值是根据光照计算得到的。顶点光照计算是分别针对红、绿、蓝和alpha进行的,其中 alpha光照计算的结果就是顶点的alpha值。有了顶点的alpha值就可根据着色模式计算出每个像素的alpha值,第一个示例程序就是材质 alpha的例子。



纹理alpha

当对物体表面使用了纹理之后,像素的alpha值就是纹理alpha混合之后的值,所以这又取决于纹理的alpha混合方法,纹理alpha混合方法决定 了纹理alpha混合之后的alpha值是取自材质,还是取自纹理,或者取自二者的某种运算。像素alpha值的具体计算过程是这样的,首先得到顶点 alpha值,顶点alpha值可能是直接指定的,也可能是光照计算得到,然后根据着色模式对顶点alpha值进行插值,得到的结果再根据纹理alpha 混合方法和纹理采样得到的alpha值进行指定的运算,得到最终每个像素的alpha值。

示例程序中将一幅纹理应用到一个矩形表面,其中纹理alpha混合的设置如下:

g_device->SetTextureStageState(0, D3DTSS_ALPHAOP,  D3DTOP_MODULATE);
g_device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
g_device->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
D3DTOP_MODULATE

Multiply the components of the arguments.
SRGBA = Arg1 x Arg2

在示例程序中将矩形4个顶点的颜色值设置为0xFFFFFFFF,其alpha成分设置为ff,即alpha值为1.0f,所以纹理alpha混合的最终结果就是取纹理的alpha值。

sCustomVertex vertices[] ={
        { -3,   -3,  0.0f,   0xffffffff,  0.0f, 1.0f},
        { -3,    3,  0.0f,   0xffffffff,  0.0f, 0.0f},
        {  3,   -3,  0.0f,   0xffffffff,  1.0f, 1.0f},        
        {  3,    3,  0.0f,   0xffffffff,  1.0f, 0.0f}
}

示例程序在一个矩形表面贴了一颗树的纹理,在树的纹理中,没有树叶和树枝的地方alpha值为0,即完全透明;有树叶和树枝的地方alpha值为1,即完全不透明。所以通过alpha混合后,渲染的结果就像是一棵真的树。



 按下数字键"1",启用纹理alpha混合。



按下数字键"0",禁用纹理alpha混合。



 

将顶点alpha由ff改为88后启用纹理混合的效果,可以看出纹理的颜色变暗了。

sCustomVertex vertices[] =    {
        { -3,   -3,  0.0f,   0x88ffffff,  0.0f, 1.0f},
        { -3,    3,  0.0f,   0x88ffffff,  0.0f, 0.0f},        
        {  3,   -3,  0.0f,   0x88ffffff,  1.0f, 1.0f},        
        {  3,    3,  0.0f,   0x88ffffff,  1.0f, 0.0f}   
};


源程序:

#include <d3dx9.h>

#pragma warning(disable : 4127)    // disable warning: conditional expression is constant

#define CLASS_NAME    "GameApp"

#define release_com(p)    do { if(p) { (p)->Release(); (p) = NULL; } } while(0)

typedef unsigned char uchar;

IDirect3D9*                   g_d3d;
IDirect3DDevice9*          g_device;
IDirect3DVertexBuffer9*  g_vertex_buffer;
IDirect3DTexture9*         g_texture;

struct sCustomVertex
{
    float x, y, z;
    DWORD color;
    float u, v;
};

#define D3DFVF_CUSTOM_VERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1)

void setup_matrices()
{
    // setup world matrix
    D3DXMATRIX mat_world;
    D3DXMatrixIdentity(&mat_world);
    g_device->SetTransform(D3DTS_WORLD, &mat_world);

    // setup view matrix

    D3DXVECTOR3 eye(0.0f, 0.0f,  -10.0f);
    D3DXVECTOR3 at(0.0f,  0.0f,   0.0f);
    D3DXVECTOR3 up(0.0f,  1.0f,   0.0f);

    D3DXMATRIX mat_view;
    D3DXMatrixLookAtLH(&mat_view, &eye, &at, &up);
    g_device->SetTransform(D3DTS_VIEW, &mat_view);

    // setup projection matrix
    D3DXMATRIX mat_proj;
    D3DXMatrixPerspectiveFovLH(&mat_proj, D3DX_PI/4, 1.0f, 1.0f, 100.0f);
    g_device->SetTransform(D3DTS_PROJECTION, &mat_proj);
}

bool init_vb()
{   
    if(FAILED(D3DXCreateTextureFromFile(g_device, "tree.tga", &g_texture)))
    {
        MessageBox(NULL, "Can not load texture file tree.tga!", "ERROR", MB_OK);
        return false;
    }

    sCustomVertex vertices[] =
    {
        { -3,   -3,  0.0f,   0xffffffff,  0.0f, 1.0f},
        { -3,    3,  0.0f,   0xffffffff,  0.0f, 0.0f},   
        {  3,   -3,  0.0f,   0xffffffff,  1.0f, 1.0f},   
        {  3,    3,  0.0f,   0xffffffff,  1.0f, 0.0f}

        /*
        { -3,   -3,  0.0f,   0x88ffffff,  0.0f, 1.0f},
        { -3,    3,  0.0f,   0x88ffffff,  0.0f, 0.0f},   
        {  3,   -3,  0.0f,   0x88ffffff,  1.0f, 1.0f},   
        {  3,    3,  0.0f,   0x88ffffff,  1.0f, 0.0f}
        */
    };

    g_device->CreateVertexBuffer(sizeof(vertices), 0, D3DFVF_CUSTOM_VERTEX, D3DPOOL_DEFAULT, &g_vertex_buffer, NULL);

    void* ptr;

    g_vertex_buffer->Lock(0, 0, &ptr, 0);
    memcpy(ptr, vertices, sizeof(vertices));
    g_vertex_buffer->Unlock();

    return true;
}

bool init_d3d(HWND hwnd)
{
    g_d3d = Direct3DCreate9(D3D_SDK_VERSION);

    if(g_d3d == NULL)
        return false;

    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory(&d3dpp, sizeof(d3dpp));

    d3dpp.Windowed             = TRUE;
    d3dpp.SwapEffect            = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat   = D3DFMT_UNKNOWN;

    if(FAILED(g_d3d->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,
                                                    D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_device)))
    {
        return false;
    }

    if(! init_vb())
        return false;

    setup_matrices();

    g_device->SetRenderState(D3DRS_LIGHTING, FALSE);

    g_device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
    g_device->SetRenderState(D3DRS_SRCBLEND,  D3DBLEND_SRCALPHA);
    g_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

    g_device->SetTextureStageState(0, D3DTSS_COLOROP,    D3DTOP_SELECTARG1);
    g_device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);

    g_device->SetTextureStageState(0, D3DTSS_ALPHAOP,    D3DTOP_MODULATE);
    g_device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
    g_device->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);

    return true;
}

void cleanup()
{
    release_com(g_vertex_buffer);
    release_com(g_texture);
    release_com(g_device);
    release_com(g_d3d);
}

void render()
{
    g_device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(55, 5, 5), 1.0f, 0);   

    g_device->BeginScene();   

    g_device->SetTexture(0, g_texture);
    g_device->SetStreamSource(0, g_vertex_buffer, 0, sizeof(sCustomVertex));
    g_device->SetFVF(D3DFVF_CUSTOM_VERTEX);
    g_device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);

    g_device->EndScene();

    g_device-> Present(NULL, NULL, NULL, NULL);
}

LRESULT WINAPI WinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {
    case WM_KEYDOWN:
        switch(wParam)
        {
        case VK_ESCAPE:
            DestroyWindow(hwnd);
            break;

        case 48:    // press key "0", disable alpha blend.
            g_device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
            break;

        case 49:    // press key "1", enable alpha blend.
            g_device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
            break;
        }        
            
        break;

    case WM_DESTROY:        
        PostQuitMessage(0);
        return 0;
    }

    return DefWindowProc(hwnd, msg, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE inst, HINSTANCE, LPSTR, INT)
{
    WNDCLASSEX wc;

    wc.cbSize                 = sizeof(WNDCLASSEX);
    wc.style                   = CS_CLASSDC;
    wc.lpfnWndProc        = WinProc;
    wc.cbClsExtra           = 0;
    wc.cbWndExtra         = 0;
    wc.hInstance            = inst;
    wc.hIcon                  = NULL;
    wc.hCursor               = NULL;
    wc.hbrBackground    = NULL;
    wc.lpszMenuName    = NULL;
    wc.lpszClassName    = CLASS_NAME;
    wc.hIconSm            = NULL;

    if(! RegisterClassEx(&wc))
        return -1;

    HWND hwnd = CreateWindow( CLASS_NAME, "Direct3D App", WS_OVERLAPPEDWINDOW, 200, 100, 600, 500,
                                                 NULL, NULL, wc.hInstance, NULL);

    if(hwnd == NULL)
        return -1;

    if(init_d3d(hwnd))
    {
        ShowWindow(hwnd, SW_SHOWDEFAULT);
        UpdateWindow(hwnd);

        MSG msg;
        ZeroMemory(&msg, sizeof(msg));

        while(msg.message != WM_QUIT)
        {
            if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
               
            render();
            Sleep(10);
        }
    }

    cleanup();
    UnregisterClass(CLASS_NAME, wc.hInstance);   

    return 0;
}







深度测试与alpha混合(5)

透过那些透明度非常高的物体看其他物体,例如透过几乎完全透明的玻璃看其他物体,会感到玻璃好像不存在,在三维图形程序中渲染时就可以不渲染这些透明度非常高的物体,从而可以提高渲染速度,这可以通过alpha测试来实现。

alpha测试根据当前像素是否满足alpha测试条件(即是否达到一定的透明度)来控制是否绘制该像素,图形程序应用alpha测试可以有效地屏蔽某些 像素颜色。与alpha混合相比,alpha测试不将当前像素的颜色与颜色缓冲区中像素的颜色混合,像素要么完全不透明,要么完全透明。由于无需进行颜色 缓冲区的读操作和颜色混合,因此alpha测试在速度上要优于alpha混合。

alpha测试通过激活渲染状态D3DRS_ALPHATESTENABLE来设置,示例代码如下:

g_device->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);

渲染状态D3DRS_ALPHAREF用来设置alpha测试的参考值,alpha测试函数比较当前绘制的像素的alpha值和参考值,如果返回TRUE,则通过测试并绘制像素,反之不予绘制。参考值的取值范围是0x00000000到0x000000ff。

渲染状态D3DRS_ALPHAFUNC用来设置alpha测试函数,alpha测试函数属于D3DCMPFUNC枚举类型,默认状态为 D3DCMP_ALWAYS。下列代码设置alpha测试函数为D3DCMP_GREATER,表示测试点像素的alpha值大于设置的alpha参考值 时则返回TRUE:

g_device->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);
g_device->SetRenderState(D3DRS_ALPHAREF, 0x00000081);
g_device->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER);

已知蓝色玻璃的alpha值为浮点值0.5f,等价于两位16进制数0x80,小于程序中设置的alpha测试参考值0x81,并且alpha测试函数被设为D3DCMP_GREATER,所以蓝色玻璃的颜色不会被绘制出来。

 



 

 

  • 大小: 47.9 KB
  • 大小: 19.8 KB
  • 大小: 21 KB
  • 大小: 17.8 KB
  • 大小: 18.6 KB
  • 大小: 25.5 KB
  • 大小: 20.3 KB
  • 大小: 27.5 KB
  • 大小: 29.8 KB
  • 大小: 118.2 KB
  • 大小: 122.3 KB
  • 大小: 86.7 KB
  • 大小: 18.1 KB
  • 大小: 17.9 KB
分享到:
评论

相关推荐

    精通Direct3D图形与动画程序设计 08_第八章 深度测试与Alpha混合.rar

    第8章 深度测试与Alpha混合 1、ZTest 演示深度测试的使用。程序运行时按下数字键“1”启用深度测试,按下数字键“0”,则禁用深度测试。 2、AlphaBlend 演示使用Alpha混合实现半透明效果。程序运行时按下数字键“1”,...

    OGLStencil深度测试 模板测试 以及 Alpha混合_VS2015.rar

    OGLStencil深度测试 模板测试 以及 Alpha混合_VS2015.rar ---------------- OGLStencil深度测试 模板测试 以及 Alpha混合_VS2015.rar --------------------参考: ...

    学习目标掌握深度缓冲Alpha混合和Alpha测试掌握多边形.pdf

    如果深度测试为TRUE,则绘制当前像素,并用当前像素点深度值更新深度缓冲区,反之则不予绘制。深度缓冲的作用是确保多边形能够正确地显示在它们本来的深度(相对于摄像机)上。 Alpha混合是指在渲染图形时,使用...

    D3D9 alpha混合 demo (自定义alpha和纹理自带alpha)

    Alpha混合是通过对像素的颜色值与背景颜色进行线性插值来实现的。Alpha通道,通常表示为0到1的浮点数,0代表完全透明,1代表完全不透明。在D3D9中,我们可以自定义Alpha值,也可以利用纹理自带的Alpha信息进行混合。...

    VS2008下的win32Alpha混合的例子

    5. `D3DRS_ZWRITEENABLE` 可能需要设置为 FALSE,以防深度缓冲区与Alpha混合产生冲突。 最后,按照常规顺序绘制模型,但注意要按后到前的顺序绘制,以正确处理透明部分的覆盖。绘制完成后,记得恢复之前的渲染状态...

    duff-porter_alpha混合_Thomas!_

    在计算机图形学领域,"duff-porter_alpha混合_Thomas!" 涉及到的是一个经典的技术,由Thomas Porter和Tom Duff两位先驱在早期发展起来的Alpha Blending技术。这项技术是现代计算机图形中透明和半透明效果的基础,...

    dx9 alpha混合纹理

    在3D图形编程中,Alpha混合纹理是一项关键的技术,它使得物体的表面可以具有半透明或者透明的效果,增加了场景的深度和真实感。本文将深入探讨DX9中的Alpha混合纹理,以及如何通过原代码实现这一功能。 首先,我们...

    OpenGL_Alpha.rar_Alpha_gl 混合_opengl alpha

    Alpha混合允许我们在渲染图像时实现半透明效果,使得不同颜色之间能够自然地融合,创造出深度感和真实感。本示例是为了解决初学者在使用OpenGL进行Alpha混合时遇到的常见问题而设计的。 在OpenGL中,Alpha通道通常...

    嵌入式图形处理器Alpha混合单元设计与实现.pdf

    Alpha混合是图形处理中的一个关键步骤,它涉及到图像层之间的透明度控制,使得多层图像能够平滑地融合在一起,创造出深度感和复杂的视觉效果。本文主要介绍了一种针对嵌入式GPU的Alpha混合单元的设计与实现方法。 ...

    DX Alpha 颜色 混合 颜色透明显示

    在这个“DX Alpha 颜色 混合 颜色透明显示”的主题中,我们将深入探讨DX中的颜色混合与透明显示技术。 首先,Alpha通道是颜色模型中的一个组成部分,它代表了颜色的透明度。在RGB颜色模型中,红、绿、蓝三个通道...

    精通direct3d图形及动画程序设计源代码下载

    5. **深度测试与Alpha混合**: 08_第八章讨论了深度测试和Alpha混合,这两者是透明和半透明物体渲染的关键。深度测试确保了正确地按距离排列3D对象,而Alpha混合则处理物体之间的重叠部分,使得混合更加自然。 6. **...

    OpenGL入门-深度测试及混合-透明度图形实现.zip

    OpenGL入门教程:深度测试与透明度图形实现 在3D图形编程中,OpenGL是一个不可或缺的工具,它提供了丰富的功能来创建复杂的三维场景。本教程将深入探讨如何在C++环境中,利用OpenGL进行深度测试和透明度图形的实现...

    精通DirectX 3D 图形与动画程序设计附带光盘part1

    "09_第九章 雾化"和"08_第八章 深度测试与Alpha混合"是两种常见的3D视觉增强技术。雾化可以模拟环境中的大气效果,使场景更具深度感;深度测试和Alpha混合则解决了3D物体的遮挡问题和透明效果,使得层次感更强。 ...

    d3dx图形与动画程序设计源代码(part1)

    4. **08_第八章 深度测试与Alpha混合**:深度测试确保了正确处理物体之间的遮挡关系,而Alpha混合则允许透明和半透明效果的实现,是实现复杂场景和特效的关键技术。 5. **09_第九章 雾化**:雾化效果增加了场景的...

    Direct alpha

    由于半透明物体可能会遮挡背后的物体,因此在进行Alpha混合时,应确保正确设置了深度测试,以避免出现错误的渲染顺序。 为了帮助初学者更好地理解Direct3D中的Alpha混合,这里提供一个简单的步骤: 1. 初始化...

    OpenGL混合例子

    同时,确保深度测试正确配置,以避免透明物体的排序问题,因为OpenGL默认按顺序绘制,后面的物体可能覆盖前面的物体,即使前面的物体部分透明。 此外,性能也是考虑因素之一。Alpha混合通常比纯色渲染更耗能,因为...

    《精通direct3d图形及动画程序设计》附书源代码

    第8章 深度测试与Alpha混合 1、ZTest 演示深度测试的使用。程序运行时按下数字键“1”启用深度测试,按下数字键“0”,则禁用深度测试。 2、AlphaBlend 演示使用Alpha混合实现半透明效果。程序运行时按下数字键“1”,...

    dx9_alpha_blending_material.rar_Alpha

    2. **启用Alpha混合**:接下来,你需要开启Alpha混合。同样使用`SetRenderState`函数,这次是设置`D3DRS_ALPHABLENDENABLE`为`TRUE`。接着设置混合源和目标因子,例如`D3DBLEND_SRCALPHA`和`D3DBLEND_INVSRCALPHA`,...

    Android下 OpenGL ES 2.0 混合半透明效果demo

    为了混合半透明像素,我们需要开启Alpha混合功能。这可以通过调用`glEnable(GL_BLEND)`来实现,并设置合适的混合函数,如`glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)`,这使得新绘制的像素颜色与已有颜色...

Global site tag (gtag.js) - Google Analytics