1. 程式人生 > >Direct3D進行Alpha混合實現半透明效果

Direct3D進行Alpha混合實現半透明效果


這次給大家奉獻的是我最近學習DirectX基礎的一些內容:進行Alpha混合。雖然我在很多的遊戲中看到了美輪美奐的半透明效果,但是能夠自己製作出半透明的效果還是一件非常欣慰的事情。因為這不僅僅是自己目的的達成,還是自己自學能力的提升。

Alpha是畫素顏色中的一個值,但是改變它並不能改變任何顏色,而是改變它的透明度。它佔一個位元組,也就是說它的取值範圍為從0到255。0代表完全看不見,255表示完全不透明。為此我記住了兩個英文單詞:transparent和opaque。為了能夠使用Alpha製作出半透明的效果,要在D3D裝置上呼叫一個函式來啟用它。這個函式就是SetRenderState。下面就是啟用Alpha混合的典型用法:

// 設定Alpha混合
m_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
m_pDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
m_pDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );

//其它複雜的我也沒有看懂,反正Alpha混合是在光柵化(Rasterization)上進行的。其中還有很多其它的操作,但是在初級階段是用不著啦。所以我也暫時不研究了。它在《Real Time Rendering》這本書裡有。這裡是我的修改的專案,就是將以前的專案稍微進行了修改,就可以顯示這樣的效果了:

// RenderByFileDialog.cpp Alpha半透明效果的實現檔案
// 2012年2月26日13:35:02 最後編輯

#include <atlbase.h>            // ATL應用程式基礎庫
#include <atlapp.h>                // ATL應用程式必須有的檔案
#include <atldlgs.h>            // 包含WTL開啟檔案的對話方塊
#include "RenderByFileDialog.h"
#include "resource.h"            // 資源標頭檔案

#define SAFE_RELEASE( p )        if ( p ) { p->Release( ); p = 0; }

CRenderByFileDialog::CRenderByFileDialog( HINSTANCE hInst, HWND hWnd,
                                          LPDIRECT3DDEVICE9 pDevice,
                                          int width, int height )// 建構函式
{
    // 成員賦值
    m_pDevice            = pDevice;
    m_MoveFact            = 0.01f;
    m_CurImageFile        = TEXT( "" );

    // 設定視角
    ZeroMemory( &m_Viewport, sizeof( m_Viewport ) );// 清零
    m_Viewport.X                = 0;
    m_Viewport.Y                = 0;
    m_Viewport.Width            = width;
    m_Viewport.Height            = height;
    m_Viewport.MinZ                = 0.0f;
    m_Viewport.MaxZ                = 1.0f;
    m_pDevice->SetViewport( &m_Viewport );

    // 建立頂點快取
    HRESULT hr;
    hr = m_pDevice->CreateVertexBuffer( 8 * sizeof( STVertex ), D3DUSAGE_WRITEONLY,
                                        TEXTURE_FVF, D3DPOOL_MANAGED, &m_pBuffer, NULL );
    ThrowIfFailed( hr, "不會吧,這都無法建立頂點快取。⊙﹏⊙b汗" );

    hr = m_pBuffer->Lock( 0, 8 * sizeof( STVertex ), (void**)&m_pVtxBackground, D3DLOCK_DISCARD );
    ThrowIfFailed( hr, "不可能,頂點怎麼會鎖住失敗呢??o(>﹏<)o" );
    m_pVertices = &m_pVtxBackground[4];
    hr = m_pBuffer->Unlock( );
    ThrowIfFailed( hr, "不可能,頂點怎麼會也會解鎖失敗呢??o(>﹏<)o" );

    D3DXIMAGE_INFO            imageInfo;
    hr = D3DXCreateTextureFromResourceEx(            // 從資源建立紋理
                                                    m_pDevice,                                // DIRECT3DDEVICE9結構指標
                                                    NULL,                                    // 模組控制代碼
                                                    MAKEINTRESOURCE( IDR_RCDATA1 ),            // 載入的影象資源名稱
                                                    D3DX_DEFAULT,                            // 寬
                                                    D3DX_DEFAULT,                            // 高
                                                    D3DX_FROM_FILE,                            // mip級別
                                                    0,                                        // 用途
                                                    D3DFMT_A8R8G8B8,                        // 格式
                                                    D3DPOOL_MANAGED,                        // 記憶體池格式
                                                    D3DX_DEFAULT,                            // 濾波器
                                                    D3DX_DEFAULT,                            // mip濾波器
                                                    0,                                        // 關鍵色(作掩碼用)
                                                    &imageInfo,                                // 原始檔資訊
                                                    NULL,                                    // 調色盤
                                                    &m_pTexBackground );
    ThrowIfFailed( hr, "怎麼可能啊,這個錯誤不應該出現的啊。找找jiangcaiyang,他最知道是什麼原因。" );
    CenterImage( imageInfo, m_pVtxBackground );    // 調整紋理至居中

    // 初始化前景紋理的頂點
    hr = D3DXCreateTextureFromResourceEx(            // 從資源建立紋理
                                                    m_pDevice,                                // DIRECT3DDEVICE9結構指標
                                                    NULL,                                    // 模組控制代碼
                                                    MAKEINTRESOURCE( IDR_RCDATA2 ),            // 載入的影象資源名稱
                                                    D3DX_DEFAULT,                            // 寬
                                                    D3DX_DEFAULT,                            // 高
                                                    D3DX_FROM_FILE,                            // mip級別
                                                    0,                                        // 用途
                                                    D3DFMT_A8R8G8B8,                        // 格式
                                                    D3DPOOL_MANAGED,                        // 記憶體池格式
                                                    D3DX_DEFAULT,                            // 濾波器
                                                    D3DX_DEFAULT,                            // mip濾波器
                                                    0,                                        // 關鍵色(作掩碼用)
                                                    &imageInfo,                                // 原始檔資訊
                                                    NULL,                                    // 調色盤
                                                    &m_pTexture );
    ThrowIfFailed( hr, "怎麼可能啊,這個錯誤不應該出現的啊。找找jiangcaiyang,他最知道是什麼原因。" );
    CenterImage( imageInfo, m_pVertices );        // 調整紋理至居中

    // 設定頂點快取和靈活頂點格式
    m_pDevice->SetStreamSource( 0, m_pBuffer, 0, sizeof( STVertex ) );    // 相同資源的頂點快取要在一起為好
    m_pDevice->SetTexture( 0, m_pTexBackground );            // 設定背景紋理
    m_pDevice->SetFVF( TEXTURE_FVF );

    // 設定關燈
    hr = m_pDevice->SetRenderState( D3DRS_LIGHTING, FALSE );
    ThrowIfFailed( hr, "怎麼不能關燈了呢?" );

    // 設定Alpha混合
    m_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
    m_pDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
    m_pDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );

    // 初始化輸入系統
    m_ImmediateInput.Initialize( IMMEDIATE, hInst, hWnd );
    m_BufferInput.Initialize( BUFFERED, hInst, hWnd );
}

unsigned long CRenderByFileDialog::Release( void )            // 釋放空間
{
    SAFE_RELEASE( m_pTexture );
    SAFE_RELEASE( m_pTexBackground );
    SAFE_RELEASE( m_pBuffer );

    return 0;
}

void CRenderByFileDialog::CenterImage( D3DXIMAGE_INFO& imageInfo, STVertex* vertices )    // 圖片居中
{
    float                    x, y;
    x = -1.0f / float( m_Viewport.Width ) * float( imageInfo.Width );
    y = -1.0f / float( m_Viewport.Height ) * float( imageInfo.Height );
    vertices[0].Set( x, y, 1.0f, 0.0f, 1.0f );
    vertices[1].Set( x, -y, 1.0f, 0.0f, 0.0f );
    vertices[2].Set( -x, y, 1.0f, 1.0f, 1.0f );
    vertices[3].Set( -x, -y, 1.0f, 1.0f, 0.0f );

}

void CRenderByFileDialog::BrowseImageFile( void )            // 瀏覽並且獲取影象檔案的路徑
{
    CFileDialog imageFileDlg( TRUE,
                              TEXT( ".jpg" ),
                              m_CurImageFile.c_str( ),
                              OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
                              TEXT( "JPG/JPEG檔案\0*.jpg\0PNG檔案\0*.png\0所有檔案\0*.*" ),
                              NULL );

    if ( IDOK == imageFileDlg.DoModal( ) )
    {
        m_CurImageFile = imageFileDlg.m_szFileName;
    }
}

void CRenderByFileDialog::Draw( void )                        // 繪圖
{
    m_ImmediateInput.UpdateKeyState( );
    m_BufferInput.UpdateKeyState( );

    m_pDevice->SetTexture( 0, m_pTexBackground );            // 設定背景紋理
    m_pDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 );

    m_pDevice->SetTexture( 0, m_pTexture );                    // 設定前景紋理
    m_pDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 4, 2 );

    // 開始進行互動
    if ( m_BufferInput.KeyDown( DIK_RETURN ) )
    {
        D3DXIMAGE_INFO            imageInfo;
        HRESULT hr;

        SAFE_RELEASE( m_pTexture );// 釋放空間
        BrowseImageFile( );// 瀏覽圖片檔案並且載入
        hr = D3DXCreateTextureFromFileEx(            // 從檔案建立紋理
                                                    m_pDevice,                                // DIRECT3DDEVICE9結構指標
                                                    m_CurImageFile.c_str( ),                // 載入的影象檔名
                                                    D3DX_DEFAULT,                            // 寬
                                                    D3DX_DEFAULT,                            // 高
                                                    D3DX_FROM_FILE,                            // mip級別
                                                    0,                                        // 用途
                                                    D3DFMT_A8R8G8B8,                        // 格式
                                                    D3DPOOL_MANAGED,                        // 記憶體池格式
                                                    D3DX_DEFAULT,                            // 濾波器
                                                    D3DX_DEFAULT,                            // mip濾波器
                                                    0,                                        // 關鍵色(作掩碼用)
                                                    &imageInfo,                                // 原始檔資訊
                                                    NULL,                                    // 調色盤
                                                    &m_pTexture );                            // IDirect3DTexture9指標

        ThrowIfFailed( hr, "不可能,載入紋理竟然會失敗!是不是檔名錯誤了呢?" );
        CenterImage( imageInfo, m_pVertices );    // 調整紋理至居中
    }
    if ( m_ImmediateInput.KeyDown( DIK_UP ) )
    {
        m_pVertices[0].y += m_MoveFact, m_pVertices[1].y += m_MoveFact;
        m_pVertices[2].y += m_MoveFact, m_pVertices[3].y += m_MoveFact;
    }
    if ( m_ImmediateInput.KeyDown( DIK_DOWN ) )
    {
        m_pVertices[0].y -= m_MoveFact, m_pVertices[1].y -= m_MoveFact;
        m_pVertices[2].y -= m_MoveFact, m_pVertices[3].y -= m_MoveFact;
    }
    if ( m_ImmediateInput.KeyDown( DIK_LEFT ) )
    {
        m_pVertices[0].x -= m_MoveFact, m_pVertices[1].x -= m_MoveFact;
        m_pVertices[2].x -= m_MoveFact, m_pVertices[3].x -= m_MoveFact;
    }
    if ( m_ImmediateInput.KeyDown( DIK_RIGHT ) )
    {
        m_pVertices[0].x += m_MoveFact, m_pVertices[1].x += m_MoveFact;
        m_pVertices[2].x += m_MoveFact, m_pVertices[3].x += m_MoveFact;
    }
}
這樣還不夠。因為僅僅是實現了啟用Alpha載入檔案,但是檔案在建立的時候也必須是帶有Alpha值的。下面我就用自己常用的Photoshop Cs4和EasyPaintToolSAI來演示一下。

在Photoshop新建對話方塊裡,選擇背景為透明,如下圖:

隨後為了簡單起見,僅僅使用單圖層,並且使用漸變的效果來演示。將漸變的不透明度調整為50%,如下圖:

然後隨便在圖片上使用漸變繪圖:

繪製完後儲存為png格式檔案,然後可以在程式中載入,結果如下圖所示:

使用EasyPaintToolSAI也非常簡單:在控制面板中調整圖層的不透明度,然後點另存為,如下圖:

另存為png格式之後提示一個對話方塊,選擇下面帶有Alpha值的就是了,要不然你繪製的帶有Alpha值的圖片是不會顯示半透明效果的,因為它根本就沒有儲存起來嘛。

最後顯示的效果如下圖所示: