プチコン4の高速なグラデーション付き三角形描画
プチコンで3Dをやろうとしたときに,速度的に一番ネックになりそうなのが描画部分
プチコン4では三角形にテクスチャを貼ったりグラデーションをかけたりするような機能は用意されていないので,何とかして自力で描画する必要があるが……
愚直にやると1つずつ点を打つ必要があり,どう考えても遅い
大体の実装ではフラットシェーディング/単色ポリゴンで妥協している
1つの三角形は,適当に回転,(XY軸)拡縮,せん断をすることで任意の三角形と合同にできる(はず)
プチコンのスプライトは回転と拡縮ができるので,せん断した三角形を用意して,適当なものを変形すれば(現実的に可能な)有限のパターンで任意三角形を表現できる
これらのパターンについてグラデーションを事前計算しておけば描画におけるボトルネックがなくなる(!)
具体的な実装としては
1. 適当な大きさの正方形(今回は64x64で説明)のマスに収まる三角形を,#FF000000で描く
$ 0 \leq n \leq 31のnについて(0,0) (0,63) (63, n)を頂点とする三角形
2. 各頂点のアルファ値を255とし,他の頂点でアルファ値が0になるようなグラデーションの付いた三角形をそれぞれの三角形の下に描く
ここまでが事前準備
3. 描きたい三角形について,事前計算した三角形の中から対応するものを選び,適当な場所にグラデーションも含めてコピー
4. 各頂点からのグラデーションに,対応する頂点色を加算合成でGFILL
5. グラデーションを元の三角形に加算合成でGCOPY
これで各頂点色がグラデーションでかかった三角形ができる
ここまでのイメージ
https://tweet-card.now.sh/1233031726617640962.jpg?lang=ja https://twitter.com/t0suk3/status/1233031726617640962
6. 完成した三角形をスプライトに登録して,適当に回転拡縮して欲しい領域に合わせる
ここまでのイメージ
https://tweet-card.now.sh/1233090150923526145.jpg?lang=ja https://twitter.com/t0suk3/status/1233090150923526145
グラデーションができればテクスチャマッピングもできる
頂点色のG,B成分にそれぞれV, U座標を格納して,描画後の三角形の色情報からA,R成分を抜いてやると,下位8ビットにU座標,上位8ビットにV座標が格納された配列を作ることができる
これをインデックスに見立てて,横幅256ピクセルの画像をGSAVEした配列をカラー情報に見立ててGLOADしてやると,テクスチャマッピングができる
「三角形ではない」領域を抜くため,テクスチャの(0,0)は透明色にする
ここまでのイメージ
https://tweet-card.now.sh/1233098212245819392.jpg?lang=ja https://twitter.com/t0suk3/status/1233098212245819392
ここで,R成分が余っているので,ここに輝度情報を入れておくことで簡易的なライティングもできる
イメージ
https://tweet-card.now.sh/1233111833327763456.jpg?lang=ja https://twitter.com/t0suk3/status/1233111833327763456
利点
描画時にピクセル単位のループを伴わない
全てGCOPY, GFILL, GSAVE, GLOAD, ARYOPで処理できる
実質ネイティブコードで動くと言える
複数のポリゴンについて同時に処理できるフェーズが存在する
グラデーションをGCOPYで合成するフェーズや,テクスチャマッピングのフェーズ
64x64では同時に処理することによる利点は見られなかったが,32x32や16x16のようなより小さいパターンを使って大量のポリゴンを描画する場合にはBASIC側のオーバーヘッドを減らせるので利点があるかもしれない
課題
エイリアスがひどい
小さい三角形を拡大してるのだから当然と言えば当然
描きたい三角形の大きさに合わせてパターンの大きさを変えると少しマシになる
アンチエイリアスの方法を考えたい
テクスチャフィルタリングがNearest Neighbor
仕方ない
ミップマップを用意するのが吉
3Dポリゴンを処理するとき,テクスチャマッピングがパースペクティブコレクトでない
1/zをR成分に持たせるなどして解決できるような気がするけど,値域が狭い(0~255)のでうまく圧縮できないとつらいことになる
テクスチャの歪みを諦めるのが一番楽
気になるほど大きいならLAYERを利用するなり、ポリゴンを分割して対応