パレットによる半透明処理

 半透明処理とは、背景の上に描いたキャラクタなどが「透けて」背景に溶け込んだように見える効果です。これにより、なんとなく「存在」感の薄いキャラクタ(^^;とか、水に浸かったような効果を出す事が出来るでしょう。

半透明処理の原理

 半透明の基本は、背景の色と描くキャラクタの色を「混ぜ合わせる」事です。例えば、RGB各8ビットの24ビットフルカラーDIBの場合を考えてみましょう。まず、背景の点(x, y)にキャラクタの点(cx, cy) を描くとします。この場合、(cx, cy) が透けてみえるようにするには、この二つの点の色を混ぜ合わせるわけですね。もし(x, y) が(255、0、0)(=赤)で、(cx, cy) が(0、0、255)(=青)なら、その点を双方の平均を取って(127、0、127)(=暗い紫)にしてやります。
 これを素直にプログラムにするなら、256×256の背景(bgBMP)の(x, y)から32×32のキャラクタ(chrBMP)を半透明で描く処理は以下のような感じになるでしょう。各座標のRGB成分を平均して、背景に書き込むわけです。

  for (i=0;i<32;i++)
      for (j=0;j<32;j++) {

          b=(bgBMP[(x+j+(i+y)*256)*3]+chrBMP[(j+i*32)*3])/2;
          g=(bgBMP[(x+j+(i+y)*256)*3+1]+chrBMP[(j+i*32)*3+1])/2;
          r=(bgBMP[(x+j+(i+y)*256)*3+2]+chrBMP[(j+i*32)*3+2])/2;

          bgBMP[(x+j+(i+y)*256)*3]=b;
          bgBMP[(x+j+(i+y)*256)*3+1]=g;
          bgBMP[(x+j+(i+y)*256)*3+2]=r;

      }

 ただ、この方法ではかなり処理量が増えてしまうため、ゲームなどで使うのはつらそうです。そこで、 今回はパレットを使って「混ぜ合わせる」計算をしないですむようにしてみました。

パレットによる半透明

 半透明化とは、背景とキャラクタの色を決まった割合で混ぜ合わせる事である、と言えるでしょう。という事は、背景とキャラクタの色が決まれば、半透明にした時の色も決まってしまう事になります。つまり、背景が4色でキャラクタも4色で描くなら半透明で描画した時に現れる色は、最大4×4(半透明で表れる色)+4(背景の色)=20色、という感じで組み合わせの問題になるのです。これなら、背景・キャラクタともに8色で描く場合も72色ですむわけで、8ビット(256色)DIBに適当なカラーテーブル(パレット)を設定すれば実現できそうですね。

・パレットの設定例

0−背景1
1−背景2
2−背景3
3−背景4
4−キャラクタ1+背景1
5−キャラクタ1+背景2
6−キャラクタ1+背景3
7−キャラクタ1+背景4
8−キャラクタ2+背景1
  ・
  ・
19−キャラクタ4+背景4

 この例では、背景をカラーテーブル0ー3番の色で描いて、キャラクタは4、8、12、16番の色で描く事になります。半透明化する時は、背景の色(カラーテーブルの番号)にキャラクタの色を足すだけ。これなら、ゲームなどでも十分な速度を得られるでしょう。

 なお半透明化する時には、色を混ぜ合わせる「割合」を変える事でキャラクタの透明度を変える事もできます。例えば、背景とキャラクタの色を足して2で割るのではなく、キャラクタの色を2倍にして背景の色と足して3で割るとキャラクタが「濃く」なりますし、逆に背景の色を2倍にして3で割るとキャラクタが「薄く」なるわけです。

 下の例は、背景を8色(0ー7)でキャラクタを3色(8、16、24)で描きキャラクタを「濃く」半透明化する場合のカラーテーブル(半透明化に使う部分)の設定例です。0(背景)を黒にしてこの上ではキャラクタを透明化しないようにしてあります。

  for (i=1;i<8;i++) {

      lpRGB[8+i].rgbRed=(lpRGB[8].rgbRed*2+lpRGB[i].rgbRed)/3;
      lpRGB[8+i].rgbGreen=(lpRGB[8].rgbGreen*2+lpRGB[i].rgbGreen)/3;
      lpRGB[8+i].rgbBlue=(lpRGB[8].rgbBlue*2+lpRGB[i].rgbBlue)/3;

      lpRGB[16+i].rgbRed=(lpRGB[16].rgbRed*2+lpRGB[i].rgbRed)/3;
      lpRGB[16+i].rgbGreen=(lpRGB[16].rgbGreen*2+lpRGB[i].rgbGreen)/3;
      lpRGB[16+i].rgbBlue=(lpRGB[16].rgbBlue*2+lpRGB[i].rgbBlue)/3;

      lpRGB[24+i].rgbRed=(lpRGB[24].rgbRed*2+lpRGB[i].rgbRed)/3;
      lpRGB[24+i].rgbGreen=(lpRGB[24].rgbGreen*2+lpRGB[i].rgbGreen)/3;
      lpRGB[24+i].rgbBlue=(lpRGB[24].rgbBlue*2+lpRGB[i].rgbBlue)/3;

  }

 また、このキャラクタを255を抜き色にして描くには、まず背景を表示用ビットマップ(bufBMP)に描いた後

  for (i=0;i<32;i++)
      for (j=0;j<32;j++)
          if (partBMP[j+i*32]!=255)
              bufBMP[x+j+(y+i)*256]+=partBMP[j+i*32];

 という感じでキャラクタを描く事になります。こうしてキャラクタを半透明化して描いたら、先ほどのカラーテーブル(先頭アドレスlpRGB)を指定した8ビットDIBのヘッダを付けて画面に表示しましょう。

  StretchDIBits(hdc,0,0,256,256,0,0,256,256,bufBMP,bufDIB,
	DIB_RGB_COLORS,SRCCOPY);

プログラム

 実行すると、背景と半透明化されたキャラクタが表示されるので、カーソルキーでキャラクタを移動してください。

プログラムソース表示


画像処理プログラミング実験室 > プログラミング資料庫