540 likes | 784 Vues
Chapter 9 DirectDraw 크래용 사용하기 . 이 장에서는 ... . 표면 생성하기 색상 팔레트 다루기 DirectDraw 로 그리기. DirectDraw 표면 (Surface). 실제로 비디오 카드의 비디오 메모리와 직접 대응 표면은 어떤 크기도 될 수 있다 . 주표면은 현재 화면 해상도와 같은 크기여야 한다 . 그러나 , 다른 표면은 어떤 크기라도 될 수 있다 . 표면은 비디오 메모리에 (VRAM) 또는 시스템 메모리에 생성할 수 있다 .
E N D
이 장에서는... • 표면 생성하기 • 색상 팔레트 다루기 • DirectDraw로 그리기
DirectDraw 표면 (Surface) • 실제로 비디오 카드의 비디오 메모리와 직접 대응 • 표면은 어떤 크기도 될 수 있다. 주표면은 현재 화면 해상도와 같은 크기여야 한다. 그러나, 다른 표면은 어떤 크기라도 될 수 있다. • 표면은 비디오 메모리에 (VRAM) 또는 시스템 메모리에 생성할 수 있다. • 비트깊이와 색상공간이 같은 한, 모든 표면은 같은 속성을 갖는다.
Surfaces • 주 디스플레이 표면(primary display surface) • 보여지는 비디오 화면 자체, 비디오 카드의 비디오 메모리와 직접적으로 대응 • 보조 디스플레이 표면(secondary display surface) • 후면버퍼(back buffer) • 주표면과 같은 구조 • 내용은 화면에 보여지지 않는다. • 애니메이션을 위해 다음 프레임을 렌더링 • 오프스크린 표면(offscreen surface) • 이것은 하드웨어 가속을 이용해서 빠르게 그리고 싶은 비트맵, 스프라이트, 또는 스크린 아이템(예들 들어 캐릭터나 우주선 등)을 저장하는 표면이다.
주표면(Primary Surface) 사용하기 • CreateSurface()를 호출 HRESULT CreateSurface( LPDDSURFACEDESC2 lpDDSurfaceDesc, LPDIRECTDRAWSURFACE7 FAR *lplpDDSurface, IUnknown FAR *pUnkOuter); • lpDDSurfaceDesc: DirecDraw 표면을 기술하는 구조체의 포인터. 원하는 표면을 기술하는 여러 속성들을 이 포인터에 설정한다. • lplpDDSurface: 성공하면 함수로부터 반환될 DirectDraw 표면의 인터페이스의 포인터이다. • pUnkOuter: 심화된 기능으로, COM aggregation에 사용된다. 항상 NULL로 놓도록 한다.
DDSURFACEDESC structure (1) typedef struct _DDSURFACEDESC2 { DWORD dwSize; // the size of this structure in bytes // this field must be set by you DWORD dwFlags; // flags field indicating which fields // of this structure are valid with data DWORD dwHeight; // width of surface DWORD dwWidth; // height of surface union { LONG lPitch; // number of bytes per line DWORD dwLinearSize; // used for compression } DUMMYUNIONNAMEN(1); DWORD dwBackBufferCount; // number of back buffers
DDSURFACEDESC structure (2) union { DWORD dwMipMapCount; // number of mip levels DWORD dwRefreshRate; // refresh rate in hz } DUMMYUNIONNAMEN(2); DWORD dwAlphaBitDepth; // number of bits for alpha DWORD dwReserved; LPVOID lpSurface; // pointer to surface memory
DDSURFACEDESC structure (3) union { DDCOLORKEY ddckCKDestOverlay; // color key for // destination overlay DWORD dwEmptyFaceColor; // 3d stuff } DUMMYUNIONNAMEN(3); DDCOLORKEY ddckCKDestBlt; // color key for // destination blit DDCOLORKEY ddckCKSrcOverlay; // color key for overlay DDCOLORKEY ddckCKSrcBlt; // color key for source blit DDPIXELFORMAT ddpfPixelFormat; // general pixel format DDSCAPS2 ddsCaps; // capabilities bits DWORD dwTextureStage; // 3d stuff } DDSURFACEDESC2, FAR* LPDDSURFACEDESC2;
dwFlags • DDSD_ALL All input members are valid. • DDSD_ALPHABITDEPTH dwAlphaBitDepth member is valid. • DDSD_BACKBUFFERCOUNT dwBackBufferCount member is valid. • DDSD_CAPS ddsCaps member is valid. • DDSD_CKDESTBLT ddckCKDestBlt member is valid. • DDSD_CKDESTOVERLAY ddckCKDestOverlay member is valid. • DDSD_CKSRCBLT ddckCKSrcBlt member is valid. • DDSD_CKSRCOVERLAY ddckCKSrcOverlay member is valid. • DDSD_HEIGHT dwHeight member is valid. • DDSD_LINEARSIZE Unused. • DDSD_LPSURFACE lpSurface member is valid. • DDSD_MIPMAPCOUNT dwMipMapCount member is valid. • DDSD_PITCH lPitch member is valid. • DDSD_PIXELFORMAT ddpfPixelFormat member is valid. • DDSD_REFRESHRATE dwRefreshRate member is valid. • DDSD_WIDTH dwWidth member is valid. • DDSD_ZBUFFERBITDEPTH dwZBufferBitDepth member is valid.
Fields (1) • dwSize: DDSURFACEDESC 구조체의 크기로 개발자가 꼭 설정해야 한다. • dwFlags: 유효한 속성 정보 플래그를 포함한다. 다른 말로 하면, 수정하거나 얻어오고 싶은 모든 속성을 논리적인 OR로 플래그 속성을 설정해야 한다. • dwHeight: 픽셀단위의 표면의 높이이다. • dwWidth: 픽셀단위의 표면의 폭이다. • lpSurface: 표면이 잠그어질 때, 표면 VRAM을 가리키는 포인터이다. 잠그기(locking)는 표면 메모리의 일부분을 수정할 것이라고 DirectDraw에게 알리는 과정이다.
Fields (2) • dwBackBufferCount: 오프스크린 버퍼나 페이지의 숫자를 지정한다. DirectDraw에서 여러 개의 표면을 생성할 수 있다. 따라서 한 표면을 표시하는 동안 다른 표면에 그림으로써 부드러운 애니메이션을 만들 수 있다. • lPitch: 표면의 메모리 피치(memory pitch)로, 한 줄당 바이트 수를 말한다. • ddCaps: 표면의 부가적인 기능(capability)들이다. typedef struct _DDSCAPS2 { DWORD dwCaps; DWORD dwCaps2; DWORD dwCaps3; DWORD dwCaps4; } DDSCAPS2, FAR* LPDDSCAPS2;
Fields (3) • dwCaps: 표면에 부여되길 원하는 기능들을 저장한다. • DDSCAPS_BACKBUFFER • 표면이 전환 사슬(flipping chain)의 후면버퍼가 된다. • DDSCAPS_COMPLEX • 표면이 하나의 주표면 이상을 갖는 복잡한 표면의 일부가 된다. • DDSCAPS_FLIP • 표면이 플립 가능하다. • DDSCAPS_FRONTBUFFER • 표면이 전환 구조에서 첫번째, 혹은 전면버퍼(frontbuffer)가 된다. • DDSCAPS_MODEX • 표면이 320x200이나 320x240 모드 X 표면이다. • DDSCAPS_OFFSCREENPLAIN • 표면이 오버레이, 텍스쳐, z-버퍼, 전면버퍼, 후면버퍼, 알파 표면이 아닌 오프스크린 표면이 된다. 보통 스프라이트나 비트맵에 사용한다.
Fields (4) • DDSCAPS_OWNDC • 표면이 장시간 윈도우즈 장치 컨텍스트와의 연관을 갖는다. • DDSCAPS_PRIMARYSURFACE • 표면이 주표면이다. 즉, 보여지고 렌더링된다. • DDSCAPS_STANDARDVGAMNODE • 표면이 표준 VGA 모드 표면이며, 모드X 표면이 아니다. • DDSCAPS_SYSTEMMEMORY • 표면이 시스템 메모리에 할당된다.
주표면 생성하기 - 1 // pointer to DirectDraw object LPDIRECTDRAW7 lpdd; // used to hold the DirectDraw surface description DDSURFACEDESC2 ddsd; // where the interface pointer will be placed // when the surface is created LPDIRECTDRAWSURFACE7 lpddsprimary; // create DirectDraw object DirectDrawCreateEx(NULL, (void **)&lpdd, IID_IDirectDraw7, NULL); // set cooperation level lpdd->SetCooperativeLevel(hwnd, DDSCL_ALLOWREBOOT | DDSCL_ALLOWMODEX | DDSCL_FULLSCREEN | DDSCL_EXCLUSIVE ); // now set the display mode; assume constants defined lpdd->SetDisplayMode(SCREEN_WIDTH,SCREEN_HEIGHT, SCREEN_BPP,0,0);
주표면 생성하기 - 2 // create the primary surface // manually set the size; very important ddsd.dwSize = sizeof(ddsd); // the only field you modify, so you must indicate in the flags // that you want a primary drawing surface; in this case, you don’t have // to define anything else, because DirectDraw already knows the // resolution and color of the video mode ddsd.dwFlags = DDSD_CAPS; // set the capabilities to what you want, a primary surf ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; // create the surface and check for an error if (lpdd->CreateSurface(&ddsd,&lpddsprimary,NULL)!=DD_OK) { /* error */ }
COM Interface Release // first release the surface, but check for NULL if (lpddsprimary) lpddsprimary->Release(); // now release the DirectDraw object itself if (lpdd) lpdd->Release();
PROG9_1.CPP • 800x600x16 모드
비디오의 다음 줄을 얻기 위해서 • 640을 더하는 대신에, 1,024같은 숫자를 더해야 할 수도 있다. (이 수를 lpitch 라고 하며 video card 마다 다르다.) UCHAR *video_buffer; // point video buffer to primary surface... // (check out the section “The details of rendering to the primary surface” // for this code) // write a single pixel; color is a palette index video_buffer[x + 1024*y] = color;
640 x 480 x 8 mode // used to access data UCHAR *video_buffer; // point video_buffer to primary surface... // check out the section “The details of rendering to the primary surface” // for this code // write a single pixel; color is a palette index video_buffer[x + 640*y] = color;
640 x 480 x 16-bit color mode // this time, use a 16-bit short USHORT *video_buffer; // point video buffer to primary surface... // write a single pixel; color is a palette index video_buffer[x + 640*y] = color; • RGB 값(각각 5비트) • 픽셀당 2 바이트 • 각 줄은 1,280 바이트
주표면 렌더링 • DirectDraw는 디자인상 렌더링을 아주 조금 지원한다. • 대부분의 게임 프로그래머는 자신의 그래픽스 엔진과 라이브러리를 작성한다. • 따라서 DirectDraw는 게임 프로그래머에게 기본적인 • 색상 지원, • 비디오 버퍼 접근, • 하드웨어를 이용한 고속 채움(filling) 및 블리팅(blitting)을 제공한다.
팔레트 모드 • 각 픽셀은 한 바이트로 표현 • 각 바이트는 실제 RGB 값을 가지고 있는 색상조사표(CLUT)의 인덱스 • 순서 • Lock() • Write or Read • Unlock() • Lock HRESULT Lock( LPRECT lpDestRect, // rectangle to lock; use NULL to lock // the entire surface LPDDSURFACEDESC2 lpDDSD, // returns the properties of the surface being locked DWORD dwFlags, // control flags HANDLE hEvent); // unused; set to NULL
Flags for Lock() • Table 9-3 Flags for Lock() • Value Meaning • 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.
Unlock • HRESULT Unlock(LPRECT lpRect); // pointer to rectangle to unlock
8-bit Example (1) • 표준 DirectDraw 호출로 (x,y) 픽셀에 col 색상으로 점을 찍는 예제 // pointer to DirectDraw object LPDIRECTDRAW7 lpdd; // you use this function to access the surface memory UCHAR video_buffer = NULL; // used to hold the DirectDraw surface description DDSURFACEDESC2 ddsd; // primary surface LPDIRECTDRAWSURFACE7 lpddsprimary; // create DirectDraw object DirectDrawCreateEx(NULL, (void **)&lpdd, IID_IDirectDraw7, NULL); // set cooperation level lpdd->SetCooperativeLevel(hwnd, DDSCL_ALLOWREBOOT | DDSCL_ALLOWMODEX | DDSCL_FULLSCREEN | DDSCL_EXCLUSIVE );
8-bit Example (2) // now set the display mode to 800 x 600 x 256 lpdd->SetDisplayMode(800,600,8,0,0); // set up data structure to create the primary surface ddsd.dwSize = sizeof(ddsd); ddsd.dwFlags = DDSD_CAPS; ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; // create the primary surface lpdd->CreateSurface(&ddsd,&lpddsprimary,NULL); // set up the surface description to lock the surface // zero out the data structure and set its size memset(&ddsd,0,sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd);
8-bit Example (3) // 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);
16bit Mode • 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 • 일반적으로 90% 이상의 video card들은 5.6.5 형식을 사용한다.
RGB Mode Macro (1) // 5.5.5 format #define _RGB16BIT(r,g,b) ((b%32)+((g%32)<<5)+((r%32)<<10)) example) UCHAR red = 0x08 // 0000 1000 UCHAR green = 0x05 // 0000 0101 UCHAR blue = 0x1b // 0001 1011 Result = _RGB16BIT(red,green,blue) • (blue % 32) = 0001 1011 • (green % 32) << 5 = (0000 0101) << 5 = 1010 0000 • (red % 32) << 10 = (0001 1000) << 10 = 0110 0000 0000 0000 Result = 0110 0000 1011 1011 = 0 (11000) (00101) (11011)
RGB Mode Macro (2) // 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)
RGB Mode Macro (3) // this builds a 16 bit color value in 5.6.5 format (green dominate mode) #define _RGB16BIT565(r,g,b) ((b & 31) + ((g & 63) << 5) + ((r & 31) << 11))
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);
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는 항상 바이트로 표현되기 때문이다.
DEMO: PROG9_3.CPP • PROG9_2_16.cpp 와 다른 점은 무엇인가?
24비트나 32비트 표면 #define _RGB24BIT(a,r,g,b) ((b) + ((g) << 8) + ((r) << 16) ) 24bit 는 32bit 중 최상위 8bit가 사용되지 않을 뿐이다. 따라서 bpp를 32로 하는 것이 좋다.
24, 32bit 이용 (1) • In Game_Init() if (lpdd->SetDisplayMode(SCREEN_WIDTH, SCREEN_HEIGHT,32,0,0)!=DD_OK) return(0);
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);
보조표면(Secondary Surface) • 보조 표면(또는 후면버퍼) 의 사용 • 부드러운 애니메이션 • 1. 주표면을 생성하고, 주표면으로부터 하나의 보조표면을 생성한다. • 2. 주표면이 보여지는 동안 보조표면에 그린다. • 3. 순간적으로 표면을 바꾸어서(switch or flip), 보조표면이 주표면이 되게 하여, (그 반대로도 마찬가지) 부드러운 애니메이션을 만든다.
보조표면의 생성 (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;
보조표면의 생성 (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);
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);
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);