自前3Dコードはcanvasに描画しているが、これはプリミティブを描画するメソッドを使用しているのではない。
canvasにはビットマップ・メモリを読み書きする仕組みがあり、それを使ってJSからピクセル単位で書き込みを行っている。
canvasの2dコンテキストにはgetImageData()
メソッドがあり、このメソッドを使用すると現在のcanvasのビットマップの状態をコピーしたImageDataオブジェクトが得られる。またcreateImageData()
で新規に作成することができる。このImageData
オブジェクトはwidth
,height
,data
の3つのプロパティを持っている。
width
,height
はcanvasビットマップの幅と高さである。重要なのはdata
プロパティである。
data
プロパティはUint8ClampedArray型の配列である。この配列は4つの要素(r,g,b,a)で1ピクセルを表す、一次元配列となっている。また1つの要素は符号なし1バイトである。
data
配列を操作することで、ピクセルごとの処理をJSから行うのである。
ただImageData
はビットマップのコピーなので、data
配列をいじった結果をcanvasに反映させるにはputImageData()
で書き込む必要がある。
かなり前(数年以上前だと思う)にこの仕組みを使っていくつかプリミティブを描画するコードを書いたみた。そのリンクを貼っておこう。
ソースを見るとES5/jquery使っているのが古さを感じさせるねぇ。。
canvasをバックバッファとして使用する
今回の3Dデモは、サイズの小さなcanvasビットマップを拡大・縮小してブラウザ・ウィンドウにフィットさせるために、JSから書き込むcanvasビットマップを非表示にしてバックバッファとして、それをdrawImageで拡大・縮小して表示するなんていうこともやっている。仕組みを図にしたのが以下である。
描画はImageDataに対して行い、それを非表示のcanvas(id : buffer)にputImageData()
で書き込んでいる。このcanvas画面は解像度固定となっている。画面ピクセル数が多いとソフトウェア描画では処理がきついと想定し、画面ピクセル数を幅256px程度に抑えている。縦ピクセルのサイズはアスペクト比が4:3になるように計算して決定している。
画面表示用のcanvasはウィンドウサイズにフィットするようになっており、先ほどの固定サイズのcanvasビットマップをアスペクト比を維持しながら、拡大・縮小して表示する。
ImageData
を拡大・縮小しながら直接画面フィット用のcanvasに書き込んでもいいんだけど、さすがにそれだと処理がきつすぎるかなあと思っている。