08. 블렌딩

소개




기다린 보람이 있었습니다. 정말 끝내주는 사이트인 Hypercosm의 동료 프로그래머 한 명이 자신이 블렌딩에 대한 튜토리얼을 쓸 수 있겠느냐고 물어왔습니다. 어쨌든 제8강이 블렌딩 튜토리얼이 될 예정이었기에 시기상으로도 완벽했지요! 이 튜토리얼은 제7강을 확장합니다. 블렌딩은 정말 멋진 효과입니다. 여러분이 모두 이 강좌를 즐기길 바라며 이 튜토리얼의 저술자는 Tom Stanis님이라는 것을 밝힙니다. 이 분이 이 강좌에 많은 노력을 기울였으니 여러분의 의견을 알려주세요. 블렌딩은 쉽게 설명할 수 있는 주제가 아닙니다.













본문


간단한 투명효과


OpenGL에 있는 대부분의 특수효과는 블렌딩에 의존합니다. 블렌딩은 화면에 곧 그릴 어떤 픽셀의 색을 이미 화면상에 존재하는 픽셀의 색과 결합하는데 사용됩니다. 색상들을 결합하는 방법은 그 색상들의 알파값과 블렌딩 함수에 기초합니다. 알파는 보통 제일 마지막에 정의되는 4번째 색상요소입니다. 과거 우리는 GL_RGB를 사용하여 3개의 요소를 사용하는 색상을 지정했습니다. 알파값도 지정하려면 GL_RGBA를 사용하면 됩니다. 또한 glColor3f()대신에 glColor4f를 사용합니다.

대부분의 사람들은 알파를 어떤 재질의 불투명도를 나타낸다고 생각합니다. 0.0의 알파값은 그 재질이 완전히 투명하며, 1.0의 값은 완전히 불투명한것을 의미합니다.

블렌딩(혼합) 공식


수학을 별로 안좋아하시고 곧바로 투명색을 적용하는 법을 보고 싶으신 분들은 이 단락을 생략하세요. 이 단락은 블렌딩의 작동원리를 배우고 싶어 하시는 분들을 위해 준비해뒀을 뿐입니다.


(Rs Sr + Rd Dr, Gs Sg + Gd Dg, Bs Sb + Bd Db, As Sa + Ad Da)

OpenGL은 위의 공식에 기초하여 두 픽셀을 블렌딩한 결과를 계산합니다. 아랫첨자 s와 d는 소스(src) 픽셀과 데스트(dst) 픽셀을 나타냅니다. S와 D 요소는 혼합 요소(blending factor)입니다. 이 값들은 픽셀들을 어떻게 혼합하고 싶은지를 나타냅니다. S와 D에 사용하는 가장 일반적인 값들은 S의 경우 원본 알파값인 (As, As, As, As)이고 D의 경우 1 - 원본알파값인 (1, 1, 1, 1) - (As, As, As, As)입니다. 이는 다음과 같은 블렌딩 식을 만들어 낼 것입니다.

(Rs As + Rd (1 - As), Gs As + Gd (1 - As), Bs As + Bd (1 - As), As As + Ad (1 - As))

이 식은 투명/반투명 스타일의 효과를 만들어 냅니다.

OpenGL에서 혼합하기

여태까지 해왔던 다른 일들과 마찬가지 방법으로 블렌딩을 켭니다. 그리고 수식을 정의하고 투명 객체를 그릴 때는 반투명 객체 뒤에 있는 물체를 계속해서 그리기 위해서 깊이 버퍼 쓰기를 끕니다. 이것은 블렌딩을 하는 적절한 방법은 아니지만 간단한 프로젝트에서는 대부분의 경우 아무 문제없이 작동할 것입니다. 루이 마틴스가 추가한 내용: 모든 투명(알파값 < 1.0) 폴리곤들을 그리는 올바른 방법은 전체장면을 그린 뒤에 반대 깊이 순서로 폴리곤들을 그리는 것입니다(즉, 가장 멀리 있는 것부터 먼저). 이는 두개의 블렌딩(1과 2)을 다른 순서로 혼합하면 다른 결과가 나오기 때문입니다. 즉, 폴리곤 1이 관찰자 바로 근처에 있다면 폴리곤 2를 먼저 그리고 폴리곤 1을 그리는게 올바른 방법입니다. 이것을 실제세계에서 본다고 생각한다면 이 두 폴리곤(투명임) 뒤로부터 오는 모든 광원들은 폴리곤 2를 먼저 지나친 뒤 폴리곤 1을 지나서야 관찰자에 눈에 들어오게 됩니다.  ''' 전체 장면을 다 그리고 나서''' ''' 깊이 버퍼를  활성화한후에 깊이 버퍼에 의해 정렬'''한 투명한 폴리곤들을 그려야합니다. 그렇지 않으면 잘못된 결과를 얻을 것입니다. 이것이 때로는 매우 귀찮은 일이지만 이것이 제대로 일을 처리하는 방법입니다.

저희는 지난 튜토리얼에서 사용했던 코드를 사용할 것입니다. 코드의 제일 윗부분에 두개의 새 변수를 추가합니다. 코드를 읽기 편하도록 전체 코드부분을 전부 다시 보여드리겠습니다.

#include <windows.h>                    // 윈도우즈용 헤더파일
#include <stdio.h>                      // 표준 입출력용 헤더파일
#include <gl\gl.h>                      // OpenGL32 라이브러리용 헤더파일
#include <gl\glu.h>                     // GLu32 라이브러리용 헤더파일
#include <gl\glaux.h>                   // GLaux 라이브러리용 헤더파일

HDC        hDC=NULL;                    // GDI 장치 컨텍스트
HGLRC        hRC=NULL;                  // 렌더링 컨텍스트
HWND        hWnd=NULL;                  // 창 핸들
HINSTANCE    hInstance;                 // 응용프로그램 인스턴스

bool    keys[256];                      // 키보드 루틴에 사용하는 배열
bool    active=TRUE;                    // 창 활성화 플래그. 디폴트값은 TRUE
bool    fullscreen=TRUE;                // 전체화면 플래그. 디폴트값은 전체화면 모드
bool    light;                          // 조명 ON/OFF
bool    blend;                          // 블렌딩 OFF/ON? ( 새코드 )
bool    lp;                             // L이 눌렸는가?
bool    fp;                             // F가 눌렸는가?
bool    bp;                             // B가 눌렸는가? ( 새코드 )

GLfloat    xrot;                        // X 회전
GLfloat    yrot;                        // Y 회전
GLfloat xspeed;                         // X 회전속도
GLfloat yspeed;                         // Y 회전속드

GLfloat    z=-5.0f;                     // 화면속으로 깊이

GLfloat LightAmbient[]=  { 0.5f, 0.5f, 0.5f, 1.0f };       // 주변광 값
GLfloat LightDiffuse[]=     { 1.0f, 1.0f, 1.0f, 1.0f };    // 산란광 값
GLfloat LightPosition[]= { 0.0f, 0.0f, 2.0f, 1.0f };       // 광원 위치

GLuint    filter;                        // 사용할 필터번호
GLuint    texture[3];                    // 텍스처 3개용 저장소

LRESULT    CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);    // WndProc 선언


LoadGLTextures()로 내려가 if (TextureImage[0]=LoadBMP("Data/Crate.bmp"))라고 되어있는 줄을 찾으세요. 이 코드를 아래와 같이 바꿉니다. 이 튜토리얼에서는 궤짝 텍스처 대신에 스테인 글래스 텍스처를 사용할 것입니다.

   if (TextureImage[0]=LoadBMP("Data/glass.bmp"))    // 스테인 글래스 비트맵을 읽어옴 ( 수정 코드 )

InitGL() 코드부분의 어딘가에 다음의 두 라인을 추가합니다. 첫번째 라인이 하는 일은 물체의 밝기를 최고로, 알파값(불투명도)을 50%로 설정하는 것입니다. 이는 블렌딩을 사용할 경우 그 물체가 50% 투명하게 된다는 뜻입니다. 두번째 라인은 코드에서 사용할 블렌딩 종류를 정합니다.


루이 마틴스가 추가한 내용: 0.0의 알파값은 그 재질이 완전히 투명하다는 뜻입니다. 1.0은 완전한 불투명입니다.

 
   glColor4f(1.0f,1.0f,1.0f,0.5f);            // 최고 밝기. 50% 알파 ( 새코드 )
   glBlendFunc(GL_SRC_ALPHA,GL_ONE);        // 소스 알파 값에 기초한 투명 블렌딩 함수 ( 새코드 )


다음의 코드를 찾읍시다. 제7강 가장 아래쪽에 있는 코드입니다.

   if (keys[VK_LEFT])                // 왼쪽 키가 눌렸는가?
   {
       yspeed-=0.01f;                // 그렇다면 yspeed를 감소시킨다
   }


위 코드 바로 아래부분에 다음의 코드들을 추가합니다. 아래의 코드들은 'B'키가 눌렸는지를 검사합니다. 만약 그렇다면 블렌딩이 켜져있는지 꺼져있는지를 확인합니다. 블렌딩이 켜져 있다면 블렌딩을 끕니다. 꺼져있었다면 켭니다.

 
   if (keys['B'] && !bp)                // B키가 눌렸고 bp가 FALSE인가?
   {
       bp=TRUE;                // 그렇다면 bp가 TRUE로 된다
       blend = !blend;                // blend를 TRUE / FALSE로 토글
       if(blend)                // blend가 TRUE이면
       {
           glEnable(GL_BLEND);        // 블렌딩을 켠다
           glDisable(GL_DEPTH_TEST);    // 깊이 테스트를 끈다
       }
       else                    // 그렇지 않으면
       {
           glDisable(GL_BLEND);        // 블렌딩을 끈다
           glEnable(GL_DEPTH_TEST);    // 깊이 테스트를 끈다
       }
   }
   if (!keys['B'])                    // B키가 안눌린다면
   {
       bp=FALSE;                // bp가 FALSE가 된다
   }


그러나 텍스처 맵을 사용한다면 어떻게 색상을 지정할 수 있을까요? 간단합니다. 모듈레이트(modulated)를 가지는  텍스쳐 혼합 모드에서는 텍스처 맵핑된 각 픽셀이 현재 색상과 곱해집니다. 따라서 그려져 있던 색상이 (0.5, 0.6, 0.4)라면 이 색상과 현재 색을 곱해서 (0.5, 0.6, 0.4, 0.5)를 얻게 됩니다. (특별히 지정된 바가 없다면 알파값은 1.0으로 가정합니다.)

이것이 전부입니다! OpenGL에서 블렌딩을 하는 것은 사실 매우 쉽지요.

노트 (1999년 11월 13일) 


저, NeHe가 블렌딩 코드를 약간 손봤습니다. 출력결과를 좀더 그렇듯하게 바꾸기 위해서였습니다. 소스(Source)와 데스트(Dest)에 알파값을 사용하여 블렌딩을 하는 것은 원하지 않는 결과를 만들어 내게됩니다. 옆면을 따라 뒷면이 어둡게 나타난다든가 하는 등의 문제입니다. 간단히 말해 그 물체가 매우 엉망으로 보일 것이란 이야기지요. 제가 블렌딩을 하는 방법이 최선의 방법은 아닐지도 모르지만 잘 작동하며, 조명을 켤 경우 그 물체가 올바르게 보입니다. 기초 코드를 제공해준 Tom에게 감사의 말씀을 드립니다. 그의 코드가 알파값을 사용하는 블렌딩의 적절한 방법을 보여주고 있지만 다른 사람들이 기대한 만큼 매력적인 모습을 보여주진 않았습니다.

glDepthMask() 함수와 잘 작동하지 않는 비디오카드들 때문에 발생하는 문제점을 고치기 위해 코드를 다시한번 수정했습니다. 어떤 비디오 카드에서는 이 함수가 깊이 버퍼 테스트를 효과적으로 켜고 끄지 못하는 것처럼 보입니다. 따라서 예전에 사용하던 깊이 테스트 활성/비활성화 방법으로 돌아갔습니다.

텍스처 맵에서 알파값 읽어오기


투명효과용으로 사용하는 알파 값을 텍스처맵에서 불러올 수도 있습니다. 다른 색상을 읽어오는 것과 마찬가지로 말입니다. 이 일을 하려면 읽어올 이미지 안에 알파값을 저장한 뒤 glTexImage2D() 함수를 호출하는 곳에서 색상 포맷을 GL_RGBA로 지정하면 됩니다.

질문?

궁금증을 가지고 계시다면 저에게 이메일을 보내주세요. stanis@cs.wisc.edu입니다.

소스코드 다운로드

이 강좌의 소스코드를 다운받으실 수 있습니다. 자신의 환경에 맞는 파일을 받아 사용하세요.




원문 정보

  • 저자: Tom Stanis, Jeff Molofee (NeHe)
  • 원문보기: Lesson 08

번역문 정보

  • 초벌번역: 포프
  • 재벌번역: 이스
  • 감수: 이스

현재 상태

  • 초벌번역시작 (2006년 1월 7일)
  • 초벌번역완료 (2006년 1월 27일)
  • 재벌번역완료 (2006년 1월 30일)


Comments