630 likes | 884 Views
Chapter 11 DirectDraw 의 깊은 곳 : 보다 발전된 기능들. 이 장에서는. - 비트맵 그래픽으로 고급 작업 수행하기 - 오프스크린 표면 사용하기 - 색상 애니메이션 기법 알아보기 - DirectDraw 와 GDI 결합하기 - 윈도우 모드에서 DirectDraw 사용하기 - DirectDraw 에서 정보 얻어오기. 투명도. Black-transparent 8-bit version. // 32 x 32 bitmap 을 화면 (x,y) 에 뿌리는 예제
E N D
이 장에서는 • - 비트맵 그래픽으로 고급 작업 수행하기 • - 오프스크린 표면 사용하기 • - 색상 애니메이션 기법 알아보기 • - DirectDraw와 GDI 결합하기 • - 윈도우 모드에서 DirectDraw 사용하기 • - DirectDraw에서 정보 얻어오기
Black-transparent 8-bit version // 32 x 32 bitmap을 화면 (x,y) 에 뿌리는 예제 // (x, y) 는 bitmap의 왼쪽 위 꼭지점이 된다. // (index_x, index_y) 는 32x32 bitmap을 따라간다. for (int index_y=0; index_y<32; index_y++) { for (int index_x=0; index_x<32; index_x++) { UCHAR pixel = image[index_x+index_y*32]; if (pixel!=0) primary_buffer[x+index_x+(y+index_y)*ddsd.lPitch] = pixel; } // end for index_x } // end for index_y
Black-transparent 16-bit version // these two loops blit a 32 x 32 bitmap for (int index_y=0; index_y<32; index_y++) { // copy next row of pixels for (int index_x=0; index_x<32; index_x++) { // get the next pixel USHORT pixel = image[index_x+index_y*32]; // test if pixel is transparent if (pixel!=_RGB16BIT565(0,0,0)) primary_buffer[x+index_x+(y+index_y)*(ddsd.lPitch/2)] = pixel; // else do nothing } // end for index_x } // end for index_y
Black-transparent 8-bit (without multiply) UCHAR *src_ptr = image; UCHAR *dest_ptr = primary_buffer + x + y * ddsd.lPitch; for (int index_y=0; index_y<32; index_y++) { for (int index_x=0; index_x<32; index_x++) { UCHAR pixel = *src_ptr; if (pixel!=0) *dest_ptr = pixel; src_ptr++; dest_ptr++; } // end for index_x // move destination pointer to start of next line dest_ptr+=(ddsd.lPitch - 32); } // end for index_y
Black-transparent 16-bit (without multiply) • Try it !
Multiplication/Division by shift • player_x*64 = player_x <<6 • player_x*96 = player_x*64 + player_x*32 = (player_x << 6) + (player_x << 5)
색상키(color keying) • 원본키(source key) • 투명한 색상의 범위를 설정하고, 블리터에게 이 범위를 복사하지 말도록 지시 HRESULT SetColorKey(DWORD dwFlags, // what kind of key LPDDCOLORKEY lpDDColorKey); // range of key typedef struct _DDCOLORKEY { DWORD dwColorSpaceLowValue; // starting color (inclusive) DWORD dwColorSpaceHighValue;// ending color (inclusive) } DDCOLORKEY,FAR* LPDDCOLORKEY;
Source Key Example DDCOLORKEY key; // color key // set transparent color range to 0 key.dwColorSpaceLowValue = 0; key.dwColorSpaceHighValue = 0; // set color key now on the source surface, which is usually the backbuffer lpddsback->SetColorKey(DDCKEY_SRCBLT, &key); // perform the blit from backbuffer to primary buffer lppddsprimary->Blt(...);
Source Key의 예 Y Source Key = R R + G B Blitting Source Destination Y G B Result
목적키(destination key) • 그려질 목적 표면에 색상키 범위를 설정 • Dest key 와 같은 색이 먼저 깔려 있으면 그 곳에는 blitting 되지 않는다. DDCOLORKEY key; // color key // set writable values from 0 to 249 key.dwColorSpaceLowValue = 0; key.dwColorSpaceHighValue = 249; // set color key now on the destination surface lpddsprimary->SetColorKey(DDCKEY_DESTCBLT, &key); // perform the blit from backbuffer to primary buffer // or whatever is the source surface... lppddsprimary->Blt(...);
B Destination Key의 예 Y Dest Key = B R + G B Blitting Source Destination Y R G Result
BOB(Bitmapped Object Bitmap) 사용 1. DirectDraw를 설정한다. 2. 10장에서처럼 8비트 또는 16비트 색상의 비트맵 이미지에 객체들을 불러온다. 3. Create_BOB() 또는 Create_BOB16()으로 BOB를 생성한다. 4. Load_BOB() 또는 Load_BOB16()으로 이미지를 BOB에 불러온다. 5. Draw_BOB() 또는 Draw_BOB16()으로 BOB를 그린다. 6. 완료하면, Destroy_BOB() 또는 Destroy_BOB16()으로 BOB을 제거한다.
BOB Structure typedef struct BITMAP_OBJ_TYP { int state; // the state of the object (general) int attr; // attributes pertaining to the object (general) int x,y; // position bitmap will be displayed at int xv,yv; // velocity of object int width, height; // the width and height of the bitmap LPDIRECTDRAWSURFACE7 image; // the bitmap surface itself } BITMAP_OBJ, *BITMAP_OBJ_PTR; #define BOB_STATE_DEAD 0 // this is a dead BOB #define BOB_STATE_ALIVE 1 // this is a live BOB #define BOB_STATE_LOADED 2 // the BOB has been loaded
죽은(dead) BOB와 살아있는(live) BOB • 살아있다는 것은 BOB가 게임 로직에 의해 처리되고 있음을 뜻한다. • BOB가 죽었다는 것은 게임 로직에 의해 처리되지 않는다는 것을 의미한다.
BOB 생성 (1) int Create_BOB(BITMAP_OBJ_PTR bob, // the BOB to create int width, int height, // size of BOB int attr, // attrs int flags = 0) // memory flag { // create the BOB object; note that all BOBs are created as off-screen // surfaces in VRAM as the default; if you want to use system memory, // set flags equal to DDSCAPS_SYSTEMMEMORY DDSURFACEDESC2 ddsd; // used to create surface // set state and attributes of BOB bob->state = BOB_STATE_ALIVE; bob->attr = attr; bob->image = NULL; // set position and velocity to 0 bob->x = bob->y = bob->xv = bob->yv = 0; // set to access caps, width, and height memset(&ddsd,0,sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
BOB 생성 (2) // set dimensions of the new bitmap surface ddsd.dwWidth = bob->width = width; ddsd.dwHeight = bob->height = height; // set surface to off-screen plain ddsd.ddsCaps.dwCaps = DDSCAPS_OFF_SCREENPLAIN | flags; // create the surface if (lpdd->CreateSurface(&ddsd,&(bob->image),NULL)!=DD_OK) return(0); // set color key to color 0 DDCOLORKEY color_key; // used to set color key color_key.dwColorSpaceLowValue = 0; color_key.dwColorSpaceHighValue = 0; // now set the color key for source blitting (bob->image)->SetColorKey(DDCKEY_SRCBLT, &color_key); // return success return(1); } // end Create_BOB
비트맵 데이터로 BOB 로딩하기 (1) • 목적 BOB와 읽어올 원본 비트맵 파일을 인수로 받는다. • (cx, cy)와 mode에 의해서 읽어올 위치가 결정된다. • Cell Mode (0): (cx, cy)는 셀 좌표로 해석된다. • Absolute Mode (1): (cx, cy)는 원본 비트맵의 절대 좌표로 해석된다. • 읽을 비트맵의 크기는 BOB와 정확하게 맞으며, 이것은 bob->width와 bob->height에 정의된다.
비트맵 데이터로 BOB 로딩하기 (3) int Load_BOB(BITMAP_OBJ_PTR bob, // BOB to load with data BITMAP_FILE_PTR bitmap, // bitmap to scan image data from int cx,int cy, // cell or absolute pos to scan image from int mode) // if 0, then cx,cy is cell position; else // cx,cy are absolute coordinates { // this function extracts a bitmap out of a bitmap file UCHAR *source_ptr, // working pointers *dest_ptr; DDSURFACEDESC2 ddsd; // DirectDraw surface description // test the mode of extraction, cell-based or absolute if (mode==0) { // re-compute x,y cx = cx*(bob->width+1) + 1; cy = cy*(bob->height+1) + 1; } // end if
비트맵 데이터로 BOB 로딩하기 (4) // extract bitmap data source_ptr = bitmap->buffer + cy*bitmap->bitmapinfoheader.biWidth+cx; // get the addr to destination surface memory // set size of the structure ddsd.dwSize = sizeof(ddsd); // lock the display surface (bob->image)->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR, NULL); // assign a pointer to the memory surface for manipulation dest_ptr = (UCHAR *)ddsd.lpSurface;
비트맵 데이터로 BOB 로딩하기 (5) // iterate through each scanline and copy bitmap for (int index_y=0; index_y<bob->height; index_y++) { // copy next line of data to destination memcpy(dest_ptr, source_ptr,bob->width); // advance pointers dest_ptr += (ddsd.lPitch); source_ptr += bitmap->bitmapinfoheader.biWidth; } // end for index_y // unlock the surface (bob->image)->Unlock(NULL); // set state to loaded bob->state |= BOB_STATE_LOADED; // return success return(1); } // end Load_BOB
BOB 그리기 int Draw_BOB(BITMAP_OBJ_PTR bob, // BOB to draw LPDIRECTDRAWSURFACE7 dest) // surface to draw the BOB on { // draw a BOB at the x,y defined in the BOB // on the destination surface defined in dest RECT dest_rect, // the destination rectangle source_rect; // the source rectangle // fill in the destination rect dest_rect.left = bob->x; dest_rect.top = bob->y; dest_rect.right = bob->x+bob->width; dest_rect.bottom = bob->y+bob->height; // fill in the source rect source_rect.left = 0; source_rect.top = 0; source_rect.right = bob->width; source_rect.bottom = bob->height; // blt to destination surface dest->Blt(&dest_rect, bob->image, &source_rect,(DDBLT_WAIT | DDBLT_KEYSRC), NULL); // return success return(1); } // end Draw_BOB
BOB 소멸하기 int Destroy_BOB(BITMAP_OBJ_PTR bob) { // destroy the BOB; simply release the surface if (bob->image) (bob->image)->Release(); else return(0); // return success return(1); } // end Destroy_BOB
BOB를 움직이게 하는 함수들 (16bit) BITMAP_FILE bitmap; // working bitmap file BITMAP_OBJ car; // the bitmapped object // load the bitmap Load_Bitmap_File(&bitmap,”cars16.bmp”); // create a 32 x 32 VRAM BOB Create_BOB16(&car,32,32,0,0,0); // load the BOB with first cell of bitmap template image Load_BOB16(&car,0,0,0); // unload the bitmap; you’re done with it Unload_Bitmap_File(&bitmap); // set position of BOB car.x = 100; car.y = 200; // draw BOB on primary surface Draw_BOB16(&car, lppdsprimary); // your game code goes here... // delete the BOB Destroy_BOB16(&car);
색상을 이용한 기술들 • 게임 프로그래머는 기교(trick)를 써서 • 그래픽 시스템이 실제보다 더 많은 색상이 있는 것처럼 보이게 하고, • 또한 기존 하드웨어로는 불가능한 것처럼 보이는 광원(lighting) 효과를 적용하기도 했다. • 요즘에는 이런 trick을 쓰지 않아도 된다. • 휴대폰이나 PDA 게임 프로그래밍에 여전히 적용 가능하다.
팔레트 애니메이션 • 8비트 색상 모드에서만 적용 • 하나 혹은 이상의 팔레트 항목을 실시간에 새로운 데이터로 갱신하는 것이다. • 이 방법으로 이들 색상으로 그려진 픽셀은 시간에 따라 변한다. • 깜빡이는 빛이나, 에너지 펄스, 폭발 등에 이 기법을 사용한다.
색상 회전(color rotation) • 움직임을 잘 시뮬레이션 한다 • 폭포수나 흐르는 샘물, 또는 컨베이어 벨트
클리핑(clipping) • 이미지에서 보이지 않는 부분을 그리지 않는 것을 의미
두가지 클리핑 방법 • 이미지-공간(image-space) 클리핑 • 픽셀 단위로 그리는 것처럼 클리핑하는 방법이다. • 시간이 많이 걸리고, 때로는 하드웨어 가속기능이 있을 때에만 잘 동작한다. • 물체의 각 픽셀을 그릴 때에 그것이 클리핑 영역에 그려지지 않는 것을 확인해야 한다는 것이다. • 오브젝트-공간(object-space) 클리핑 • 매 픽셀마다 클리핑 영역과 비교하는 것이 아니라, 그려질 물체의 형태를 분석하여, 어느 정도의 부분을 그려야 하는지 계산 • 이미지-공간 클리핑보다 조금 더 수학적이다.
오브젝트-공간 클리핑의 간단한 예 // trivial rejections test first // is the rectangle totally off the screen? if (x1 > MAX_X || x2 < MIN_X || y1 > MAX_Y || y2 < MIN_Y) { /* totally clipped, do nothing */ } // check x coords if (x1 < MIN_X) x1 = MIN_X; else if (x2 > MAX_X) x2 = MAX_X; // now y coords if (y1 < MIN_Y) y1 = MIN_Y; else if (y2 > MAX_Y) y2 = MAX_Y; // at this point, (x1,y1) through (x2,y2) contain the clipped rectangle...
DirectDrawClipper의 소개 • DirectDraw는 블리트하고자 하는 어떤 표면에도 추가할 수 있는 DirectDrawClipper를 지원한다. • 클리퍼는 여러 개의 클리핑 영역을 가질 수 있다.
DirectDrawClipper를 생성하는 단계 1. DirectDrawClipper 객체를 생성한다. 2. 클리핑 사각형의 목록을 생성하고 그것을 클리퍼(clipper)에 할당한다. 3. 클리퍼를 표면(surface)에 추가한다.
DirectDrawClipper 생성 // Prototype HRESULT CreateClipper(DWORD dwFlags, // unused; make 0 LPDIRECTDRAWCLIPPER FAR *lplpDDClipper, // ptr to result IUnknown FAR *pUnkOuter); // always NULL // Example LPDIRECTDRAWCLIPPER lpddclipper; // the clipper if ((lpdd->CreateClipper(0,&lpddclipper,NULL))!=DD_OK) return(NULL);
클리핑 목록 채우기 (1) • RGNDATA typedef struct _RGNDATA { // rgnd RGNDATAHEADER rdh; // the header char Buffer[1]; // a list of RECTs defining clipping } RGNDATA; typedef struct _RGNDATAHEADER { // rgndh DWORD dwSize; // size of this header DWORD iType; // must be RDH_RECTANGLES DWORD nCount; // number of rectangles in buffer DWORD nRgnSize; // size of the buffer RECT rcBound; // a bounding box around all the rects } RGNDATAHEADER;
클리핑 목록 채우기 (2) HRESULT SetClipList( LPRGNDATA lpClipList, // ptr to RGNDATA DWORD dwFlags); // unused; must be 0 RGNDATA region_data; // holds the RECTs and data header // fill in region_data... // set clipping list if ((lpddclipper->SetClipList(®ion_data, 0))!=DD_OK) { /* error */ }
클리퍼를 표면에 추가하기 if ((lpdds->SetClipper(lpddclipper))!=DD_OK) { /* error */ }
Wrapper Fn: DD_Attach_Clipper (1) LPDIRECTDRAWCLIPPER DD_Attach_Clipper(LPDIRECTDRAWSURFACE7 lpdds, int num_rects, LPRECT clip_list) { // this function creates a clipper from the sent clip list // and attaches it to the sent surface int index; // looping var LPDIRECTDRAWCLIPPER lpddclipper; // pointer to the newly created dd clipper LPRGNDATA region_data; // pointer to the region data that contains // the header and clip list // first create the DirectDraw clipper if ((lpdd->CreateClipper(0,&lpddclipper,NULL))!=DD_OK) return(NULL); // now create the clip list from the sent data // first allocate memory for region data region_data = (LPRGNDATA)malloc(sizeof(RGNDATAHEADER) + num_rects*sizeof(RECT)); // now copy the rects into region data memcpy(region_data->Buffer, clip_list, sizeof(RECT)*num_rects);
Wrapper Fn: DD_Attach_Clipper (2) // set up fields of header region_data->rdh.dwSize = sizeof(RGNDATAHEADER); region_data->rdh.iType = RDH_RECTANGLES; region_data->rdh.nCount = num_rects; region_data->rdh.nRgnSize = num_rects*sizeof(RECT); region_data->rdh.rcBound.left = 64000; region_data->rdh.rcBound.top = 64000; region_data->rdh.rcBound.right = -64000; region_data->rdh.rcBound.bottom = -64000; // find bounds of all clipping regions for (index=0; index<num_rects; index++) { // test whether the next rectangle unioned with the current bound is larger if (clip_list[index].left < region_data->rdh.rcBound.left) region_data->rdh.rcBound.left = clip_list[index].left; if (clip_list[index].right > region_data->rdh.rcBound.right) region_data->rdh.rcBound.right = clip_list[index].right; ….
Wrapper Fn: DD_Attach_Clipper (3) // set up fields of header // you’ve computed the bounding rectangle region and // set up the data; now set the clipping list if ((lpddclipper->SetClipList(region_data, 0))!=DD_OK) { // release memory and return error free(region_data); return(NULL); } // end if // now attach the clipper to the surface if ((lpdds->SetClipper(lpddclipper))!=DD_OK) { // release memory and return error free(region_data); return(NULL); } // end if // all is well, so release memory and send back the pointer // to the new clipper free(region_data); return(lpddclipper); } // end DD_Attach_Clipper
클리핑 목록을 설정하는 방법 // create clip list (4) rectangles RECT cliplist[4] = { {0,0,100,100}, {200,200,300,300}, {400,100,500,200}, {500,400,550,450}}; // attach clipper and save in lpddclipper lpddclipper = DD_Attach_Clipper(lpddsprimary,4,cliplist);
GDI와 DirectDraw의 사용 • 윈도우즈와 DirectDraw의 고수준(메시지 상자, 메뉴 등)과 저수준(물체 그리기, 텍스트 그리기) 혼합
DirectDraw에서 GDI를 사용하기 위해 세 가지 선택 • 고수준 컨트롤의 경우 • GDC(Graphics Device Context; 그래픽 장치 컨텍스트)를 이용하지 않고, GDI를 이용하는 가장 간단한 방법이다. 대신, 메시지 상자, 대화상자, 메뉴와 같은 고수준 컨트롤을 사용한다. GDC는 HDC(Handle to Device Context)의 특수한 경우이다. • GDC를 이용하는 경우 • 임의의 표면에 그리기 위해 GDC(비디오 모드, 메모리, 해상도, 색상 공간 등의 기술)를 사용할 수 있다. IDIRECTDRAWSURFACE7의 함수를 이용하여 DirectDraw로부터 GDC를 요청한다. • 윈도우 모드의 경우 • 표준 윈도우 모드를 이용하여 GDI와 함께 DirectDraw를 사용할 수 있다. 이 책에 나오는 대부분의 그래픽을 이용하는 코드는 전체 비디오 표면을 위임받고 그래픽 모드를 변경한다. 그러나 때때로 그렇게 하고 싶지 않을 수도 있고, 표준 윈도우 모드에서 DirectDraw를 이용하여 그릴 수도 있다.