2D Game Programming 한국산업기술대학교 게임공학과 정내훈
개요 표면 표시가능한메모리 팔레트 DirectDraw 로그리기 2
표면 (Surface) 정의 : 실제그래픽이그려지는장소 모니터로나오는것은주표면하나뿐 위치 : 메모리영역 한프로그램에서여러개의표면을다룰수있다. 주표면 비디오스크린자체 3
표면 (Surface) 메모리 표면 메인메모리 표면 표면 표면 표면 표면 비데오메모리 표면 표면 4
표면 (Surface) 성질 표면은임의의크기를가질수있다. 예외 ) 주표면 : 화면해상도와일치해야한다. 비디오메모리와시스템메모리에작성할수있다. 비디오메모리가빠르다. 비트깊이와색상공간으로속성정의 같은속성을갖는경우데이터를교환할수있다. 5
표면 (Surface) 종류 주표면 Primary Display Surface 모니터에표시되는표면 한개만존재 보조표면 Secondary Display Surface 후면버퍼 (back buffer) 라고도불린다. 주표면과같은속성 (, 크기 ) 애니메이션을위한다음준비표면 오프스크린표면 실제모니터와연결되는일이없음 하드웨어가속을위해이미지를저장하는표면 6
표면 (Surface) DirectDraw 에서의표면 표면자체의포인터 LPDIRECTDRAWSURFACE7 표면속성자료구조 LPDDSURFACEDESC2 표면생성 API HRESULT CreateSurface( LPDDSURFACE2 lpddsurfacedesc, LPDIRECTDRAWSURFACE7 FAR *lplpddsurface, Iunknown FAR *punkouter); 성공시 DD_OK 7
표면 (Surface) DirectDraw 표면기술자구조체 Surface descriptor structure typedef struct _DDSURFACEDESC2 { DWORD dwsize; // size of the DDSURFACEDESC structure DWORD dwflags; // determines what fields are valid DWORD dwheight; // height of surface to be created DWORD dwwidth; // width of input surface union { LONG lpitch; // distance to start of next line (return value only) DWORD dwlinearsize; // Formless late-allocated optimized surface size } DUMMYUNIONNAMEN(1); union { DWORD dwbackbuffercount; // number of back buffers requested DWORD dwdepth; // the depth if this is a volume texture } DUMMYUNIONNAMEN(5); union { DWORD dwmipmapcount; // number of mip-map levels requestde // dwzbufferbitdepth removed, use ddpfpixelformat one instead DWORD dwrefreshrate; // refresh rate (used when display mode is described) DWORD dwsrcvbhandle; // The source used in VB::Optimize } DUMMYUNIONNAMEN(2); DWORD dwalphabitdepth; // depth of alpha buffer requested DWORD dwreserved; // reserved LPVOID lpsurface; // pointer to the associated surface memory... 8
표면 (Surface) DirectDraw 표면기술자구조체 Surface descriptor structure typedef struct _DDSURFACEDESC2 {....... union { DDCOLORKEY ddckckdestoverlay; // color key for destination overlay use DWORD dwemptyfacecolor; // Physical color for empty cubemap faces } DUMMYUNIONNAMEN(3); DDCOLORKEY ddckckdestblt; // color key for destination blt use DDCOLORKEY ddckcksrcoverlay; // color key for source overlay use DDCOLORKEY ddckcksrcblt; // color key for source blt use union { DDPIXELFORMAT ddpfpixelformat; // pixel format description of the surface DWORD dwfvf; // vertex format description of vertex buffers } DUMMYUNIONNAMEN(4); DDSCAPS2 ddscaps; // direct draw surface capabilities DWORD dwtexturestage; // stage in multitexture cascade } DDSURFACEDESC2; 9
표면 (Surface) DDSURFACEDESC2 의용도 표면의속성을정의하거나수정할때사용 모든멤버를다정의해야할필요는없음 필요한멤버를정의하고정의된멤버를 dwflags 에지정하면됨 dwsize 를지정하는것을잊으면안됨 10
표면 (Surface) dwflag 의설정들 값 DDSD_ALL DDSD_HEIGHT DDSD_WIDTH DDSD_PITCH DDSD_PIXELFORMAT DDSD_LPSURFACE DDSD_CAPS DDSD_ALPHABITDEPTH 의미모든멤버가유효 dwheight가유효 dwwidth가유효 lpitch가유효 ddpfpixelformat이유효 lpsurface가유효 ddscaps가유효 dwalphabitdept가유효 11
표면 (Surface) ddcaps 란? 화면에부가적인속성을지정할때사용 typedef struct _DDSCAPS2 { DWORD dwcaps; // capabilities of surface wanted DWORD dwcaps2; DWORD dwcaps3; union { DWORD dwcaps4; DWORD dwvolumedepth; } DUMMYUNIONNAMEN(1); } DDSCAPS2; dwcaps 만사용 dwcaps2, dwcaps3, dwcaps4 는 3D 용 12
표면 (Surface) DwCaps 의설정들 값 의미 DDSCAPS_BACKBUFFER 표면이후면버퍼가된다. DDSCAPS_FLIP 플립이가능하다. DDSCAPS_FRONTBUFFER 표면이전면버퍼가된다. DDSCAPS_OFFSCREENPLAIN 표면이오버레이, 텍스쳐, z- 버퍼, 전면버퍼, 후면버퍼가아닌오프스크린버퍼이다 DDSCAPS_OWNDC DDSCAPS_PRIMARYSURFACE DC 를갖는다 주표면이다 DDSCAPS_SYSTEMMEORY 주메모리에할당된다. 13
표면 (Surface) 주표면생성하기 LPDIRECTDRAW7 lpdd = NULL; // dd object LPDIRECTDRAWSURFACE7 lpddsprimary = NULL; // dd primary surface DDSURFACEDESC2 ddsd; // a direct draw surface description struct if (DirectDrawCreateEx(NULL, (void **)&lpdd, IID_IDirectDraw7, NULL)!=DD_OK) return(0); // set cooperation level to windowed mode normal if (lpdd->setcooperativelevel(main_window_handle, DDSCL_ALLOWMODEX DDSCL_FULLSCREEN DDSCL_EXCLUSIVE DDSCL_ALLOWREBOOT)!=DD_OK) return(0); // set the display mode if (lpdd->setdisplaymode(screen_width,screen_height,screen_bpp,0,0)!=dd_ok) return(0); // Create the primary surface memset(&ddsd,0,sizeof(ddsd)); ddsd.dwsize = sizeof(ddsd); ddsd.dwflags = DDSD_CAPS; ddsd.ddscaps.dwcaps = DDSCAPS_PRIMARYSURFACE; if (lpdd->createsurface(&ddsd,&lpddsprimary,null)!=dd_ok) return(0); 14
표면 (Surface) 주표면생성하기 잊지말것 if (lpddsprimary!=null) lpddsprimary->release(); // release the directdraw object if (lpdd!=null) lpdd->release(); 15
실습 프로그램 Prog9_1.cpp 16
메모리주소와픽셀의위치 메모리와픽셀의대응 가로세로 w, h 의크기를갖는표면 (0,0) 메모리주소증가방향 (dwbpp / 8 만큼증가 ) 메모리주소증가방향 (lpitch 만큼증가 ) (w-1, h-1) 17
메모리주소와픽셀의위치 선형 vs 비선형메모리 dwbpp / 8 * w = lpitch???? 선형메모리 : YES 비선형메모리 : NO 하나의가로줄의메모리크기가 2^n 형태로표현되지않을때 lpitch 가 2^n 이되면서비선형메모리가될수있다. 2^n 이되면곱하기가아닌 shift 연산으로좌표를계산할수있다. 1024 * x = x << 10
메모리주소와픽셀의위치 비선형메모리 0 (0,0) dwbpp / 8 * (w 1) 2^n - 1 2^n 2^n * 2 메모리주소증가방향 (dwbpp / 8 만큼증가 ) 메모리주소증가방향 (lpitch 만큼증가 ) 화면에표시되지 않는 영역 (w-1, h-1) 19
메모리주소와픽셀의위치 픽셀좌표계산 픽셀의메모리주소 표면의주소 + X 좌표 * dwbpp / 8 + Y 좌표 * dwwidth * dwbpp / 8 선형메모리일경우만옳음 표면의주소 + X 좌표 * dwbpp / 8 + Y 좌표 * lpitch 픽셀의 Color 변환법 UCHAR *video_buffer; // 8 BPP // Width 가 1024 인경우 video_buffer[x + 1024*y] = color; UINT *video_buffer; // 32 BPP // Width 가 1024 인경우 video_buffer[x + 1024*y] = color; 20
DirectDraw 의그래픽 API DirectDraw 는가장기본적인 API 만제공한다. 색상설정 표면메모리접근 HW 가속 : 채움 (Filling), 블리팅 (BitBlt) 그러면선? 원? 다각형? 위의 API 로각자제작해야한다!
팔레트모드 과거메모리절약을위해사용하던 color 간접지정방식 사용할 color 의리스트를테이블에저장하고실제표면에는리스트의 index 를저장 일반적으로 256 크기의테이블사용 BPP 가 8 이됨 자세한내용은생략
픽셀그리기 그리기위해필요한사항 표면의주소 BPP, width 표면의주소 DDSD의 lpsurface lpsurface를얻는 API : Lock HRESULT Lock(LPRECT lpdestrecct, LPDDSURFACEDESC2 lpddsd, DWORD dwflags, HANDLE hevent); 23
픽셀그리기 Lock 표면의위치는 DirectX 가관리, 언제든지이동가능 따라서사용하기위해서는움직이지않게고정 (lock) 시킬필요가있음. HRESULT Lock(LPRECT lpdestrecct, // 변경하고자하는영역 LPDDSURFACEDESC2 lpddsd, // 얻는표면의속성 DWORD dwflags, HANDLE hevent); // 얻고자하는속성 // 사용되지않음, 반드시 NULL 24
픽셀그리기 Lock 의플랙 DDLOCK_READONLY The surface locked is readable only. DDLOCK_SURFACEMEMORYPTR The surface locked returns a memory pointer to the surface memory in lpsurface. This default action takes place if you don t send any flags. DDLOCK_WAIT If the surface can t be locked, wait until it can be. DDLOCK_WRITEONLY The surface being locked is written to only. 25
픽셀그리기 Unlock 다그린이후에는반드시 Unlock 을해야한다. HRESULT Unlock( ); LPRECT lprect // pointer to rectangle to unlock 26
픽셀그리기 픽셀을화면에찍는법 1. 표면을잠근다. 2. 디스플레이표면의포인터를얻는다. 3. 픽셀주소를계산한다. 4. 주소에쓴다. 5. 표면잠금을푼다. 27
픽셀그리기 픽셀을화면에찍는법 memset(&ddsd,0,sizeof(ddsd)); ddsd.dwsize = sizeof(ddsd); // lock the primary surface lpddsprimary->lock(null,&ddsd, DDLOCK_SURFACEMEMORYPTR DDLOCK_WAIT,NULL); // get video pointer video_buffer = (UCHAR *)ddsd.lpsurface; //.. use video pointer to write to memory // notice the use of lpitch (linear pitch) video_buffer[x + y*ddsd.lpitch] = col; // unlock the surface lpddsprimary->unlock(null); 28
실습 프로그램 Prog9_2.cpp 29
Color 16Bit 1.5.5.5 형식 alpha 1 bit red, green, blue 각각 5 bits 5.6.5 형식 red 5 bits green 6 bits ( 사람의눈은 green 에가장민감 ) blue 5 bits 일반적으로 99% 이상의 video card 들은 5.6.5 형식을사용한다. 30
Color 16 비트 Color 매크로 // 5.5.5 format #define _RGB16BIT(r,g,b) ((b%32)+((g%64)<<5)+((r%32)<<11)) example) UCHAR red = 0x08 // 0000 1000 UCHAR green = 0x05 // 0000 0101 UCHAR blue = 0x1b // 0001 1011 Result = _RGB16BIT565(red,green,blue) (blue % 32) = 0001 1011 (green % 64) << 5 = (0000 0101) << 5 = 1010 0000 (red % 32) << 11 = (0000 1000) << 11 = 0100 0000 0000 0000 Result = 0100 0000 1011 1011 = (01000) (000101) (11011) 31
Color // this builds a 16 bit color value in 1.5.5.5 format (1-bit alpha mode) #define _RGB16BIT555(r,g,b) ((b & 31) + ((g & 31) << 5) + ((r & 31) << 10)) example) UCHAR red = 0x08 // 0000 1000 UCHAR green = 0x05 // 0000 0101 UCHAR blue = 0x1b // 0001 1011 Result = _RGB16BIT555(red,green,blue) (blue & 31) = (0001 1011) & (0001 1111) = 0001 1011 (green & 31) << 5 = (0000 0101) << 5 = 1010 0000 (red & 31) << 10 = (0001 1000) << 10 = 0110 0000 0000 0000 Result = 0110 0000 1011 1011 = 0 (11000) (00101) (11011) 32
16bit Mode 로그리기 640x480x16 모드에서 (x, y) 위치에 (r, g, b) 색상으로그리는예제 memset(&ddsd,0,sizeof(ddsd)); ddsd.dwsize = sizeof(ddsd); // lock the primary surface lpddsprimary->lock(null,&ddsd, DDLOCK_SURFACEMEMORYPTR DDLOCK_WAIT,NULL); // get video pointer video_buffer = (USHORT *)ddsd.lpsurface; // use video pointer to write to memory.. // notice the use of lpitch (linear pitch) and the division by 2 (>>1); // this is needed to keep the addressing correct because you re using USHORT // pointers and lpitch is always in bytes video_buffer[x + (y*ddsd.lpitch >> 1)]= (USHORT)_RGB16BIT565(r,g,b); // unlock the surface lpddsprimary->unlock(null); 33
Why lpitch >> 1 // pointers and lpitch is always in bytes video_buffer[x + (y*ddsd.lpitch >> 1)]= (USHORT)_RGB16BIT565(r,g,b); USHORT 를사용할때모든포인터연산은 16 비트로이루어지지만, lpitch 는항상바이트로표현되기때문이다. 34
DEMO: PROG9_3.CPP PROG9_2_16.cpp 와다른점은무엇인가? 35
24 비트나 32 비트표면 #define _RGB24BIT(a,r,g,b) ((b) + ((g) << 8) + ((r) << 16) ) 24bit 는 32bit 중최상위 8bit 가사용되지않을뿐이다. 따라서 bpp 를 32 로하는것이좋다. 36
24, 32bit 이용 (1) In Game_Init() if (lpdd->setdisplaymode(screen_width, SCREEN_HEIGHT,32,0,0)!=DD_OK) return(0); 37
24, 32bit 이용 (2) Game_Main() UINT *video_buffer = NULL; video_buffer = (UINT *)ddsd.lpsurface; int words_per_line = (ddsd.lpitch >> 2); UCHAR red = rand()%256; UCHAR green = rand()%256; UCHAR blue = rand()%256; video_buffer[x + (y*words_per_line)] = _RGB24BIT(red,green,blue); 38
보조표면 (Secondary Surface) 보조표면 ( 또는후면버퍼 ) 의사용 부드러운애니메이션 1. 주표면을생성하고, 주표면으로부터하나의보조표면을생성한다. 2. 주표면이보여지는동안보조표면에그린다. 3. 순간적으로표면을바꾸어서 (switch or flip), 보조표면이주표면이되게하여, ( 그반대로도마찬가지 ) 부드러운애니메이션을만든다. 39
Page Flipping 40
보조표면의생성 (1) // DirectDraw surface description DDSURFACEDESC2 ddsd; // device capabilities structure, used to query for // secondary backbuffer, among other things DDSCAPS2 ddscaps; LPDIRECTDRAWSURFACE7 lpddsprimary, // primary surface lpddssecondary; // secondary backbuffer surface // prepare to create primary surface with one backbuffer memset((void *)&ddsd,0,sizeof(ddsd)); ddsd.dwsize = sizeof(ddsd); // DDSURFACEDESC would work, too // set the flags to validate both the capabilities // field and the backbuffer count field ddsd.dwflags = DDSD_CAPS DDSD_BACKBUFFERCOUNT; // you need to let dd know that you want a complex flippable surface structure; 41
보조표면의생성 (2) // set flags for that ddsd.ddscaps.dwcaps = DDSCAPS_PRIMARYSURFACE DDSCAPS_FLIP DDSCAPS_COMPLEX; // set the backbuffer count to 1 ddsd.dwbackbuffercount = 1; // create the primary surface lpdd->createsurface(&ddsd,&lpddsprimary,null); // query for the backbuffer or secondary surface // notice the use of ddscaps to indicate what you re requesting ddscaps.dwcaps = DDSCAPS_BACKBUFFER; // get the surface lpddsprimary->getattachedsurface(&ddscaps,&lpddsback); 42
주표면과부표면 43
Rendering Backbuffer // used to access secondary video buffer UCHAR *video_buffer; // lock the secondary surface lpddsback->lock(null,&ddsd, DDLOCK_SURFACEMEMORYPTR DDLOCK_WAIT,NULL); // draw on the surface: ddsd.lpsurface and // ddsd.lpitch are valid as before video_buffer = (UCHAR *)ddsd.lpsurface; // unlock the surface lpddsback->unlock(null); 44
Flipping HRESULT Flip( LPDIRECTDRAWSURFACE7 lpddsurfaceoverride, // always NULL DWORD dwflags); // always DDFLIP_WAIT // flip the primary and secondary surfaces while(lpddsprimary->flip(null, DDFLIP_WAIT)!=DD_OK); 45
Flipping 전후 46
오프스크린표면 (off-screen surface) 시스템메모리나 VRAM 에모두존재할수있음 주표면과같은색상깊이와속성을가지는비트맵 하드웨어가속 Blt() 를위해사용 스프라이트 간단히말해서비디오게임화면에서움직이는작은물체를뜻한다. 대부분의경우, 스프라이트는비트맵일뿐 8 비트 PC 인 MSX 에서 HW 에서사용된용어 47
VRAM 에오프스크린표면생성 //.. assume DirectDraw has been set up and so on DDSURFACEDESC2 ddsd; // a DirectDraw surface descriptor LPDIRECTDRAWSURFACE7 lpwork; // the working surface // set the size parameter as always memset(&ddsd,0,sizeof(ddsd)); ddsd.dwsize = sizeof(ddsd); // set the flags; very important // remember that you must set the flags of the fields that will be valid ddsd.dwflags = DDSD_CAPS DDSD_WIDTH DDSD_HEIGHT; // set dimensions of the new surface ddsd.dwwidth = width; ddsd.dwheight = height; // what kind of offscreen surface, system memory, or VRAM // default is VRAM ddsd.ddscaps.dwcaps = DDSCAPS_OFFSCREENPLAIN; // now create the surface and check for error if (lpdd->createsurface(&ddsd,&lpwork,null)!=dd_ok) { /* error */ } 48
시스템메모리에 // set flags for an offscreen plain system memory surface ddsd.ddscaps.dwcaps = DDSCAPS_OFFSCREENPLAIN DDSCAPS_SYSTEMMEMORY; // now create the surface and check for error if (lpdd->createsurface(&ddsd,&lpwork,null)!=dd_ok) { /* error */ } 49
팔레트 생략. 50
질문 51