床井研究室

シス工猫

大学のキャンパスのシステム工学部棟の前というか、システム情報学センターと教育学部の間あたりに、尻尾の短い(足も短い?)キジトラの猫が出没しています。私は猫好きなので(奥さんの影響ですが)、出会うたびに写メを奥さんに送っています(奥さんからそうしろという指示が出ているのです)。私は彼(オスのようです)に勝手に「しまじろう」と名づけているのですが、学生さんは「シス工猫」とか呼んでいるようです。とても人懐っこくてかわいらしいので、多分、以前人に飼われていたんだろうとは思いますが、捨て猫というのは生き残るのがなかなか難しいと聞いているので、この先どうなるかちょっと心配です。

シス工猫

実験の指導書の更新

演習室のパソコンをリプレースする際、演習室から CRT ディスプレイを排除したいという理由で、立体視の実験用のディスプレイが時分割方式から 3D 液晶ディスプレイに変更されました。そのため、実験の指導書もそれに合わせて書き直す必要が出てきました。 ところが、3D 液晶ディスプレイを使った立体表示がドライバレベルでサポートされていない場合は、アプリケーションソフトウェア側でそれに対応した画面表示を行う必要があります。2年前(もう2年も経つんだ…)、3D 液晶ディスプレイを搭載した NEC のノートパソコン (Lavie G Type S) を買ったときに作ったサンプルプログラムソースファイル)では、これにステンシルバッファを使っていました。ウィンドウのサイズが変わるたびに1画素おきの縦線を描いてステンシルバッファにマスクを作り直し、glStencilFunc()glColorMask() を切り替えながら左右の目の画像を4回に分けて描く、という手順になります。しかし、この実装はそれなりに複雑なので、これを実験の指導書に採用する気にはなれませんでした。

glPolygonStipple() を使う

何かほかにいい方法が無いか考えているとき、たまたま screen door transparency のデモを見ていて、その網のパターンがディスプレイ面に対して固定している(動いていない)ことに気づきました。そこで、このデモで使われていた glPolygonStipple() を使って 3D 液晶ディスプレイ用の画面表示を行ってみました(サンプルプログラム)。

用意するのは 32 × 32 ビットのパターンを二つだけです。偶数番目の列を残すマスクと奇数番目の列を残すマスクを作っておき、初期化の際に GL_POLYGON_STIPPLE を有効にしておきます。

static unsigned int emask[32]; /* 偶数番目のライン用マスク */
static unsigned int omask[32]; /* 奇数番目のライン用マスク */

  /* マスクを作る */
  for (i = 0; i < 32; ++i) {
    emask[i] = 0xaaaaaaaa;
    omask[i] = 0x55555555;
  }

  /* 描画するポリゴンにマスクをかける */
  glEnable(GL_POLYGON_STIPPLE);

あとは左右の目の画像を描く際に、glPolygonStipple() に渡すマスクと glColorMask() の組み合わせを切り替えるだけです。この 3D 液晶ディスプレイの視差バリアは副画素の単位で左右の目を分離しているので、R・Bの副画素とGの副画素とでは、割り当てる目が入れ替わります。

  /* 奇数ラインにR・Bを表示 */
  glPolygonStipple((GLubyte *)omask);
  glColorMask(GL_TRUE, GL_FALSE, GL_TRUE, GL_TRUE);

  /* シーンの描画 */
  glNewList(list, GL_COMPILE_AND_EXECUTE);
  scene();
  glEndList();

  /* 偶数ラインにGを表示 */
  glPolygonStipple((GLubyte *)emask);
  glColorMask(GL_FALSE, GL_TRUE, GL_FALSE, GL_FALSE);

  /* シーンの描画 */
  glCallList(list);

結果は期待通りうまくいったので、これを使って実験の指導書を書き直しました。この方法はウィンドウサイズが変わってもマスクを作り直す必要がありませんし、ステンシルバッファという貴重なリソースを消費しないという点でも好みです。

やっぱり時分割方式

シャープ製の 3D 液晶ディスプレイは2視点なので、観測者は頭を動かすことができません。そのためヘッドトラッキングの実験などができなくなってしまいました。またプログラムの組みやすさや、表示画像の画質や見易さの点でも時分割方式の方が優れているので、次回(があれば4年先だけど)は時分割方式に戻したいなあと考えて始めています。最近は液晶ディスプレイの応答速度も向上してきたようですし、時分割方式が液晶ディスプレイで実現できないものでしょうか。今度、実験してみようかな。

GLSL による実装(2026 年 8 月 23 日追記)

OpenGL 2.0 で GLSL(プログラマブルシェーダ)が導入された後は、ステンシルバッファや glPolygonStipple() のような固定機能のマスキングを使わず、フラグメントシェーダ内で直接ピクセル座標を判定して左右の目の画像をインターリーブ合成する版(pitcher2)も作成しました。

フラグメントシェーダ内では、gl_FragCoord.x(スクリーンの X 座標)の偶数・奇数と、左右の視点を切り替える uniform 変数 leftOrRight(0: 右目, 1: 左目)に応じて、画素単位で R・B 成分と G 成分を出力します。

// 3dlcd.frag
uniform int leftOrRight;

void main (void)
{
	if (leftOrRight == 0) {
		// 右目
		if (mod(gl_FragCoord.x, 2.0) < 1.0) {
			// 偶数ライン: G
			gl_FragColor = vec4(0.0, gl_Color.g, 0.0, 1.0);
		}
		else {
			// 奇数ライン: R, B
			gl_FragColor = vec4(gl_Color.r, 0.0, gl_Color.b, 1.0);
		}
	}
	else {
		// 左目
		if (mod(gl_FragCoord.x, 2.0) < 1.0) {
			// 偶数ライン: R, B
			gl_FragColor = vec4(gl_Color.r, 0.0, gl_Color.b, 1.0);
		}
		else {
			// 奇数ライン: G
			gl_FragColor = vec4(0.0, gl_Color.g, 0.0, 1.0);
		}
	}
}

この方法を用いれば、CPU 側でマスク用の幾何図形を描いたりステートを何度も切り替えたりすることなく、GPU 上で効率的に視差バリア向けの合成を行えます。