桌面透明視窗程式渲染

2021-06-23 04:00:04 字數 4459 閱讀 6214

市面上基本所有的3d遊戲都依賴乙個普通的windows視窗,包含標題欄、邊框、最小化、最大化、關閉按鈕。視窗的大小決定了玩家可視的遊戲空間,整個視窗的畫素都被遊戲內容填充滿,視窗背景不是透明的。渲染時,只要建立乙個主渲染緩衝區,將各元素渲染在上面,再顯示就可以了。

採用類似技術的遊戲有「哈姆寶寶」、「寵物王國」等。該技術比較適合寵物養成類遊戲,比傳統2d寵物的渲染方式要複雜的多。

//華麗的分隔線

先介紹下渲染的基本步驟:

1. 將角色渲染到貼圖(rendertotexture),此時貼圖在視訊記憶體中

2. 貼圖內容拷貝到system memory texture,此時貼圖在記憶體中。(對顯示卡頻寬要求較高,在老舊的顯示卡上,大部分的效能都消耗在這個步驟,cpu佔用率90%以上)

3. 從system memory texture「拷貝」到windows視窗的gdi點陣圖上。這裡不是簡單的拷貝,需要對貼圖中每個畫素進行特殊處理。

4. 更新視窗內容,就完成顯示了

需要注意的是,步驟1和2中的貼圖,大小、格式一致,並且與所在視窗的客戶區大小一致(客戶區大小不一定等同於視窗大小)。所以,這裡都是逐畫素拷貝,不存在任何拉伸問題。

按照以上渲染步驟,因此,渲染前要做的準備工作如下:

1. 建立視窗

2. 建立與視窗關聯的gdi點陣圖

3. 建立system memory texture

4. 建立渲染用的texture

//華麗的分隔線

【準備步驟1】

拿上圖中的例子來說,渲染該寵物的貼圖大小為256x256,那麼首先要建立乙個同等大小的windows視窗,不帶邊框:

m_hwnd = ::createwindowex(ws_ex_topmost | ws_ex_toolwindow | ws_ex_layered, classname, title, ws_popup,0,0,256,256,0,null,hinst, null);

注意到style引數為ws_popup,就是建立不帶邊框、標題欄等內容的視窗,這時,客戶區大小等同於視窗大小。

ws_ex_layered也是至關重要的乙個屬性,用於實現視窗透明化功能,後面會提到。

其它引數請查閱msdn。

【準備步驟2】

然後是建立gdi點陣圖,**如下:

bitmapv4headerbm4;

bm4.bv4width = 256;         //視窗寬度

bm4.bv4height = -256;      //視窗高度。為什麼是負數,請查閱bitmap相關技術文件

bm4.bv4bitcount = 32;     //畫素位元數,這裡必須為32,畫素格式a8r8g8b8

bm4.bv4size = sizeof(bitmapv4header);

bm4.bv4planes = 1;

bm4.bv4v4compression = bi_rgb;

m_hdc = ::createcompatibledc( 0 );

m_hbp = ::createdibsection(m_hdc, (bitmapinfo *)&bm4, dib_rgb_colors, &m_pbitmapbits, 0, 0 );

m_holdobj = ::selectobject(m_hdc, m_hbp);

退出時記得銷毀:

if ( m_hdc )

if ( m_hbp )

變數宣告如下:

hdc m_hdc;

hbitmap m_hbp;

hgdiobj m_holdobj;

void*            m_pbitmapbits;

【準備步驟3】

建立system memory texture:

hr = dev9->createoffscreenplainsu***ce( 256, 256, d3dfmt_a8r8g8b8, d3dpool_systemmem, &m_psysmemsu***ce, null );  //d3dfmt_x8r8g8b8也可以

銷毀**:

if ( m_psysmemsu***ce )

變數宣告如下:

lpdirect3dsu***ce9 m_psysmemsu***ce; 

【準備步驟4】

建立渲染貼圖的方法就不講了,各引擎不一樣。

//華麗的分隔線

準備工作完畢後,可以開始渲染。

【渲染步驟1】先將物體渲染到rendertexture

【渲染步驟2】從rt拷貝到system memory texture

lpdirect3dsu***ce9 rt_su***ce= null;

hresult hr;

hr = rt_texture->getsu***celevel(0, &rt_su***ce);

//【渲染步驟2核心語句】

hr = dev9->getrendertargetdata( rt_su***ce, m_psysmemsu***ce );

rt_su***ce->release();

//【渲染步驟3】從system memory texture到gdi點陣圖

d3d9su***ceblt2dibperpixelalpha( m_hwnd, m_psysmemsu***ce, m_hdc, m_pbitmapbits, 0xff);

//華麗的分隔線

d3d9su***ceblt2dibperpixelalpha實現如下:

bool	d3d9su***ceblt2dibperpixelalpha( hwnd window, lpdirect3dsu***ce9 psu***ce, hdc srcdc, void *psrcdcbmp, byte wndalpha )	//只支援32bit的su***ce

; d3dsu***ce_desc d3dsu***ce_desc;

d3dlocked_rect d3drt = ;

if ( !::getclientrect(window, &wr ) )

point dstpt = ;

if (!::clienttoscreen(window, &dstpt))

hresult hr = psu***ce->lockrect( &d3drt, null, 0);//d3dlock_nosyslock|d3dlock_no_dirty_update|d3dlock_readonly );

assert( succeeded(hr) && d3drt.pbits );

if ( null == d3drt.pbits ) return false;

psu***ce->getdesc(&d3dsu***ce_desc);

//【渲染步驟3核心語句】

bltsu***ce32todib32_selfmulalpha(psrcdcbmp, d3drt.pbits, d3dsu***ce_desc.width, d3dsu***ce_desc.height, d3drt.pitch);

psu***ce->unlockrect();

size dstsz = ;

blendfunction bfc = ;

point srcpt = ;

//【渲染步驟4】這句就是通知視窗更新顯示內容,視窗必須擁有屬性ws_ex_layered

::updatelayeredwindow(window, 0, &dstpt, &dstsz,

srcdc, &srcpt, 0, &bfc, ulw_alpha );

return true;

}

//華麗的分隔線

bltsu***ce32todib32_selfmulalpha函式是用彙編實現的,有三個版本:mmx、sse、sse2,可以固定選擇乙個,也可以根據當前cpu支援的指令集動態選擇乙個。

這裡就貼乙個sse版本,該函式的具體實現細節,因為年代久遠,我也記不太清了,有問題歡迎諮詢討論 [email protected]

【渲染步驟3】的關鍵問題是gdi點陣圖需要「自乘alpha」,懶得碼字了,請參閱msdn,

blendfunction和updatelayeredwindow。

使視窗透明

define lwa colorkey 0x00000001 use color as the transparency color.define ws ex layered 0x00080000 define lwa alpha 2 use balpha to determine the opac...

directx 透明視窗

第一步 新增類class cuimagedc hbitmap getbmphandle void dword getbits void protected hbitmap m hbmp hdc m hdc char m pbits cuimagedc cuimagedc cuimagedc cuim...

透明框架視窗

如何讓整個視窗具有透明效果呢?使用如下的類 class clayeredimpl layered window template class atl no vtable clayeredimpl virtual clayeredimpl long setlayeredstyle bool setla...