Upload
others
View
2
Download
0
Embed Size (px)
Citation preview
한국산업기술대학교
게임공학과
정 내 훈
2D Game Programming
개요
2
표면
– 표시 가능한 메모리
팔레트
DirectDraw로 그리기
표면 (Surface)
3
정의 : 실제 그래픽이 그려지는 장소
– 모니터로 나오는 것은 주표면 하나 뿐
위치 : 메모리 영역
– 한 프로그램에서 여러 개의 표면을 다룰 수있다.
주표면
– 비디오 스크린 자체
표면 (Surface)
4
메모리
메인메모리
비데오메모리
표면
표면
표면
표면
표면
표면
표면
표면
표면 (Surface)
5
성질
– 표면은 임의의 크기를 가질 수 있다.
• 예외) 주표면 : 화면해상도와 일치해야 한다.
– 비디오 메모리와 시스템 메모리에 작성할 수있다.
• 비디오 메모리가 빠르다.
– 비트 깊이와 색상 공간으로 속성 정의
• 같은 속성을 갖는 경우 데이터를 교환할 수 있다.
표면 (Surface)
6
종류– 주표면
• Primary Display Surface
• 모니터에 표시되는 표면
• 한 개만 존재
– 보조 표면
• Secondary Display Surface
• 후면버퍼 (back buffer)라고도 불린다.
• 주표면과 같은 속성 (, 크기 )
• 애니메이션을 위한 다음 준비 표면
– 오프스크린 표면
• 실제 모니터와 연결되는 일이 없음
• 하드웨어 가속을 위해 이미지를 저장하는 표면
표면 (Surface)
DirectDraw에서의 표면
– 표면 자체의 포인터
• LPDIRECTDRAWSURFACE7
– 표면 속성 자료구조
• LPDDSURFACEDESC2
– 표면 생성 API
• 성공시 DD_OK
7
HRESULT CreateSurface(
LPDDSURFACE2 lpDDSurfaceDesc,
LPDIRECTDRAWSURFACE7 FAR *lplpDDSurface,
Iunknown FAR *pUnkOuter);
표면 (Surface)
DirectDraw 표면 기술자 구조체
– Surface descriptor structure
8
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
...
표면 (Surface)
DirectDraw 표면 기술자 구조체
– Surface descriptor structure
9
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;
표면 (Surface)
DDSURFACEDESC2의 용도
– 표면의 속성을 정의하거나 수정할 때 사용
– 모든 멤버를 다 정의해야 할 필요는 없음
– 필요한 멤버를 정의하고 정의된 멤버를dwFlags에 지정하면 됨
– dwSize를 지정하는 것을 잊으면 안됨
10
표면 (Surface)
dwFlag의 설정들
11
값 의미
DDSD_ALL 모든 멤버가 유효
DDSD_HEIGHT dwHeight가 유효
DDSD_WIDTH dwWidth가 유효
DDSD_PITCH lpitch가 유효
DDSD_PIXELFORMAT ddpfPixelFormat이 유효
DDSD_LPSURFACE lpSurface가 유효
DDSD_CAPS ddsCaps가 유효
DDSD_ALPHABITDEPTH dwAlphaBitDept가 유효
표면 (Surface)
ddCaps란?
– 화면에 부가적인 속성을 지정할 때 사용
– dwCaps만 사용
• dwCaps2, dwCaps3, dwCaps4는 3D용
12
typedef struct _DDSCAPS2
{
DWORD dwCaps; // capabilities of surface wanted
DWORD dwCaps2;
DWORD dwCaps3;
union
{
DWORD dwCaps4;
DWORD dwVolumeDepth;
} DUMMYUNIONNAMEN(1);
} DDSCAPS2;
표면 (Surface)
DwCaps의 설정들
13
값 의미
DDSCAPS_BACKBUFFER 표면이 후면 버퍼가 된다.
DDSCAPS_FLIP 플립이 가능하다.
DDSCAPS_FRONTBUFFER 표면이 전면 버퍼가 된다.
DDSCAPS_OFFSCREENPLAIN 표면이 오버레이, 텍스쳐, z-버퍼,
전면버퍼, 후면버퍼가 아닌 오프스크린 버퍼이다
DDSCAPS_OWNDC DC를 갖는다
DDSCAPS_PRIMARYSURFACE 주표면이다
DDSCAPS_SYSTEMMEORY 주메모리에 할당된다.
표면 (Surface)
주 표면 생성하기
14
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);
표면 (Surface)
주 표면 생성하기
– 잊지 말 것
15
if (lpddsprimary!=NULL)
lpddsprimary->Release();
// release the directdraw object
if (lpdd!=NULL)
lpdd->Release();
실습
프로그램
– Prog9_1.cpp
16
메모리 주소와 픽셀의 위치
메모리와 픽셀의 대응
– 가로세로 w, h의 크기를 갖는 표면
17
(w-1, h-1)
(0,0)
메모리 주소 증가 방향 (dwBPP / 8 만큼 증가)
메모리 주소 증가 방향 (lPitch만큼 증가)
메모리 주소와 픽셀의 위치
선형 vs 비선형 메모리
– dwBPP / 8 * w = lPitch????
• 선형 메모리 : YES
• 비선형 메모리 : NO
– 하나의 가로줄의 메모리크기가 2^n형태로표현되지 않을때 lPitch가 2^n이 되면서 비선형 메모리가 될 수 있다.
• 2^n이 되면 곱하기가 아닌 shift연산으로 좌표를계산할 수 있다.
– 1024 * x = x << 10
메모리 주소와 픽셀의 위치
비선형 메모리
19
(w-1, h-1)
2^n - 1
메모리 주소 증가 방향 (dwBPP / 8 만큼 증가)
메모리 주소 증가 방향 (lPitch만큼 증가)
화면에표시되지
않는
영역
(0,0)
dwBPP / 8 * (w – 1)
2^n
0
2^n * 2
메모리 주소와 픽셀의 위치
픽셀 좌표 계산
– 픽셀의 메모리 주소• 표면의 주소 + X 좌표 * dwBPP / 8 + Y 좌표 * dwWidth *
dwBPP / 8
– 선형 메모리일 경우 만 옳음
• 표면의 주소 + X 좌표 * dwBPP / 8 + Y 좌표 * lPitch
– 픽셀의 Color 변환법
20
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;
DirectDraw의 그래픽 API
DirectDraw는 가장 기본적인 API만 제공한다.
– 색상 설정
– 표면 메모리 접근
– HW 가속 : 채움(Filling), 블리팅 (BitBlt)
그러면 선? 원? 다각형?
– 위의 API로 각자 제작해야 한다!
팔레트 모드
과거 메모리 절약을 위해 사용하던 color
간접 지정 방식
사용할 color의 리스트를 테이블에 저장하고 실제 표면에는 리스트의 index를 저장
일반적으로 256크기의 테이블 사용
– BPP가 8이 됨
자세한 내용은 생략
픽셀 그리기
그리기 위해 필요한 사항
– 표면의 주소
– BPP, width
표면의 주소
– DDSD의 lpSurface
– lpSurface를 얻는 API : Lock
23
HRESULT Lock(LPRECT lpDestRecct,
LPDDSURFACEDESC2 lpDDSD,
DWORD dwFlags,
HANDLE hEvent);
픽셀 그리기
Lock
– 표면의 위치는 DirectX가 관리, 언제든지 이동 가능
• 따라서 사용하기 위해서는 움직이지 않게 고정(lock)시킬 필요가 있음.
24
HRESULT Lock(LPRECT lpDestRecct, // 변경하고자 하는 영역
LPDDSURFACEDESC2 lpDDSD, // 얻는 표면의 속성
DWORD dwFlags, // 얻고자 하는 속성
HANDLE hEvent); // 사용되지 않음, 반드시 NULL
픽셀 그리기
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을 해야 한다.
26
HRESULT Unlock(
LPRECT lpRect // pointer to rectangle to unlock
);
픽셀 그리기
픽셀을 화면에 찍는 법
– 1. 표면을 잠근다.
– 2. 디스플레이 표면의 포인터를 얻는다.
– 3. 픽셀 주소를 계산한다.
– 4. 주소에 쓴다.
– 5. 표면 잠금을 푼다.
27
픽셀 그리기
픽셀을 화면에 찍는 법
28
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);
실습
프로그램
– 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
31
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)
32
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)
33
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);
34
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는 항상바이트로 표현되기 때문이다.
35
DEMO: PROG9_3.CPP
PROG9_2_16.cpp 와 다른 점은 무엇인가?
36
24비트나 32비트 표면
#define _RGB24BIT(a,r,g,b) ((b) + ((g) << 8) + ((r) << 16) )
24bit 는 32bit 중 최상위 8bit가 사용되지 않을 뿐이다.
따라서 bpp를 32로 하는 것이 좋다.
37
24, 32bit 이용 (1)
In Game_Init()
if (lpdd->SetDisplayMode(SCREEN_WIDTH,
SCREEN_HEIGHT,32,0,0)!=DD_OK) return(0);
38
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);
39
보조표면(Secondary Surface)
보조 표면(또는 후면버퍼) 의 사용
– 부드러운 애니메이션
– 1. 주표면을 생성하고, 주표면으로부터 하나의 보조표면을 생성한다.
– 2. 주표면이 보여지는 동안 보조표면에 그린다.
– 3. 순간적으로 표면을 바꾸어서(switch or
flip), 보조표면이 주표면이 되게 하여, (그 반대로도 마찬가지) 부드러운 애니메이션을 만든다.
40
Page Flipping
41
보조표면의 생성 (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;
42
보조표면의 생성 (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);
43
주표면과 부표면
44
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);
45
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);
46
Flipping 전후
47
오프스크린 표면(off-screen surface)
시스템 메모리나 VRAM에 모두 존재할 수 있음
주표면과 같은 색상깊이와 속성을 가지는 비트맵
하드웨어 가속 Blt()를 위해 사용
스프라이트
– 간단히 말해서 비디오 게임 화면에서 움직이는 작은물체를 뜻한다. 대부분의 경우, 스프라이트는 비트맵일 뿐
– 8비트 PC인 MSX에서 HW에서 사용된 용어
48
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 */ }
49
시스템 메모리에
// 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 */ }
50
팔레트…
생략.
51
질문