您的位置:首页 > Web前端

Windows编程 32位色彩正式教学 在Direct中使用双缓冲、三缓冲(Triple Buffering)

2016-09-07 10:53 429 查看
版本:VS2015      语言:C++

 

现在估计很少有能使用32位色彩以下的显卡了吧(也没必要吧),所以我就把8位、16位、24位这样的内容跳过,直接上32位,而且所有的代码都是能在Win10 - VS2015中运行的。

 

因为是32位存储,所以调色板不需要了,那个是在8位中使用的。

 

好了,让我们来看一下初始化的代码:

// 游戏初始化
int Game_Init(void* params = NULL)
{
// 基础设置
if (FAILED(DirectDrawCreateEx(NULL, (void**)&lpdd, IID_IDirectDraw7, NULL))) //获取d7对象
return 0;
if (FAILED(lpdd->SetCooperativeLevel(main_window_handle,
//DDSCL_NORMAL
DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX | DDSCL_EXCLUSIVE | DDSCL_ALLOWREBOOT
))) //跟windows协作等级设置为全屏,这是最常用的参数
return 0;
if (FAILED(lpdd->SetDisplayMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, 0, 0))) //设置显示模式,如果设置为8位会直接出错
return 0;

// 开始创建显示主界面
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS; //表明ddsCaps是个有效成员
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; //表明该界面是主界面

lpdd->CreateSurface(&ddsd, &lpddsprimary, NULL); //根据界面描述创建主界面

return 1;
}


一样的,就是去掉了调色板,首先获取d7对象,然后创建主界面描述,最后根据描述创建主界面。

 

现在给大家带来最关心的如何使用32位绘图,这边我使用了两中方法,原理是一样的:

LPWSTR msg = new TCHAR[1024];
// 弹出消息
void popMessage(LPWSTR str)
{
MessageBox(main_window_handle, str, TEXT("提示"), MB_OK);
}

// 32位像素上色
// 不理解的童鞋想一下:我们一般说的都是argb,内存中也是如此排列的,a是放在的是最高位,b是最低位,按照习惯从左往右读就是argb。
// 但是要记住char[0]到char[4]这样排列实际上是内存从小到大排列的,所以写的时候感觉是相反的
void Plot_Pixel_Fast32(int x, int y, int red, int green, int blue, int alpha, UCHAR* video_buffer, int lpitch)
{
DWORD pixel_addr = 4 * x + y * lpitch;	//获取当前要写入的地址

video_buffer[pixel_addr] = blue;	//写入颜色
video_buffer[pixel_addr + 1] = green;
video_buffer[pixel_addr + 2] = red;
video_buffer[pixel_addr + 3] = alpha;
}

// 游戏主循环
int Game_Main(void* params = NULL)
{
// 判断是否要退出
if (KEYDOWN(VK_ESCAPE))
PostMessage(main_window_handle, WM_CLOSE, 0, 0);

// 初始化主界面描述
DDRAW_INIT_STRUCT(ddsd);

if (FAILED(lpddsprimary->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR, NULL)))	//加锁
{
wsprintf(msg, TEXT("LOCK 出错了"));
popMessage(msg);
}

//画颜色
UCHAR *video_buffer = (UCHAR*)ddsd.lpSurface;
for (int x = 0; x < 640; ++x)
for (int y = 0; y < 480; ++y)
Plot_Pixel_Fast32(x, y, 0, 255, 0, 128, video_buffer, ddsd.lPitch);

if (FAILED(lpddsprimary->Unlock(NULL)))	//解锁
{
wsprintf(msg, TEXT("UNLOCK 出错了"));
popMessage(msg);
}
return 1;
}

是不是感觉比8位的还方便?直接在获取主界面缓存中写就行了,让我们再来简化简化,首先加入另一方法:

#define _RGB32BIT(a, r, g, b) ((b) + (g << 8) + (r << 16) + (a << 24))
// 32位像素上色
// 使用第二种方法
void Plot_Pixel_Fast32_2(int x, int y, int red, int green, int blue, int alpha, UINT* video_buffer, int lpitch)
{
video_buffer[x + y * (lpitch>>2)] = (UINT)(_RGB32BIT(alpha, red, green, blue));	//使用宏直接写,有点区别的是lpitch需要除以4,因为lpitch算的是横向的字节数,而我们把主界面的内存弄成UINT型,是32位、4个字节的,上一节中是我理解的不够深刻
}

然后游戏循环中的画颜色步骤替换成一下代码:

UINT *video_buffer = (UINT*)ddsd.lpSurface;
for (int x = 0; x < 640; ++x)
for (int y = 0; y < 480; ++y)
Plot_Pixel_Fast32_2(x, y, 255, 0, 0, 128, video_buffer, ddsd.lPitch);

注意这边用的是int了,最后来看看结果:



嗯,大红色(当然不是用画图工具画出来的!)。绘制的方式还是比较简单的,仔细分清各个步骤,不要写错就能出结果。

 

下面来看看界面的双重缓存。

 

现在游戏应该是3层缓存、或者可能更多吧,主要还是提高游戏运行的流畅度,玩家们想想看CPU为什么要加3级高速缓存呢?其实也就是这个道理吧。

 

让我们来看看代码:

// 游戏初始化
int Game_Init(void* params = NULL)
{
// 基础设置
if (FAILED(DirectDrawCreateEx(NULL, (void**)&lpdd, IID_IDirectDraw7, NULL)))	//获取d7对象
return 0;
if (FAILED(lpdd->SetCooperativeLevel(main_window_handle,
//DDSCL_NORMAL
DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX | DDSCL_EXCLUSIVE | DDSCL_ALLOWREBOOT
)))	//跟windows协作等级设置为全屏,这是最常用的参数
return 0;
if (FAILED(lpdd->SetDisplayMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, 0, 0)))	//设置显示模式,如果设置为8位会直接出错
return 0;

// 开始创建显示主界面
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;	//表明ddsCaps是个有效成员,并且拥有后备的缓冲
ddsd.dwBackBufferCount = 1;	//表明有一个缓冲
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | //表明该界面是主界面
DDSCAPS_COMPLEX | //表明拥有缓冲链
DDSCAPS_FLIP;	//表明是反正结构的一部分,上面的参数相当于是有缓冲,而这个参数表明可以切换缓冲

lpdd->CreateSurface(&ddsd, &lpddsprimary, NULL);	//根据界面描述创建主界面

// 开始创建后备界面,
// 这边其实不算是创建,创建主界面的时候所有的后备表面已经创建完成,
// 我们之后的工作只是把最后一个表面的指针取出来
ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;	//表明该界面是后备界面
if (FAILED(lpddsprimary->GetAttachedSurface(&ddsd.ddsCaps, &lpddsback)))	//通过主界面创建出备用表面
{
popMessage(TEXT("创建备用表面出错了"));
return 0;
}

return 1;
}

// 游戏主循环
int Game_Main(void* params = NULL)
{
// 判断是否要退出
if (KEYDOWN(VK_ESCAPE))
PostMessage(main_window_handle, WM_CLOSE, 0, 0);

// 初始化主界面描述
DDRAW_INIT_STRUCT(ddsd);

if (FAILED(lpddsback->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR, NULL)))	//有备用表面时用备用表面加锁
{
wsprintf(msg, TEXT("LOCK 出错了"));
popMessage(msg);
}

//画颜色
UINT *video_buffer = (UINT*)ddsd.lpSurface;
for (int x = 0; x < 640; ++x)
for (int y = 0; y < 480; ++y)
Plot_Pixel_Fast32_2(x, y, 0, 0, 255, 128, video_buffer, ddsd.lPitch);

if (FAILED(lpddsback->Unlock(NULL)))	//解锁
{
wsprintf(msg, TEXT("UNLOCK 出错了"));
popMessage(msg);
}

while (FAILED(lpddsprimary->Flip(NULL, DDFLIP_WAIT)));	//切换界面,这边的while不是很懂,应该每次只会调用一次

return 1;
}

在创建主界面的时候设置后备的参数,然后再从主界面取出备用界面就OK了。在游戏循环中直接在备用界面中着色,完成后由主界面进行切换。

 

那么怎么创建3重缓冲呢?书上没有说,我一开始以为要创建两次,然后我就google了一下,在微软的官网的找到了这么一句话:

You do not need to keep track of all surfaces in a triple buffered flippingchain. The only surfaces you must keep pointers to are the primary surface andthe back-buffer surface.

 

意思就是不用显示调用循环链,直接放到备用缓冲里面就可以了,玩家们仔细看看我对后备缓冲创建时的注释。

 

嗯,这样就很简单了,直接把dwBackBufferCounting设置为2就行了,其他的操作跟双缓冲一样。

 

好了,这边的难度感觉确实要高点了,我看的有点慢,下一节还会讲第七章,是如何把图像拷贝到主界面中,按cocos里来说就是创建Sprite精灵。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: