元の解像度から最大6倍に拡大する CRT シェーダを複数書いて、Neko Project 21/W(以下 np21w)に組み込んで CRT っぽい画面を描画できるようにしてみた。
発端と計画
LCD に映像を出力して PC-98 を使っていると時々 CRT を接続したいという気分になるが、置き場所や重量などを考えると気軽に入手できる感じでもない。 一方、普段実機を使ってばかりというわけでもなくエミュレータを使うこともある。 np21w は画面設定で Direct3D による描画を選べるので、どうにかシェーダを読み込めるようにして HLSL を書けば CRT シェーダを足せる筈だと思って試してみることにした。
PC 用 CRT の個人的な印象はアパーチャグリルなので、縦 1px の列を RGB に分けて 3n(n は1以上の整数)倍の解像度で描画するシェーダを用意するのが良さそうである。
PC-98 ノーマルモードの解像度 640×400 の3倍の 1920×1200 の LCD は既に所有しているものがあったが、大きさは24インチで横方向には約 52cm なので、これに3倍解像度の映像を表示した時、AG ピッチに相当する距離(以下、LCD 上の AG ピッチやドットピッチ相当の距離でも単に「AG ピッチ」、「ドットピッチ」と書く)は 520 / (1920 / 3) = 0.8125 mm となる。
調べてみると1980年代半ばの SONY トリニトロン管 KV-14MD1 でも AG ピッチは 0.37mm らしい1ので、0.8125mm というのは粗すぎる結果になる気がする。
そこで、他にいい感じのディスプレイがないか探してみると、6倍の WQUXGA の解像度で15インチ前後のモバイルディスプレイが販売されているのが分かり、適当に見繕った結果 Intehill U13NA を買ってみた。
U13NA は13.4インチ(約 34cm)なので、横方向には約 29cm であり、これに6倍解像度の映像を表示した時、AG ピッチは 290 / (3840 / 3) = 0.226... ≒ 0.23 mm となる。
2000年頃の SONY FD トリニトロン管 CRT の AG ピッチは 0.22~0.25mm くらいのよう2 3なので、流石に粗さは目立たないだろう。
また、画面の大きさ的に14インチ程度の CRT に映しているような雰囲気になると思われる。
実装
CRT シェーダ追加の主な作業として、np21w の Direct3D 周りの実装調整と、シェーダ自体の実装の2つを行う必要があった。
シェーダ本体以外の実装
シェーダをそれらしく動かすのに最低限必要そうな部分しかコードを読んでいないが、np21w の Direct3D での描画処理は scrnmng_d3d.cpp に書かれている。
描画処理では、サーフェスをロックして画面の内容を書き込んだ後、そのサーフェスの内容をバックバッファに転送して描画を行っている。
そこで、以下のような処理を追加して CRT シェーダを適用できるようにした。
しかし、元々の実装を残しつつシェーダによる描画機能を追加するのが上手くいかず、最終的に各所のコードを置き換えたため描画部分の実装は今回の実験専用となってしまった。
- 画面の内容が書かれたサーフェスを一旦テクスチャに転送する。
- 手順1で用意したテクスチャを貼った板ポリを自作の効果(CRT シェーダ)とともに別のサーフェスに描画する。
- 手順2で描画したサーフェスの内容をバックバッファに転送する。
他に、シェーダの描画に必要な各種リソースの初期化と解放処理等も追加した。
助かったのはシェーダの読み込みとコンパイルをプログラム実行時に行うようにできたことである。 これにより、np21w を再起動するだけで更新したシェーダの動作確認ができるようになったので、作業が結構捗った。
3倍及び6倍アパーチャグリルシェーダ
前述の通り、CRT の PC 用ディスプレイ画面の個人的な印象はアパーチャグリルなので、縦 1px の列を RGB に分けて3倍及び6倍の解像度で描画するシェーダの実装からやってみた。 3倍の場合の基本的なアプローチは以下の通り。 6倍では拡大結果が 6×6 になり R、G、B の列が2回繰り返されるようになる以外は大体同じである。
- 元の画素を 3×3 のサイズに拡大して各列で R、G、B のみ表示する。
- 元の画素の色は平滑化フィルタで周囲も考慮して取得する。
- 3×3 ブロックのうち最上行を少し暗くして走査線を再現する。
- 画素が暗くなるにつれて 3×3 ブロックの最上行と最下行が中央行よりも暗くなるようにする。
- File:Aperture grille closeup teletext.jpg の横の端の方の画素のような感じ。
R G B
+--+--+--+
| | | | 走査線
+--+ +--+--+--+
| | → | | | |
+--+ +--+--+--+
元画素 | | | |
+--+--+--+
拡大結果
上述のアプローチで最初のシェーダを実装してみたが、第一印象は「暗い!」に尽きた。 1px の列が合計の光量はほぼそのままで 3px か 6px に広がるので当然である。 シェーダのレンダリング結果にあわせてディスプレイの明るくすると、普段使いには明るくなりすぎて目に優しくないし、都度ディスプレイの明るさを調整するのは面倒でやりたくない。 CRT のような結果が得られない訳ではなかったが、光量の少なさからくる不便さの方が印象に強く残った。
そこで、元の画素と周囲の画素の明るさに合わせて、例えば R の列であれば (R,G,B)=(255,0,0) とするのではなく (R,G,B)=(255,64,64) のように G、B の値も幾らか増やす処理を追加した。
元の画素以外に周囲の画素も参照するのは明るい画素を少し膨張したような見た目にした方がそれっぽくなりそうだと思ったためである。
また、この変更により明るい部分が白っぽくなるので、最後にコントラストを調整する処理も追加した。
増やした明るさがコントラスト調整処理である程度失われるので、そのような意味では無駄があるが、処理の流れが分かりやすく、色合いも調整しやすかったのでこのような実装になった。
上記の実装に基づいて描画結果とディスプレイの明度のバランスを微調整してみたが、結局画面を十分に明るくすることはできず、実用上は見栄えのために画面を明るくするか逆に周囲を暗くするなどで対処した方が良いという結論になり、以下の2倍解像度アパーチャグリルシェーダの実験に移った。
2倍アパーチャグリルシェーダ
3倍シェーダの出力は 1920×1200 になるが、この世にそこそこ普及していると思われる 1920×1080 のフル HD 解像度で画面全体を表示できないのがちょっと気になった。 そこで、1920×1200 未満かつ 1280×800 以上の解像度の環境向けにここまでに書いたシェーダをベースとした2倍シェーダを作ってみた。 実装は、下記 (a) のように描画結果の各列が R、G、B 何れかを担当するという点は同じまま、縦 1px を 2px に増やすようにしたものである。
R G B R G B R G B R G B
+--+--+--+--+--+--+ +--+--+--+--+--+--+
+--+--+--+ | | | | | | | +--+--+--+ |黒|黒|青|赤|黒|黒|
| | | | → +--+--+--+--+--+--+ |黒|白|黒| → +--+--+--+--+--+--+
+--+--+--+ | | | | | | | +--+--+--+ |黒|黒|青|赤|黒|黒|
A B C +--+--+--+--+--+--+ +--+--+--+--+--+--+
A A B B C C 黒 紫 黒
(a) 2倍シェーダ概要 (b) 2倍シェーダの上手くいかない例
CG を表示する場合はこの実装で悪くない雰囲気の結果が得られた。 しかし、黒背景に縦 1px 幅の白線が多出するテキスト画面では (b) のように、白が RGB のうちの2色で描画されるようになり見た目の色が白にならないという問題が生じた。
これを受けて、ある2列が描画できない色を、その2列に 1/2 ずつ持たせる実装も試してみたが、結果はあまり芳しくなく不採用となった。
この方法では、例えば、(b) の真ん中の B、R の列であれば、元の画素は白で (R,G,B)=(255,255,255) であるので、G 成分を1/2ずつ割り振って B 列が (R,G,B)=(0,127,255)、R 列が (R,G,B)=(255,127,0) となる。
このようにすると、元の色が一様な箇所での色合いの変化が |赤紫|青緑|薄青|橙|黄緑|青紫| の6列単位となってしまい、あまり CRT らしい見た目にはならなかった。
描画結果の各列に RGB の何れかを担当させる 3n 倍シェーダをベースとしている以上、2倍した時に RGB どれかが欠けてしまう問題からは逃れられず、現状2倍シェーダはおまけのような扱いである。
周囲の画素の色合いの一様度合いを計算しながら、拡大後の画素の色味の調整を行うようにするとマシになるのかもしれないが、現状試していない。
アパーチャグリルシェーダの結果
画面全体のスクリーンショットは画像が大きくなってしまうため、一部を切り出したものだが、作成した CRT シェーダによる描画結果のスクリーンショットは以下の通り。
6倍シェーダのスクショについては、他のスクショとの比較ができること優先でこのページの表示が崩れること承知で貼った。
CRT っぽい描画結果の集合のどこかに入ってるくらいにはなったんじゃないかと思う。
※6倍シェーダの結果は画素ピッチの狭い環境で閲覧した方が良いかもしれません。
※6倍シェーダの色合いが他とちょっと違う理由は末尾の「おわりに」に書きました。
シェーダの実装で「周りの画素の明るさを考慮する処理を入れた」旨を書いたが、これをやりすぎると CRT のフォーカスが甘いような状態に近づくことになる。 確かにテキスト画面の描画例を見るとちょっと甘いかもしれない。 配布物の中にシェーダのソースコードも入っていますので気になる人は適宜調整していただけると良いかと思います。
テキスト画面の描画例
FreeDOS(98) 起動直後の画面。 2倍シェーダで色が変化する現象が見られる。
↑2倍シェーダ
↑3倍シェーダ
↑6倍シェーダ
グラフィック画面の描画例(16色)
宇宙快盗ファニー Bee のジュノー船内。
↑2倍シェーダ
↑3倍シェーダ
↑6倍シェーダ
グラフィック画面の描画例(256色)
夢幻泡影のアリスの館。
↑2倍シェーダ
↑3倍シェーダ
↑6倍シェーダ
延長戦:6倍シャドーマスクシェーダ
アパーチャグリルのシェーダがそこそこ上手くいったように思えたので、解像度6倍の WQUXGA 出力ならばシャドーマスクシェーダも作れるんじゃないのか?と魔が差してやってみた。 「出力画面上に存在するシャドーマスクの画素の位置を計算して点描をするくらい難しい事じゃないぜ」と高を括って実装してみたところ、待っていたのはモアレとの闘いだった。 最終的になんとか着地させられないではなかったが、より良い描画結果のためには体感最低でも 8K は欲しい。
モアレが出てきてしまうのは解像度が足りず以下のような描画結果となるためである。
- 結果を U13NA で表示した時にドットピッチが 0.25~0.45mm になるのを想定して実装すると、WQUXGA でも、そこに描画されるシャドーマスクの画素を構成する LCD の画素数は1~5個にしかならなかった。
- LCD の正方画素上にシャドーマスクの画素をマッピングすると、上記画素グループの形状変化が反復してしまいモアレとなって見えてくる。
シェーダによる描画結果のシャドーマスクの画素は円に見えて欲しいが、1~5個の LCD 画素が集まったグループがなす図形は円には見えず、やや粗い印象の見え方となる。 ドットピッチを大きくすれば、各 LCD 画素グループに含まれる画素数を増やすことができるので、シャドーマスクの画素はだんだんと円に近い形状になるが、ドットピッチが大きくなっているので結局粗い見え方になる。
また、シャドーマスクの1画素にあたる LCD の画素グループ内の画素数は、シャドーマスクの画素の位置によって±1~2くらいの差がある。 そのため、例えば、全体が真っ白である画面をこのシェーダで描画してLCD の画素グループ1組に含まれる画素数が3~5個になったとすると、3個の LCD 画素で表現されるシャドーマスク画素は暗く見え、5個の LCD 画素で表現されるシャドーマスク画素は明るく見えることとなる。 結果、画面の各部の色合いが RGB の何れかに傾いてしまう。 この現象に対処するため、各シャドーマスク画素の明るさが一様な見た目に近づくよう、含まれる画素数に応じて各画素グループの明るさを調整する必要があった。
以上のような状況から、シャドーマスクの1画素を構成する LCD の画素数が十分多くなり、円もどきの図形の反復でなく円の反復した整列に見える程度に出力解像度が大きくないと手放しで良いと思えるシャドーマスクシェーダの出力は得られなさそうである。
取り敢えず、あまり高品質な出力とはならないことは承知で調整を行い、今回の6倍シェーダでは一応以下のような実装となった。 モアレは幾らか軽減させられたが完全になくすことはできなかった。
- ドットピッチは 0.43mm(PC-KD552 と同じ4)とし、シャドーマスクの1画素を構成する LCD の画素グループの個数は最小でも4個になるようにした。
- 画素数の少ない LCD 画素グループがあると明るさの調整が難しくなるため。
- シャドーマスクの画素の直径は最寄りの画素との距離(充填される正三角形の辺の長さ)の8割とした。定量的な根拠はなく、視覚の印象で調整した結果である。
- シャドーマスクの画素範囲外となっている LCD 画素は常に黒で描画していたが、それをやめて周囲に合わせて少し明るくなるようにした。
- コントラストやシャープネスよりもモアレの軽減を優先したため。
この6倍シャドーマスクシェーダの描画結果のスクリーンショットは以下の通り。 ある程度離れれば見れなくもない……だろうか。
前述の通りシャドーマスクシェーダは高解像度な画面に点描を行うシェーダであるので、入力解像度に対する出力解像度の拡大率についての制約はあまりなく、拡大率が大きいほど綺麗に描写できる(筈)のシェーダと言える。 よって、左右端に黒帯を入れてピラーボックスにした描画結果を 16:9 の 8K(7680×4320)ディスプレイに表示するような方法でより高品質な結果が得られるかもしれない。
ちなみに、ざっと調べたところ、どうやら 640×400 の 9倍(5760×3600)及び 12倍(7680×4800)の PC 用ディスプレイは殆どないようで、もしも9倍か12倍のアパーチャグリルシェーダを作ろうとしたら、出力解像度よりも大きな解像度のディスプレイに額縁放送状態に描画するしかないのではないかと思っている。
できたもの
今回の作業でできたもののダウンロードはこちら → np21w_rev94_crt_shader.zip からどうぞ。 諸々の説明は同梱の readme に書いてあります。 元の機能を削ってしまった上に、操作によっては明らかにウィンドウがおかしくなってその後操作不能となったりすることもあります。 ご使用は各自の責任にてお願いします。
また、3倍アパーチャグリルシェーダと6倍アパーチャグリルシェーダはほぼ同時に実装を進めており、周囲の明るさに基づいた調整処理は3倍シェーダで実験していたのですが、これを6倍シェーダに移植し忘れていたことが判明したため、配布物のシェーダでは6倍シェーダが一部処理を持っていません。 上記、描画例で6倍シェーダの色合いがちょっと違うのは多分これが原因です。 やる気が出たら直します。
おわりに
np21w に機能追加して自作 CRT シェーダを動かすという、最初のアイデアが無事実現できたのは良かった。 一方、元の機能を削ってしまったところがあったり、シャドーマスクシェーダが正直微妙な出来にしかならなかったりと心残りもある。 とはいえど、動いていない訳ではなく、α版かβ版のようなものとすればある程度区切りが良いかと思ったので公開することにした。 今後何か更新があれば投稿するかも。
余談:LCD の発売時期と価格とか
PC-98 及び互換機の初期のカラー LCD 搭載機種は1989年代末~1990年代初頭に発売されている(ただし8色の機種もある)。
- 1989:NEC PC-9801LX5C(8色)
- 1990:NEC PC-9801T model S5/F5(8色), EPSON PC-386LSC(16色)
- 1991:NEC PC-9801NC(16色)
- 1992:EPSON PC-386NOTE ARC(16色)
PC 全体の出荷実績としては JEITA の統計(Internet Archive) があり、全体に占めるノート型 PC の割合は、1996年度から 35.7%、44.6%、47.9%、48.6% と増加し、2000年度に 52.1% で過半数を超える。
PC Watch の1997年頃の PC 向け LCD ディスプレイの発売記事では、188000円で低価格と記載があるくらいでまだまだ高価だったようである。
- 1997/03:ナナオ E141L 13.8型 XGA 26万色 TFT 398000円
- 1997/10:I-O DATA LCD-A14T 13.8型 XGA 26万色 TFT 298000円
- 1997/10:富士通 VL-1500T/1410SS 15/13.8型 XGA TFT/DSTN 358000/228000円
- 1997/11:飯山 TXA3601GT 14.1型 XGA 16万色 TFT 188000円(低価格との記載あり)
単体の商品の発売記事を目当てに web を跋渉するのは大変なので PC Watch の「ディスプレイ相場情報」を追っかけてみた。 1998年06月の調査 から LCD が対象に含まれるようになっている。 価格の推移では、2001年頃に LCD の価格が1998年~1999年頃の CRT くらいになったようである。 また、IDC Japan の市場調査 では2005年の国内の PC ディスプレイ市場のうち98.9%が LCD ディスプレイとなったとのこと。
- 1998/06 相場情報:130000円~210000円程度 (CRT は 59800円~178000円程度)
- LCD が最初に調査対象になった回(表にはないが特価85000円程度のものもあり)。
- 「液晶ディスプレイは、最近値下がりが激しく、それに伴って売れ行きも伸びているといわれている」とある。
- 1999/01 相場情報:86200円~149799円程度 (CRT は 43800円~148000円程度)
- 2000/01 相場情報:94400円~140000円程度
- 2000/07 相場情報:88800円~148000円程度
- 2001/01 相場情報:79800円~147000円程度
- 2001/07 相場情報:54800円~158000円程度
- 2002/01 相場情報:39800円~169800円程度