ActionScript 3.0で始めるオブジェクト指向スクリプティング

第45回領域をビットマップで塗る - テクスチャマッピング

今回から、定められた領域をビットマップで塗りつぶすお題に取りかかる。目指すは、3次元空間から2次元平面に透視投影された面に、素材となるビットマップを貼りつけることだ。素材を「テクスチャ」と呼び、貼りつけは「マッピング」という。この「テクスチャマッピング」を学ぼう。

ビットマップで塗る

まず、3次元空間は今回置いておく。ビットマップで、決まった領域を塗ってみたい。使うのは、Graphicsクラスのメソッドだ。第33回遠近法の投影「ステージにランダムなシェイプを配置する」で、Shapeインスタンスに塗りの円を描いた。このとき塗り色を定めたのは、Graphics.beginFill()メソッドだった。このメソッドをGraphics.beginBitmapFill()と書替えれば、塗りにビットマップが使える。必須の第1引数には、塗りに用いるBitmapDataオブジェクトを渡す[1]⁠。

Graphicsオブジェクト.beginBitmapFill(BitmapDataオブジェクト)

塗りのビットマップは[ライブラリ]に置いて、インスタンスは動的につくろう。そのためには、そのビットマップに[クラス]を設定しておかねばならない(詳しい手順については、第34回3次元空間における回転「ビットマップのインスタンスを動的に配置する」参照)。クラス名は「Image」としておく図1⁠。

図1 塗りで使う[ライブラリ]のビットマップにクラス名「Image」を設定
図1 塗りで使う[ライブラリ]のビットマップにクラス名「Image」を設定

タイムラインには予め何も置かなくてよい。つぎのスクリプト1をフレームアクションとして書くだけだ。

スクリプト1 Shapeインスタンスに描いた円をビットマップで塗る
// フレームアクション
var nSize:Number = stage.stageHeight / 2;
var myShape:Shape = new Shape();
var myGraphics:Graphics = myShape.graphics;
var myTexture:BitmapData = new Image();
myGraphics.beginBitmapFill(myTexture);
myGraphics.drawCircle(nSize, nSize, nSize);
myGraphics.endFill();
addChild(myShape);

タイムラインに動的にShapeインスタンス(myShape)を置いて、そのShape.graphicsプロパティから得たGraphicsオブジェクト(myGraphics)Graphics.drawCircle()メソッドで円を描いた(前掲第33回「遠近法の投影」参照⁠⁠。そのとき、塗りはGraphics.beginBitmapFill()メソッドで、[ライブラリ]のビットマップのインスタンス(myTexture)を指定している[2]⁠。すると、円の内側はビットマップでタイル状に塗られる図2⁠。

図2 Shapeインスタンスに描いた円がビットマップでタイル状に塗られた
図2 Shapeインスタンスに描いた円がビットマップでタイル状に塗られた

三角形に分けて描く - Graphics.drawTriangles()メソッド

描画領域をいくつもの三角形に分けて描くのが、Graphics.drawTriangles()メソッドだ。三次元空間の曲面も、複数の細かな三角形に分ければ、平面の三角形の集まりで表現できる。某ビールメーカーの特徴的な酎ハイの缶を思い浮かべてもらうとよい図3⁠。必須の第1引数はNumberベース型のVectorオブジェクトで、3角形の頂点座標をxyの順に交互に3つずつ加えていく。

図3 小さな三角形で形づくられた缶の曲面
図3 小さな三角形で形づくられた缶の曲面

Graphics.drawTriangles()メソッドにより、ふたつの三角形をビットマップで塗ってみよう。ふたつの三角形それぞれの3頂点、都合6つの座標は下図4のように定める。

図4 Graphics.drawTriangles()メソッドで描くふたつの三角形
図4 Graphics.drawTriangles()メソッドで描くふたつの三角形

描くのが仮に三角形1ひとつだけだったら、引数となるNumberベース型のVectorオブジェクトをつぎのようにつくって、Graphics.drawTriangles()メソッドに渡しても構わないかもしれない。

var vertices:Vector.<Number> =
  new <Number>[20, 20, 120, 20, 20, 120];   // 三角形1の3頂点座標
myGraphics.drawTriangles(vertices);

しかし、Graphics.drawTriangles()メソッドをテクスチャマッピングに用いると、面を数多くの三角形に分けて塗ることが少なくない。上記のように座標をただ書連ねると、三角形がいくつあるのか、どれがx座標でどれがy座標なのか訳がわからなくなる。筆者としては、VectorオブジェクトにVector.push()メソッドを用いて、頂点ひとつずつ、つまりxy座標ひと組ごとに加えることをお勧めしたい。

Graphics.drawTriangles()メソッドでふたつの三角形を描き、ビットマップで塗ったのが、つぎのスクリプト2だ。

スクリプト2 Graphics.drawTriangles()メソッドによりふたつの三角形をビットマップで塗る
// フレームアクション
var myShape:Shape = new Shape();
var myGraphics:Graphics = myShape.graphics;
var myTexture:BitmapData = new Image();
var vertices:Vector.<Number> = new Vector.<Number>();
// 三角形1の3頂点座標
vertices.push(20, 20);
vertices.push(120, 20);
vertices.push(20, 120);
// 三角形2の3頂点座標
vertices.push(140, 40);
vertices.push(140, 140);
vertices.push(40, 140);
myGraphics.beginBitmapFill(myTexture);
myGraphics.drawTriangles(vertices);
myGraphics.endFill();
addChild(myShape);

[ムービープレビュー]を確かめると、ふたつの三角形が描かれて、その内側はビットマップでタイル状に塗られる図5上図⁠。Graphics.beginBitmapFill()メソッドは、デフォルトでは対象となるShapeオブジェクトの基準点、つまりスクリプト2ではステージ左上隅からビットマップを塗り始める[3]⁠。そのため、三角形に塗ったビットマップの左上が少し切れてしまった図5下図⁠。

図5 ふたつの三角形がビットマップでタイル状に塗られた
画像 画像

ビットマップを三角形の頂点に対応させる

Graphics.drawTriangles()メソッドが優れているのは、三角形ひとつひとつについてビットマップのどの部分で塗るかが細かく決められることだ。塗りのビットマップも、やはり三角形の頂点座標で領域を定める。Graphics.drawTriangles()メソッドには、その指定を第1引数と同じように、Numberベース型のVectorオブジェクトで渡す。ただし、注意すべきことがふたつある。

第1に、このVectorオブジェクトはGraphics.drawTriangles()メソッドの第3引数になる。第2引数を省く訳にはいかないので、とくに指定がないときはデフォルト値のnullを渡す。第2は、ビットマップの位置を決める座標の単位がピクセルではないことだ。ビットマップの左上は(0, 0)、右下が(1, 1)となる比率で示す。この水平軸をu、垂直軸をvで表すので、uv座標と呼ばれる図6⁠。

図6 ビットマップの領域は(0, 0)から(1, 1)までのuv座標で示す
図6 ビットマップの領域は(0, 0)から(1, 1)までのuv座標で示す

この第3引数として渡すVectorオブジェクトの数値(Numberベース型)エレメントは、第1引数のVectorオブジェクトのxy座標値(Numberベース型)のエレメントにそれぞれ対応させる。つまり、ふたつのオブジェクトのエレメント数Vector.lengthプロパティ値)は、等しくなければならない。第1引数のxy座標に対して、第3引数のuv座標は下表1のように決めよう。

表1 第1引数の頂点座標とそれに対応する第3引数のuv座標
第1引数/頂点座標第3引数/uv座標
(20, 20)(0, 0)
(120, 20)(1, 0)
(20, 120)(0, 1)
(140, 40)(1, 0)
(140, 140(1, 1)
(40, 140)(0, 1)

前掲スクリプト2のGraphics.drawTriangles()メソッド呼出しに、第3引数のuv座標(uvData)を加えたのがつぎのスクリプト3だ。メソッドに渡す第2引数のnullを忘れないでほしい。

スクリプト3 ふたつの三角形の頂点にビットマップを対応させて塗る
// フレームアクション
var myShape:Shape = new Shape();
var myGraphics:Graphics = myShape.graphics;
var myTexture:BitmapData = new Image();
var vertices:Vector.<Number> = new Vector.<Number>();
var uvData:Vector.<Number> = new Vector.<Number>();
// 三角形の頂点のxy座標(第1引数)
vertices.push(20, 20);
vertices.push(120, 20);
vertices.push(20, 120);
vertices.push(140, 40);
vertices.push(140, 140);
vertices.push(40, 140);
// ビットマップの対応させるuv座標(第3引数)
uvData.push(0, 0);
uvData.push(1, 0);
uvData.push(0, 1);
uvData.push(1, 0);
uvData.push(1, 1);
uvData.push(0, 1);
myGraphics.beginBitmapFill(myTexture);
myGraphics.drawTriangles(vertices, null, uvData);
myGraphics.endFill();
addChild(myShape);

[ムービープレビュー]を確かめると、ビットマップは第3引数に指定したとおりふたつの三角形を塗る。結果として、四角いビットマップが対角線でふたつに切られたように描かれる図7⁠。

図7 四角いビットマップが対角線で切られたように描かれる
図7 四角いビットマップが対角線で切られたように描かれる

ふたつの引数となるVectorオブジェクト(Numberベース型)の座標は、好きなように決められる。たとえば、前掲スクリプト3でGraphics.drawTriangles()メソッドに渡した第1引数(vertices)の座標値を書替えれば、ビットマップが変形できる図8⁠。

図8 Graphics.drawTriangles()メソッドに渡す引数の座標値を変えるとビットマップが変形できる
画像 画像

Graphics.drawTriangles()メソッドで面を三角形に分けて、ビットマップのuv座標をうまく定めれば、ビットマップをあたかも伸縮自在のラッピングペーパーのように使ってマッピングできる。次回は、メソッドの第2引数の説明に加えて、テクスチャマッピングの練習をしてみたい。

今回解説した次のサンプルファイルがダウンロードできます。

おすすめ記事

記事・ニュース一覧