市面上基本所有的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...