더블버퍼링, BitBlt, TransparentBlt, AlphaBlend, BLENDFUCNTION

A Code AM·2020년 3월 12일
0

수업(20200212~)

목록 보기
2/12

일반적인 비디오 카드에서 FrameBuffer는 2중 포트(Dual Port)구조
한쪽은 GPU, 다른 한 쪽은 Video Controller
GPU는 그려질 화면을 프레임 버퍼에 쓰는 일을
Video Controller는 프레임 버퍼에 그려진 화면을 출력하는 일을 함
=> 싱글 버퍼링

GPU가 프레임 버퍼에 쓰는 작업은 Video Controller가 프레임 버퍼를 읽는 속도에 비해 훨씬 느리다

Flickering(깜빡임) 현상은 이러한 속도차로 발생하는 문제로 Frame Buffer에 완전히 정보가 저장되지 않은 상태로 불완전한 내용의 버퍼를 화면에 그리면서 발생한다 => 이 현상을 해결하기 위해 더블버퍼링 사용.
(참고)
https://t1.daumcdn.net/cfile/tistory/147FDC4E4D5B671C0F

싱글 버퍼링은 비디오 메모리 (프론트 버퍼 = 화면 DC)만 사용하는데 예비 버퍼 (백 버퍼 = memDC, backDC)를 둬서 먼저 버퍼2에 그림 보내고 그것을 다시 버퍼 1로 보낸 다음 버퍼 1의 데이터를 처리하는 동안 다시 버퍼 2에 그림을 보내면 데이터 전송과 처리가 막히는 일이 없어 깜빡임이 사라진다 > 이 때 그림의 복사는 BitBlt라는 고속복사로 이루어짐

Blt = bit-block transfer

BitBlt

더블 버퍼링에 사용되는 내부 버퍼는 구체적으론 메모리 영역인데 이는 외부 버퍼, 즉 화면의 포맷과 호환되어야 함. 그래야 내부 버퍼에 그린 그림을 별도의 조작없이 외부 버퍼로 고속 전송 할 수 있음. 과거 DOS시절에 내부 버퍼를 비디오 RAM의 물리적은 포맷대로 작성한 후 비디오 램의 곧바로 전송 방식을 사용했었다. 또는 아예 비디오 카드가 하드웨어적으로 여러 개의 페이지를 제공해서 페이지를 교체하는 방식을 사용하기도 했다.

윈도우즈에선 내부 버퍼를 메모리에 직접 작성할 필요가 없음 왜냐면 비트맵이 내부 버퍼 역할을 해주어서. 화면 DC와 호환되는(색상 포맷이 같고 크기가 동일한) 비트맵을 생성한 후 이 비트맵에 그림을 그리면 비트맵 자체가 내부 버퍼 역할을 하게 된다. < Bitblt함수 사용

더블 버퍼링에서 내부 버퍼 = 비트맵 , 메모리DC가 아님
메모리DC는 비트맵 선택하기 위해 잠시 사용되는 DC임.

Video Controller가 프론트버퍼의 내용을 출력하는 동안 GPU는 백 버퍼에 다음에 그려질 내용을 쓴다. GPU가 내용을 전부 썼으면 비디오 컨트롤러가 백버퍼로 스위칭해서 새로운 내용을 화면에 그림. 동시에 GPU는 프론트 버퍼로 스위칭, 새로 화면에 그릴 내용을 버퍼에 쓴다.

*현재 비디오 컨트롤러가 읽고 있는 버퍼를 프론트버퍼로 정의 / 화면에 내용이 보이지 않지만 그래픽 프로세서가 기록중인 버퍼가 백버퍼. 따라서 버퍼는 프론트버퍼와 백버퍼 반복함.

더블 버퍼링 위해서는 두 배의 메모리가 필요
예를 들어 어떤 버퍼가 화소당 R, G, B, A 각각 8비트씩 할당한다고 하면 해당 화소에 맺히는 물체의 깊이 정보를 저장하기 위해 16비트 깊이 버퍼(z-buffer)를 할당한다 치면 한 화소당 총 48비트를 요구.
만약 여기에 더블버퍼링 적용하면 동일한 크기의 메모리가 필요하므로 화소당 총 96비트가 필요

더블 버퍼링도 완벽하지 않아서 백버퍼에 화면을 넣고 있는 동안 프론트 버퍼와의 전환이 이루어지면 지금 넣고 있는 것과 이전 화면이 섞이면서 테어링(=화면 찢어짐)이 발생하는데 이 것을 제거하기 위한 기술이 수직동기화(V-sync)이다.

Bitblt 에 인자가 많이 들어감
1. HDC hdc
: 프론트버퍼, 실제 화면DC
2. HDC backMemDC
: 백버퍼, 백DC, 메모리DC. 화면에 보내기 직전의 완성된 메모리DC. 모든 이미지들이 작업되는 DC 공간이다.
3. HDC MemDC1, MemDC2
: 그리려고 backMemDC의 크기설정, backMemDC는 초기 1픽셀 공간을 가지고 있다. 그런데 다른 메모리 디씨의 이미지를 백DC에 옮기려 하면 공간이 없으니 복사가 안된다. 그래서 먼저 backMemDC에 도화지를 펼쳐줘야 한다.
4. HBITMAP hMyBitmap, hOldBitmap
: 그릴 비트맵들의 핸들

BOOL BitBlt (HDC hdcDest, int nXDest, int nYDest, in nWidth, nHeight, HDC hdcSrc, int nXSrc, int nYSrc, DWORD dwRop)
  1. HDC hdcDest
    : 복사될 공간(Destination)의 HDC
  2. int nXDest, int nYDest, in nWidth, nHeight
    : 복사할 공간의 X, Y, Width, Height
  3. HDC hdcSrc
    : 복사가 이루어지는 곳(Source)의 DC
  4. int nXSrc, int nYSrc
    : 복사하는 소스의 좌표 > BitBlt는 비트맵 크기 변경 안하고 통째로 복사한다 (= 폭과 높이는 복사 대상에서 한번만 지정함)
  5. DWORD dwRop
    : 레스터 연산 방법 지정. SRCCOPY를 쓰면 복사할 곳에서 그대로 복사 대상으로 복사한다. dwRop에 다른 값을 사용하면 기존 그림에 겹친다거나 반전시킬 수 있다.

비트맵 출력이 끝난 후에는 비트맵 자체와 메모리 DC를 해제 해야 한다. 비트맵은 GDI오브젝트라서 DeleteObject 함수로 지우면 되고 메모리 DC는 DeleteDC를 사용해서 지워야 한다.

단점
: 내부 버퍼에 그림 그린 후 외부 버퍼로 다시 전송해서 전체적인 속도가 느려질 수 있다 > 이때 1초에 몇번씩 전송할 수 있는가를 프레임 레이트(Frame Rate)라고 하는데 내부 버퍼에 그림 준비하는 과정이 복잡할수록 프레임 수가 떨어짐. 16 프레임 이상 정도만 되어도 부드러운 움직임 구현 가능하다.

GdiTrnasparentBlt ( = TransparentBlt)

: 픽셀에 대응하는 사각형의 색 데이터를 비트-블록 단위로 지정된 Source DC(device context)에서 Destination DC로 전송.

가장 핵심 인자는 UINT crTransparent : 투명하게 처리할 색상 지정해주면 그 색상은 투명으로 처리하고 나머지 색상을 Destination DC로 복사한다.

BOOL GdiTransparentBlt(
  HDC  hdcDest,
  int  xoriginDest,
  int  yoriginDest,
  int  wDest,
  int  hDest,
  HDC  hdcSrc,
  int  xoriginSrc,
  int  yoriginSrc,
  int  wSrc,
  int  hSrc,
  UINT crTransparent
);
  1. HDC hdcDest
    : 렌더할 위치 대상의 DC
  2. int xoriginDest, int yoriginDest
    : 렌더 시작점 X, Y 설정
  3. int wDest, int hDest
    : 렌더 할 영역의 가로, 세로 설정
  4. HDC hdcSrc
    : 찍을 메모리 DC (찍을 이미지)
  5. int xoriginSrc, int yoriginSrc
    : 이미지의 시작점 X, Y
  6. int wSrc, int hSrc
    : 이미지의 찍을 영역 가로, 세로
  7. UINT crTransparent
    : 제외할 픽셀 색상(RGB) 또는 0x~

주의점
1. 이미지가 그려질 영역을 지정하기 때문에 확대/축소 가능
2. 확대/축소 가능해서 stretchBlt 함수와 동일한 기능을 하는것처럼 보이나 -영역에 대해서는 인식하지 않아 거꾸로 뿌려지지는 않음
3. 확대/축소시 연산량이 많아져서 사이즈를 동일하게 해서 사용해야 부하가 적다.

GdiAlphaBlend

: 투명 or 반투명 지원

BOOL GdiAlphaBlend(
  HDC           hdcDest,
  int           xoriginDest,
  int           yoriginDest,
  int           wDest,
  int           hDest,
  HDC           hdcSrc,
  int           xoriginSrc,
  int           yoriginSrc,
  int           wSrc,
  int           hSrc,
  BLENDFUNCTION ftn
);

블렌드 구조체 (BLENDFUCNTION)

: 소스 및 대상 비트맵 블렌딩 기능 지정해서 블렌딩 제어하기 위한 구조체

typedef struct _BLENDFUNCTION {
  BYTE BlendOp;
  BYTE BlendFlags;
  BYTE SourceConstantAlpha;
  BYTE AlphaFormat;
} BLENDFUNCTION, *PBLENDFUNCTION;
  1. BYTE BlendOp
    : 원본 혼합 방식 지정. 현재는 원본 이미지와 대상 이미지를 섞는다는 의미인 AC_SRC_OVER만 사용한다.
  2. BYTE BlendFlags
    : 0이어야 함
  3. BYTE SourceConstantAlpha
    전체 소스 비트맵에 사용되는 알파 투명도 값 지정함. SourceConstantAlpha는 임의의 픽셀당 알파값과 결합. 0(투명) ~ 255(불투명) 사이의 값 지원해서 그 사이 값 입력하면 반투명 지원하는 것. 그렇기 때문에 AlphtBlend 함수는 TransparentBlt 함수처럼 특정 색을 투명하게 하는게 아니라 Source DC에 있는 픽셀과 Destination DC에 있는 픽셀을 혼합해서 색을 만드는 것
  4. BYTE AlphaFormat
    : 원본과 destination 비트맵들이 해석되는 방식을 조절한다. 32비트맵인 경우 AC_SRC_ALPHA로 설정하고 그 외에는 0으로 설정.

AC_SRC_ALPHA

: 이 플래그는 그 비트맵이 픽셀 별 알파 채널 가질때 설정됨(= per-pixel alpha). 그 API들이 premultiplied alhpa를 사용한다는 뜻이고 이는 해당 비트맵의 R, G, B채널의 값들은 반드시 알파 채널의 값과 premultiplied 된다는 뜻이다.
예를 들어, 알파 채널 값이 x면 호출 전에 r, g, b채널은 반드시 x와 멀티플라이되고 0xff로 나뉜다.

Q. Premultiplied Image?
A. RGB x A(alpha)로 된 이미지. 이 이미지는 RGB픽셀이 절대 알파값보다 밝아질수 없으며 (곱하므로) 알파가 검정색(0,0,0)이면 RGB도 검정색이 됨. 마야나 맥스에서 알파 체크하고 렌더링한 이미지들이 Premultiplied Image들임.

GdiAlphaBlend, GdiAlphtBlend 함수와 TransparentBlt, AlphaBlend 함수의 차이점

: Gdi 함수의 경우 특별한 라이브러리 없이 Windows.h만 include 하면 그냥 사용 가능하지만 TransparentBlt와 AlphaBlend는 WINAPI의 함수가 아니라서 라이브러리 링크를 시켜주어야 사용 가능함.

Q. 왜 TransparentBlt는 라이브러리 링크하나?
A. GdiTransparentBlt, GdiAlpha는 Gdi 라이브러리 (gdi32.lib)에 있고, Windows.h 헤더에 포함되어 있어서 Windows.h 헤더만 있어도 사용할 수 있지만 Transparent와 AlphaBlend 함수는 msimg32 라이브러리 포함되어 있어 따로 라이브러리 링크해야 됨.

profile
배움기록

0개의 댓글