我的DirectDraw7学习之旅
学习DirectDraw大概两个月,这两个月也是我系统地学习游戏编程的两个月。所以在这个时间段里,我还学习了其他的东西,例如C++----当然,C++不是容易学的,我只学了点皮毛-----后来看《C++编程经典》,看到满世界的对象,顿时觉得自己的程序中的那些“面向对象”是多么粗糙!
我的整个学习过程都是以渐进式做东西而推进的,即是学到一个阶段了就会去做一个游戏---这样学习很快,可以很快的把握住一个整体的脉络!
下面是我在自己的学习日志中记录的一些东西,前面一些比较零散,后面有专门做总结的,有意见的或者想要详细资料的可以Email我。
2005.12.1
要在窗口模式下对页面进行象素级别的操作,只能判断当前颜色深度从而执行不同的代码。在窗口模式下,默认的页面位深度是和当前屏幕的位深度相同的,当然你可以指定一个颜色深度来创建表面,但是网友们说那没有多大意义。而我觉得有意义的地方在于:可以只写一分代码,而不用去根据当前桌面位深度去执行不同的代码!事实上,要在窗口模式下创建指定位深度的页面,只能建立非RGB模式的页面,且只能放进系统内存!
而在全屏模式下,其他页面的位深度也和主页面相同。
2005.11.16
对于位图的象素级操作,无非就是从点阵信息的每个点中取出颜色信息,然后再构造成页面允许的格式写入页面,从而达到Blt。
2005.11.17
模糊效果,简单的方法是取目标点四周4点的平均值。但是如果对于同一个页面,这样显然是不行的。(每次都会影响旁边的点)。
所以应该用两个页面(当然也可以放置两个内存区域,都是一样的原理)进行这种操作,在页面A取目标点四周4点的平均值,然后在把这个值写入页面B的目标位置处,然后页面B就会比页面A模糊一点!(在xheartblue的网站上的Fireworks程序中就用到了这个技术----事实上我的这个方法就是从他的那个程序里学到的。当初在学习粒子系统的时候,想找份原代码,就发邮件给那篇文章的作者,就是xheartblue,他就给了我他的个人网站地址,使我得到了那份代码~)
2005.11.18
DirectDraw学习总结
1.
首先要明白的是,DirectDrawSurface 对象是内存(显存)里的一块连续的存储区。
2.
当一个页面被锁定(调用Lock或者GetDC)后,再调用其 Blit方法向其它页面位块传送,就会返回DDERR_SURFACEBUSY或者DDERR_LOCKEDSURFACES的错误信息。
3.
结构即使刚定义也最好用ZeroMemory将其清0,否则创建页面等就不会成功。DirectDraw
4.
贴位图到页面上:
HDC hmemDC=CreateCompatibleDC(NULL);
HBITMAP hBitMap=(HBITMAP)LoadImage(NULL,FileName,
IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
SelectObject(hmemDC,hBitMap);
LpDDSSurface->GetDC(&hdc);
BitBlt(hdc,0,0,width,height,hmemDC,0,0,SRCCOPY);
LpDDSSurface->ReleaseDC(hdc);
5.无论是在窗口模式下还是在全屏幕模式下,主页面都代表整个屏幕。创建主页面上时就不需要指定其大小,而创建离屏页面就要指定。
6.
果你要把页面A以DDBLT_SRCBLT方式传送到页面B,即页面A是作为源页面,那么在你就要给页面A设置颜色键。
7.
计时机制的Tick=GetTickCount()不能放到消息循环中,否则当没有消息时,Tick就得不到更新。
8.
默认情况下,离屏页面的位深度等于主页面的位深度,在窗口模式下,主页面的位深度为当前屏幕设置的位深度。
9.
得到一个页面的位深度,可以先Lock后,从填充的ddsd.ddpfPixelFormat.dwRGBBitCount得到,也可以调用IdirectDrawSurface::GetPixelFormat来得到,对于16位页面,还可以得到其是565格式还是555格式。(如果是565格式,dwRGBBitCount==16,555就等于15),
由于一般每个页面位深度都是一样的,都和当前的显示模式相同—即使显示模式是你自己定义的(即全屏幕模式下SetDisplayMode),所以还可以调用IdirectDraw4::GetDisplayMode(LPDDSURFACEDESC2);从ddsd.ddpixelFormat.dwRGBBitCount得到!
10.
对页面进行象素级的操作,先 Lock住一个页面,然后就以ddsd.lpSurface(void*类型的),和ddsd.lPitch。对于24位和32位页面,由于RGB都有一个字节,所以A:取lpSurface为BYTE*型的,自己移动来取得RGB颜色分量。B:取 lpSurface为DWORD(4字节,24位由于没有哪种数据类型为3字节的,所以这个方法对它不行),用来容纳一个点(包含了RGB颜色分量在其中),然后从其中分离得到RGB颜色分量。反之,将颜色分量(或整个象素)写进lpSurface即可达到写页面的目的。
11.
关于位图:24位位图的三基色是以BGR的形式排列的。读入位图数据,一般只需要点阵数据:二进制形式打开文件
à读入fileHeader,如果bfType!=0x4d42则不是BMP文件-
à读入infoHeader,取得width,height,bits 来确定分配多大的内存空间---
à分配内存--
à移动文件指针( 根据fileHeader的bfOffBits)到点阵数据处,读点阵数据放到刚刚分配的内存。从内存中根据坐标分离每个点的颜色信息,然后写入页面,即可达到BLT到页面的作用。
12.
简易算法(由xHeartBlue的 烟火程序学来)
Blur
创建两个屏幕大小的位图()这里是对整个屏幕进行模糊,创建位图本质上是分配内存,主要是此技术模拟了
DIB
),从位图
A
取一个点周围四点的颜色信息(需要分离
RGB
)的加权值,然后将结果放入位图
B
及后台缓冲页面,这样当处理完
A
的所有数据时,
B
中就有了一幅相对较模糊的图案,交换两个位图的地址(就是交换两个指针的值),循环调用这一过程,这样就可以达到整个屏幕模糊到什么也看不到的效果。
2005.11.22
对于淡入淡出效果,其实就是Alpha-Blending的灵活运用。 Alpha-Blending技术是一种象素混合技术,简单的说,就是先分别从目标页面(并不局限于页面)和源页面取出象素的RGB颜色信息,再分别对每个象素的RGB三基色做 Alpha混合运算,最后把结果写入目标页面,这个时候目标页面就是两个页面 Alpha混合后的结果!
Alpha混合的基本公式是:result=Alpha*SrcPixel+(1-Alpha)*destPixel其中0<=Alpha<=1;有时候因为速度问题会对这个公式进行一定的变形!---从这个公式可以看出,其实就是要让最后的结果既有源象素的特征又有目标象素的特征,这样看上去就会呈现出混合效果了!
首先我们要会对页面进行象素级别的操作------因为必须要去取出象素—然后把象素里的RGB三基色分离出来。例如以下为16位565格式,假设color已经存储了一个象素。
得到三基色的方法:blue=color&0x1f;
green=(color>>5)&0x3f;
red=(color>>11)&0x1f;
很简单的位操作,在一个象素中,RED在最高5位,中间6位是绿色,最低5位是兰色。
分离了颜色信息后,再分别对每个象素的三基色进行Alpha混合。
db=((sb-db)*alpha)/32+db;
dg=((sg-dg)*alpha)/32+dg;
dr=((sr-dr)*alpha)/32+dr;
db代表destBlue,sb代表sourceBlue,其他的变量就可以自己推出。这里为什么没有采用>>5而是要去/32呢?我也不清楚,反正我当时用>>5为时没有成功,颜色信息是乱的,用/32是正确的。希望高手帮我指正一下!
最后再把颜色信息合成起来,并放进目标表面即可。
Color=blue | ( green<<5 ) | ( red <<11 );
以下是详细代码,它工作在16位色深模式:
bool AlphaBlend16(LPDIRECTDRAWSURFACE7 lpDDSDest,RECT RectDest,LPDIRECTDRAWSURFACE7 lpDDSSrc,RECT RectSrc,int alpha)
{
if(RectDest.right-RectDest.left!=RectSrc.right-RectSrc.left||
RectDest.bottom-RectDest.top!=RectSrc.bottom-RectSrc.top )
return false;
DDSURFACEDESC2 ddsdDest,ddsdSrc;
WORD *lpDest,*lpSrc;
WORD DestColor,SrcColor;
WORD db,dg,dr;
WORD sb,sg,sr;
//alpha在0,32之间
ddsdDest.dwSize=ddsdSrc.dwSize=sizeof(DDSURFACEDESC2);
lpDDSDest->Lock(NULL,&ddsdDest,DDLOCK_WAIT,NULL);
lpDDSSrc->Lock(NULL,&ddsdSrc,DDLOCK_WAIT,NULL);
//得到第一个象素的地址
lpDest=(WORD*)ddsdDest.lpSurface;
lpSrc=(WORD*)ddsdSrc.lpSurface;
for(int y=RectDest.top;y<RectDest.bottom;y++)
for(int x=RectDest.left; x<RectDest.right;x++)
{
//先得到颜色
DestColor=lpDest[x+y*(ddsdDest.lPitch>>1)];
SrcColor=lpSrc[x+y*(ddsdSrc.lPitch>>1)];
//然后分离颜色,565模式
db=DestColor&0x1f;
sb=SrcColor&0x1f;
dg=(DestColor>>5)&0x3f;
sg=(SrcColor>>5)&0x3f;
dr=(DestColor>>11)&0x1f;
sr=(SrcColor>>11)&0x1f;
//Alpha混合运算
db=((sb-db)*alpha)/32+db;
dg=((sg-dg)*alpha)/32+dg;
dr=((sr-dr)*alpha)/32+dr;
// 合成新的颜色并写入
lpDest[x+y*(ddsdDest.lPitch>>1)]=db|(dg<<5)|(dr<<11);
}
lpDDSDest->Unlock(NULL);
lpDDSSrc->Unlock(NULL);
return true;
}
OK~本文就暂告一段落了,全文没有什么很高深的技术,甚至是些过时的技术。我只是把它写出来给自己做个总结,也算是复习吧!
有问题请MAIL我zmhn320@163.com