読者です 読者をやめる 読者になる 読者になる

S.F. Page

Programming,Music,etc...

WebGL + GLSLでレトロPC風グラフィックスメモリ(RGBプレーン)の再現ができた - PCグラフィックスを懐かしむ。楽しむ。(9)

RGBのプレーンの再現だがなんとかできた。

デモページ:http://github.sfpgmr.net/graphics/devver/20160408/index.html

ソースコード: GitHub - sfpgmr/graphics at 98af700d92b169b8449067b619c8c5baa7310d30

以下のような仕様でソースを書いてみた。

  1. R/G/Bプレーンをそれぞれ独立したテクスチャとして持つ。
  2. プレーンのテクスチャは幅64ピクセル、高さ256ピクセルのgl.LUMINANCE形式。
    実際の幅は512ピクセルだが、8ピクセルで1バイトとするので、512/8 = 64バイトとなる。
  3. パレットは幅8ピクセル、高さ1ピクセルgl.LUMINANCE形式のテクスチャとする。
  4. 表示用のcanvasを作る。実画面サイズによって640x480もしくは320x240に切り替える。
  5. 画面一杯に表示する矩形ポリゴンを1枚作る。
  6. テクスチャのuvサイズは表示領域が幅320x高さ240となるように調整する。
  7. フラグメントシェーダーで各プレーンのテクスチャを読み出し、その値をインデックスとしてカラーパレット用のテクスチャを読み出し、ピクセルの色とする。
  8. 描画時は各プレーンに色情報を各プレーンに書き込む。
  9. requestAnimationFrameの周期で、毎回プレーンとカラーパレットの情報をテクスチャに転送し、矩形ポリゴンを表示用のcanvasに描画する。

シェーダーコードは以下のとおりである。前回と比べてフラグメント・シェーダーのコードを変更している。

// パレットエミュレートシェーダー
var vshaderPSrc = 
`precision mediump float;
attribute vec2 position;
attribute vec2 texture_coord;
varying vec2 vtexture_coord;
 
void main(void) {
    gl_Position = vec4(position,0.0,1.0);
    vtexture_coord = texture_coord;
}
`;

var fshaderPSrc = 
`precision mediump float;
uniform sampler2D textureB;
uniform sampler2D textureG;
uniform sampler2D textureR;
uniform sampler2D pallet_color;
varying vec2 vtexture_coord;
void main(void){
 // テクスチャ座標よりビット位置を求め、そのビットが立った数値を得る。
 float t = exp2(floor(mod(vtexture_coord.x * 512.0,8.0)));
 // RGB各プレーンの現在座標のバイトデータを読み込む
 vec4 rt = texture2D(textureR, vtexture_coord);
 vec4 gt = texture2D(textureG, vtexture_coord);
 vec4 bt = texture2D(textureB, vtexture_coord);
 
 // バイトデータの中でビットが立っているかどうかを調べる
 // Rプレーン
 float r = floor(mod(min(rt.x * 256.0,255.0) / t,2.0)) * 4.0;
 // Gプレーン
 float g = floor(mod(min(gt.x * 256.0,255.0) / t,2.0)) * 2.0;
 // Bプレーン
 float b = floor(mod(min(bt.x * 256.0,255.0) / t,2.0));
 // 各色の値を足して正規化を行い、パレットインデックスから実際の色を得る 
 vec4 p = texture2D(pallet_color,vec2((r + g + b) / 8.0 ,0.5));
 float i = min(p.x * 256.0,255.0);
 float ar = floor(mod(i * 0.25,2.0)); // bit3
 float ag = floor(mod(i * 0.5,2.0));  // bit2
 float ab = floor(mod(i,2.0)); // bit1
 
 gl_FragColor = vec4(ar,ag,ab,1.0);
}

問題はWebGL 1.0のGLSLではビット演算ができないのでそれをどうするかであった。

プレーン上のピクセルの位置は以下で求められる。

バイト位置:ピクセル位置座標 / 8 (切り捨て)
ビット位置:ピクセル位置座標 / 8の整数剰余

そしてバイト位置のデータを読み出して、ビット位置のビットが立っているかどうかを調べる。R/G/Bの各ビットを調べ、それをRGBの3ビットのビット列にすれば色番号を求めることができる。

一番問題なのはバイト位置にあるビット位置が立っているかどうかをどう求めるかである。試行錯誤した結果、以下の式で求めることができた。

floor(mod(【バイトデータ】/ pow2(【ビット位置】),2.0))

上記計算によって、バイトデータの指定ビットが立っているときは1.0、立っていないときは0.0の値が得られる。この式によって何とかRGBプレーンをフラグメント・シェーダー側でそのまま渡し、パレットに従い色を取得して描画できるようになった。

今後はこの環境を使って、プリミティブの描画を行っていくことにする。