2013年4月9日(7——5表面动态和页面切换)

 

距离上一次更新有一个月了,这个月进行了SHADOWMAP和HDR,幸运的是,4*4PCF已经搞定,HDR仍然在继续中。后来发现了还需要动态贴图,这个2D的引擎需要继续写下去,尽管不知道会如何。

 

闲话少说,现在看看离屏表面,第一类是后备缓冲,

 

后备缓,是指一些动画链中的表面,具有和主表面相同的尺寸和色深,创建主表面时也创建后备缓冲。

后备缓冲通常在VRAM中,另外,也可以将后备缓冲 和主表面进行页面切换。

创建关联有后备缓冲的主表面,

1,   将DDSD_BACKBUFFERCOOUNT加到dwFlags标志字段,从而使dwBackBufferCount字段有效,并填上后备缓冲数目

ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;

ddsd.dwBackBufferCount = 1;

2,   ddsCaps.dwCaps字段上加上DDSCAPS_COMPLEX和DDSCAPS_FLIP

ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP;

3,   创建主表面

lpdd->CreateSurface(&ddsd, &lpddsprimary, NULL);

4,   存放后备缓冲变量

Ddsd.ddsCaps.dwCaps = DD3CAPS_BACKBUFFER;

5,关联后备缓冲,用GetAttachedSurface()

lpddsprimary->GetAttachedSurface( & ddsd.ddsCaps, & lpddsback );

页面切换的几个步骤

1,   清除后备缓冲

2,   将场景渲染到后备缓冲

3,   用后备缓冲表面切换掉主表面

4,   锁定在某个帧速率

5,   重复步骤1

 

另外提到了显卡不足的可能,不过,现在的显卡都很大了,不存在这个问题,不过,也是一种解决问题的方法。

根据惯例,先看下运行结果

 

现在考虑引擎,使用双缓冲,首先,加上后备缓冲

DDRAW_Interface.h

LPDIRECTDRAWSURFACE7        m_lpddsback; 

Interface.cpp

构造函数中,

DDRAW_Interface::DDRAW_Interface()

{

     m_lpddsback                 = NULL;

}

析构函数中释放

 

DDRAW_Interface::~DDRAW_Interface()

{

 

     if( m_lpddsback )

     {

         m_lpddsback->Release();

         m_lpddsback = NULL;

     }

}

下一步,在MAIN函数中使用的话,要返回值,

 

LPDIRECTDRAWSURFACE7 DDRAW_Interface::getbackbuffer()

{

     return m_lpddsback;

}

下一步,进行创建带缓冲的主表面,

HRESULT DDRAW_Interface::InitDDraw( HWND hWnd, DWORD width, DWORD height, DWORD bpp,bool windowed  )

{

。。。。。。

         ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;

 

         ddsd.dwBackBufferCount = 1;

         // request primary surface

         ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP;

 

     ddsd.ddsCaps.dwCaps              = DDSCAPS_BACKBUFFER;

     lpddsprimary->GetAttachedSurface( & ddsd.ddsCaps, & lpddsback );

}

 

                        

在main()函数中,进行循环

int Game_Main(void *parms = NULL,int num_parms = 0)

{

 

     // for now test if user is hitting ESC and send WM_CLOSE

     if (KEYDOWN(VK_ESCAPE))

     {

         exit( 0 );

         //PostMessage(main_window_handle,WM_CLOSE,0,0);

     } // end if

 

    

     DDSURFACEDESC2        ddsd;   

 

     // copy the double buffer into the primary buffer

     DDRAW_INIT_STRUCT(ddsd);

 

     // lock the primary surface

     ddraw->getbackbuffer()->Lock(NULL,&ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT,NULL);

 

     // get video pointer to primary surfce

     UCHAR * back_buffer = (UCHAR *)ddsd.lpSurface;      

 

     // test if memory is linear

     if (ddsd.lPitch == SCREEN_WIDTH)

     {

         // copy memory from double buffer to primary buffer

         memset( back_buffer, 0, SCREEN_WIDTH*SCREEN_HEIGHT);

     } // end if

     else

     { // non-linear

 

         // make copy of source and destination addresses

         UCHAR *dest_ptr = back_buffer;

    

 

         // memory is non-linear, copy line by line

         for (int y=0; y < SCREEN_HEIGHT; y++)

         {

              // copy line

              memset( dest_ptr, 0, SCREEN_WIDTH);

 

              // advance pointers to next line

              dest_ptr+=ddsd.lPitch;

 

         } // end for

 

     } // end else

// you would perform game logic...

 

     // draw the next frame into the double buffer

     // plot 5000 random pixels

     for (int index=0; index < 1000; index++)

     {

         int   x   = rand()%SCREEN_WIDTH;

         int   y   = rand()%SCREEN_HEIGHT;

         UCHAR col = rand()%256;

         back_buffer[x+y*ddsd.lPitch] = col;

     } // end for index

 

     // now unlock the primary surface

     if (FAILED(ddraw->getbackbuffer()->Unlock(NULL)))

         return(0);

 

     while( FAILED( ddraw->getPrimarySurface()->Flip( NULL, DDFLIP_WAIT ) ) );

     // sleep a bit

     Sleep(30);

 

     // return success or failure or your own return code here

     return(1);

 

}

运行OK

 

第三步,就是如何根据书上的t3dlib1引擎更改。以标准些。

先看看《3D游戏编程大师技巧》中,如何说的,第3章把它们当做黑盒子对待,不过,说明的原理很透彻,

这里有必要说明下缓存交换,

方法是:同时锁定辅助缓存和主缓存,逐行复制。

具体步骤

1,   清除辅助缓存

2,   锁定辅助缓存

3,   写象素

4,   解锁辅助缓存

5,   交换主缓存和辅助缓存

这里有两个注意的地方

1,   逐行复制辅助缓存的时候,不用memcpy(),因为这个函数无法知道辅助缓存的行与行之间是否连续。因此,要根据内存跨距复制缓存,并处理每一行。

2,   访问缓存的实际内存时,必须锁定缓存,完成操作时,必须解除对缓存的锁定。

3,   窗口显示时,不允许使用双缓存(主缓存和后备缓存),而是创建一个主缓存(整个屏幕)和一个离屏缓存(大小与窗口客户区相同),换页时,把离屏后缓存复制到窗口的客户区域。即全屏FLIP(),窗口blit()

T3DLIB中,有三个函数对应这个DEMO

1,FLIP_DISPLAY()交换缓存

2,清除辅助缓存Fill_Secondary( int color)

3,清除主缓存Fill_Primary( int color )

 

先看看ddraw_Init(),发现FLIP仅使用于全屏,所以要改变到全屏区域部分,T3DLIB写了好几个判断是否全屏,我觉得写一个比较好,因为可读性容易些。窗口模式暂且不管。

现在可以加入DDRAW_FLIP(),分为全屏和窗口,

 

void DDRAW_Interface::DDraw_Flip()

{

     if( !m_bWindowed )

     {

         while( FAILED( m_lpddsprimary->Flip( NULL, DDFLIP_WAIT ) ) );

 

     }

}

并且在main()函数调用时ddraw->DDraw_Flip();即可,

现在是晚上22:52,我觉得还有必要将T3DLIB中的一些东西搞进来,对了,比如判断是否为全屏,而进行的标志,#define WINDOWED_APP                        1   //0--全屏,-窗口

将创建窗口的标志WS_POPUPWINDOW | WS_VISIBLE,改为判断

WINDOWED_APP ? (WS_OVERLAPPED | WS_SYSMENU | WS_VISIBLE ) : ( WS_POPUP | WS_VISIBLE ),

这样,以前的问题应该能够解决了,把DDRAW_INIT()中的setdisplaymode()从窗口模式中也去掉吧。

再看下DDrawFlip()m这里涉及到了主从缓冲变量,可以加上。primary_buffer 和 back_buffer,初始值均为NULL,FLIP()函数改为

 

int DDRAW_Interface::DDraw_Flip()

{

     if( m_primarybuffer || m_backbuffer )

         return ( 0 );

 

     if( !m_bWindowed )

     {

         while( FAILED( m_lpddsprimary->Flip( NULL, DDFLIP_WAIT ) ) );

 

     }

 

     return ( 1 );

}

 

 

再加上primary_lpitch和back_lpitch,来对内存定位,以及返回back_lpitch,锁定和解锁各内存,

 

UCHAR * DDRAW_Interface::DDraw_Lock_Primary_Surface()

{

     if( m_primarybuffer )

         return m_primarybuffer;

     DDSURFACEDESC2        ddsd;   

     DDRAW_INIT_STRUCT( ddsd );

     m_lpddsprimary->Lock( NULL, & ddsd, DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR, NULL );

     m_primarybuffer                      = ( UCHAR * ) ddsd.lpSurface;

     m_primarylpitch                      = ddsd.lPitch;

 

     return m_primarybuffer;

 

 

}

 

int DDRAW_Interface::DDraw_Unlock_Primary_Surface(void)

{

     if( !m_primarybuffer )

         return 0;

 

     m_lpddsprimary->Unlock( NULL );

 

     m_primarybuffer                      = NULL;

     m_primarylpitch                      = 0;

 

     return 1;

}

 

 

UCHAR * DDRAW_Interface::DDraw_Lock_Back_Surface()

{

     if( m_backbuffer )

         return m_backbuffer;

     DDSURFACEDESC2        ddsd;   

     DDRAW_INIT_STRUCT( ddsd );

     m_lpddsback->Lock( NULL, & ddsd, DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR, NULL );

     m_backbuffer                     = ( UCHAR * ) ddsd.lpSurface;

     m_backlpitch                     = ddsd.lPitch;

 

     return m_backbuffer;

 

 

}

 

 

int DDRAW_Interface::DDraw_Unlock_Back_Surface(void)

{

     if( !m_backbuffer )

         return 0;

 

     m_lpddsback->Unlock( NULL );

 

     m_backbuffer                     = NULL;

     m_backlpitch                     = 0;

 

     return 1;

}

 

UCHAR *  DDRAW_Interface::DDraw_Lock_Surface(LPDIRECTDRAWSURFACE7 lpdds,int *lpitch)

{

     if( !lpdds )

         return NULL;

 

     DDSURFACEDESC2        ddsd;   

     DDRAW_INIT_STRUCT( ddsd );

     lpdds->Lock( NULL, & ddsd, DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR, NULL );

     if( lpitch )

         * lpitch                         = ddsd.lPitch;

 

     return ( UCHAR * ) ddsd.lpSurface;;

 

 

}

 

int DDRAW_Interface::DDraw_Unlock_Surface(LPDIRECTDRAWSURFACE7 lpdds)

{

     if( !lpdds )

         return 0;

     lpdds->Unlock( NULL );

 

     return 1;

 

}

 

int DDRAW_Interface::getbacklpitch()

{

     return m_backlpitch;

}

在game_main()中,

 

int Game_Main(void *parms = NULL,int num_parms = 0)

{

 

     // for now test if user is hitting ESC and send WM_CLOSE

     if (KEYDOWN(VK_ESCAPE))

     {

         exit( 0 );

         //PostMessage(main_window_handle,WM_CLOSE,0,0);

     } // end if

 

      

     UCHAR * back_buffer = ddraw->DDraw_Lock_Back_Surface();      

 

     // test if memory is linear

     //if (ddsd.lPitch == SCREEN_WIDTH)

     if (ddraw->getbacklpitch() == SCREEN_WIDTH)

     {

         // copy memory from double buffer to primary buffer

         memset( back_buffer, 0, SCREEN_WIDTH*SCREEN_HEIGHT);

     } // end if

     else

     { // non-linear

 

         // make copy of source and destination addresses

         UCHAR *dest_ptr = back_buffer;

    

 

         // memory is non-linear, copy line by line

         for (int y=0; y < SCREEN_HEIGHT; y++)

         {

              // copy line

              memset( dest_ptr, 0, SCREEN_WIDTH);

 

              // advance pointers to next line

     //       dest_ptr+=ddsd.lPitch;

              dest_ptr+=ddraw->getbacklpitch();

 

         } // end for

 

     } // end else

// you would perform game logic...

 

     // draw the next frame into the double buffer

     // plot 5000 random pixels

     for (int index=0; index < 1000; index++)

     {

         int   x   = rand()%SCREEN_WIDTH;

         int   y   = rand()%SCREEN_HEIGHT;

         UCHAR col = rand()%256;

     //   back_buffer[x+y*ddsd.lPitch] = col;

         back_buffer[x+y*ddraw->getbacklpitch()] = col;

     } // end for index

 

     ddraw->DDraw_Unlock_Back_Surface();

     ddraw->DDraw_Flip();

 

     // sleep a bit

     Sleep(30);

 

     // return success or failure or your own return code here

     return(1);

 

}

Ok了,基本上全屏的就这个意思了。现在是4月10日0:23了,花费了6个小时。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值