桌面透明窗口程序渲染

编程入门 行业动态 更新时间:2024-10-07 09:29:35

桌面<a href=https://www.elefans.com/category/jswz/34/1768258.html style=透明窗口程序渲染"/>

桌面透明窗口程序渲染

        市面上基本所有的3D游戏都依赖一个普通的windows窗口,包含标题栏、边框、最小化、最大化、关闭按钮。窗口的大小决定了玩家可视的游戏空间,整个窗口的像素都被游戏内容填充满,窗口背景不是透明的。渲染时,只要创建一个主渲染缓冲区,将各元素渲染在上面,再显示就可以了。

        本文介绍一种方法,窗口的背景是透明的,窗口中只渲染主要的游戏元素,比如主角,而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 )
{
::SelectObject( m_hDC, m_hOldObj );
::DeleteDC(m_hDC);
m_hDC = NULL;
}
if ( m_hBp )
{
::DeleteObject( m_hBp );
m_hBp = NULL;
}

变量声明如下:

HDC m_hDC;
HBITMAP m_hBp;
HGDIOBJ m_hOldObj;
void *            m_pBitmapBits;


【准备步骤3】

创建System Memory Texture:
hr = dev9->CreateOffscreenPlainSurface( 256, 256, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &m_pSysMemSurface, NULL );  //D3DFMT_X8R8G8B8也可以

销毁代码:

if ( m_pSysMemSurface )
{
m_pSysMemSurface->Release();
m_pSysMemSurface = NULL;
}

变量声明如下:

LPDIRECT3DSURFACE9 m_pSysMemSurface; 


【准备步骤4】

创建渲染贴图的方法就不讲了,各引擎不一样。


//华丽的分隔线


准备工作完毕后,可以开始渲染。

【渲染步骤1】先将物体渲染到RenderTexture

【渲染步骤2】从RT拷贝到System Memory Texture

LPDIRECT3DSURFACE9 rt_surface= NULL;  

HRESULT hr;

hr = rt_texture->GetSurfaceLevel(0, &rt_surface);

//【渲染步骤2核心语句】

hr = dev9->GetRenderTargetData( rt_surface, m_pSysMemSurface );
rt_surface->Release();

//【渲染步骤3】从System Memory Texture到GDI位图

D3D9SurfaceBlt2DIBPerPixelAlpha( m_hWnd, m_pSysMemSurface, m_hDC, m_pBitmapBits, 0xff);


//华丽的分隔线


D3D9SurfaceBlt2DIBPerPixelAlpha实现如下:

bool	D3D9SurfaceBlt2DIBPerPixelAlpha( HWND window, LPDIRECT3DSURFACE9 pSurface, HDC srcDC, void *pSrcDCBmp, BYTE wndAlpha )	//只支持32bit的surface
{RECT	wr = {0};D3DSURFACE_DESC		d3dsurface_desc;D3DLOCKED_RECT		d3drt = {0};if ( !::GetClientRect(window, &wr ) ){return false;}POINT	dstPT = {wr.left, wr.top};if (!::ClientToScreen(window, &dstPT)){return false;}HRESULT hr = pSurface->LockRect( &d3drt, NULL,  0);//D3DLOCK_NOSYSLOCK|D3DLOCK_NO_DIRTY_UPDATE|D3DLOCK_READONLY );assert( SUCCEEDED(hr) && d3drt.pBits );if ( NULL == d3drt.pBits )	return	false;pSurface->GetDesc(&d3dsurface_desc);//【渲染步骤3核心语句】BltSurface32ToDIB32_SelfMulAlpha(pSrcDCBmp, d3drt.pBits, d3dsurface_desc.Width, d3dsurface_desc.Height, d3drt.Pitch);pSurface->UnlockRect();SIZE	dstSZ = {wr.right - wr.left, wr.bottom - wr.top};BLENDFUNCTION	bfc = { AC_SRC_OVER, 0, wndAlpha, AC_SRC_ALPHA };POINT	srcPT = {0, 0};//【渲染步骤4】这句就是通知窗口更新显示内容,窗口必须拥有属性WS_EX_LAYERED::UpdateLayeredWindow(window, 0, &dstPT, &dstSZ, srcDC, &srcPT, 0, &bfc, ULW_ALPHA );return	true;
}


//华丽的分隔线


BltSurface32ToDIB32_SelfMulAlpha函数是用汇编实现的,有三个版本:MMX、SSE、SSE2,可以固定选择一个,也可以根据当前cpu支持的指令集动态选择一个。

这里就贴一个SSE版本,该函数的具体实现细节,因为年代久远,我也记不太清了,有问题欢迎咨询讨论 unknowfish@163。

【渲染步骤3】的关键问题是GDI位图需要“自乘alpha”,懒得码字了,请参阅MSDN,

BLENDFUNCTION和UpdateLayeredWindow。

void	BltSurface32ToDIB32_SelfMulAlphaSSE( void *pDst, void *pSrc, unsigned int width, unsigned int height, unsigned int src_pitch )
{int	src_pitch_sub_dst_pitch;//src pointer对齐到下一行scanline,需要跳过多少字节__asm{//取参数,判断width和height是否有任一为0mov		eax, height		//eax = heighmov		ebx, widthmul		ebx				//width * heighttest	eax, eax		//影响ZFjz		end_pixel//常量赋值mov		esi, pSrcmov		edi, pDstpcmpeqd mm5, mm5	//mm5 = 0xffffffff_ffffffffpxor mm7, mm7		//mm7 = 0x0psrld mm5, 8		//mm5 = 0x00ffffff_00ffffff//判断pitchmov		edx, src_pitchshl		ebx, 2		//每个像素4个字节, dst_pitch = width * 4sub		edx, ebx	//src_pitch - dst_pitchjnz		diff_pitch//same_pitch:mov		ecx, eaxmov		edx, 1		//how many lines,eax和edx构成2层循环and		ecx, 1		//一行上剩下多少个不成对的象素,same_pitch时就是(width*height & 1),diff_pitch时就是(width & 1)shr		eax, 1		//一行上主循环多少次,same_pitch时就是(width*height >> 1)diff_pitchh时就是(width >> 1)jmp		test_pair_pixeldiff_pitch:mov		src_pitch_sub_dst_pitch, edx	//src_pitch - dst_pitchmov		eax, widthmov		edx, height	//how many lines,eax和edx构成2层循环mov		ecx, eaxand		ecx, 1		//一行上剩下多少个不成对的象素,same_pitch时就是(width*height & 1),diff_pitch时就是(width & 1)shr		eax, 1		//一行上主循环多少次,same_pitch时就是(width*height >> 1)diff_pitchh时就是(width >> 1)mov		ebx, eax	//main loop count on every scanlinejmp		test_pair_pixelloop_line:mov		eax, ebxloop_pair_pixel:movq mm0, [esi]		//mm0 = 0xaarrggbb_AARRGGBBmovq mm4, mm5		//mm4 = mm5 = 0x00ffffff_00ffffffmovq mm1, mm0		//mm1 = mm0 = 0xaarrggbb_AARRGGBBpandn  mm4, mm0		//保存alpha, mm4 = 0xaa000000_AA000000punpcklbw mm0, mm7	//mm0 = 0x00AA_00RR_00GG_00BBpunpckhbw mm1, mm7	//mm1 = 0x00aa_00rr_00gg_00bbpshufw	mm2, mm0, 0xff	//mm2 = 0x00AA_00AA_00AA_00AApshufw	mm3, mm1, 0xff	//mm3 = 0x00aa_00aa_00aa_00aapmullw mm0, mm2		//自乘alpha,字组相乘,取低16位pmullw mm1, mm3psrlw mm0, 8		//除以256psrlw mm1, 8packuswb mm0, mm0	//合并单个象素packuswb mm1, mm1punpckldq mm0, mm1	//将2个象素合并pand mm0, mm5		//恢复原始alphapor mm0, mm4//put_pixel:MOVNTQ [edi], mm0add esi, 8add edi, 8dec eax
test_pair_pixel:jnz	loop_pair_pixel//rest_line_pixel:jecxz	next_line	//scanline_rest_pixel不是0就是1movq mm0, [esi]		//mm0 = 0xaarrggbb_AARRGGBBmovq mm4, mm5		//mm4 = mm5 = 0x00ffffff_00ffffffmovq mm1, mm0		//mm1 = mm0 = 0xaarrggbb_AARRGGBBpandn  mm4, mm0		//保存alpha, mm4 = 0xaa000000_AA000000punpcklbw mm0, mm7	//mm0 = 0x00AA_00RR_00GG_00BBpunpckhbw mm1, mm7	//mm1 = 0x00aa_00rr_00gg_00bbpshufw	mm2, mm0, 0xff	//mm2 = 0x00AA_00AA_00AA_00AApshufw	mm3, mm1, 0xff	//mm3 = 0x00aa_00aa_00aa_00aapmullw mm0, mm2		//自乘alpha,字组相乘,取低16位pmullw mm1, mm3psrlw mm0, 8		//除以256psrlw mm1, 8packuswb mm0, mm0	//合并单个象素packuswb mm1, mm1punpckldq mm0, mm1	//将2个象素合并pand mm0, mm5		//恢复原始alphapor mm0, mm4movd	[edi], mm0add esi, 4add edi, 4next_line:add	esi, src_pitch_sub_dst_pitch	//设置指针到下一个src行dec	edxjnz	loop_lineemmsend_pixel:}
}


更多推荐

桌面透明窗口程序渲染

本文发布于:2024-02-14 07:39:21,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1762385.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:透明   桌面   窗口   程序

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!