Away3D TypeScriptではじめる3次元表現

第2回 物体にテクスチャを貼って回す

この記事を読むのに必要な時間:およそ 6 分

前回の第1回Away3D TypeScriptで基本的な3次元の形状をつくるは,基本的なかたちとして球体をつくって3次元空間に置いた。もっとも,表面の素材(マテリアル)はデフォルトのままで動きもしないため,3次元表現としては冴えない(再掲第1回サンプル2)。そこで,今回は素材にテクスチャを貼って,アニメーションで回してみよう。

第1回サンプル2 Away3D 14/08/26: Creating a sphere in the 3D space(再掲)

3次元空間でボールを回す

まずは,球体に貼るテクスチャのビットマップが要る。これは,Away 3D TypeScriptサイトの作例GitHub: StageGL Examplesにある素材を使わせてもらおう。beachball_diffuse.jpgがビーチボールのようなテクスチャだ図1)。といっても,ご覧のとおり球体の展開図などつくらなくてよい。矩形のビットマップをAway3Dが伸び縮みさせて球体に貼りつけてくれる。JPEG形式で大きさは512×256ピクセルだ。3次元のCGではテクスチャの1辺を2の累乗にするのがお約束になっている。

図1 ビーチボールのテクスチャ

図1 ビーチボールのテクスチャ

外部ファイルを読込んで使うときには,ロード待ちしなければならない。そこで,LoaderEvent.RESOURCE_COMPLETEイベントに静的メソッドAssetLibrary.addEventListener()でリスナー関数を加えて,読込み終えたときの処理を定める。そして,ロードを始めるのが静的メソッドAssetLibrary.load()だ。引数はURLRequestオブジェクトで,コンストラクタには読込むURLを文字列で渡す。

away.library.AssetLibrary.addEventListener(away.events.LoaderEvent.RESOURCE_COMPLETE, リスナー関数)

away.library.AssetLibrary.load(new away.net.URLRequest(URL))

それでは,第1回コード13次元空間に球体をひとつ置くに手を加えていこう。素材用のフォルダ(assets)を設けてテクスチャを納め,URLは変数(imageDiffuse)に定めた。初期設定の関数(initialize())でつくる平行光源(directionalLight)の色は,自然な白に戻す。そして,LoaderEvent.RESOURCE_COMPLETEイベントのリスナー(onResourceComplete())の追加とテクスチャの読込みを加えた。なお,AssetLibraryクラスの完全修飾名(名前空間)が長いので,予めクラスと同名のローカル変数にとっている。

さらに,素材にはテクスチャを読込んで与えるので,球体をつくる関数(createSphere())でデフォルトのテクスチャは定めなくてよい。第1回コード1を,つぎのように書替える。

var sphere;
var imageDiffuse = "assets/beachball_diffuse.jpg";
function initialize() {
  var directionalLight = createDirectionalLight(0.25, 0xFFFFFF);    // 0x00FFFF);
  var AssetLibrary = away.library.AssetLibrary;
  AssetLibrary.addEventListener(away.events.LoaderEvent.RESOURCE_COMPLETE, onResourceComplete);
  AssetLibrary.load(new away.net.URLRequest(imageDiffuse));

}

function createSphere(radius, segmentsH, segmentsV, light) {
  // var defaultTexture = away.materials.DefaultMaterialManager.getDefaultTexture();
  // var material = new away.materials.TriangleMethodMaterial(defaultTexture);
  var material = new away.materials.TriangleMethodMaterial();
  var sphere = new away.prefabs.PrimitiveSpherePrefab(radius, segmentsH, segmentsV)
  .getNewObject();
  sphere.material = material;

}

つぎに,LoaderEvent.RESOURCE_COMPLETEイベントのリスナー関数(onResourceComplete())だ。引数に受取るイベントオブジェクトのLoaderEvent.assetsプロパティから素材の配列が得られる。今回読込んでいるテクスチャはひとつしかないので,インデックス0の素材をMaterialBase.textureプロパティに与えれば表面素材が定まる。

function onResourceComplete(eventObject) {
  var assets = eventObject.assets;
  var material = sphere.material;
  material.texture = assets[0];
  view.render();
}

ところが,このリスナー関数ではテクスチャが貼られなかったりする図2)。テクスチャがキャッシュされていない場合,LoaderEvent.RESOURCE_COMPLETEイベントのリスナー関数(onResourceComplete())が呼び出されてLoaderEvent.assetsプロパティの参照がとれても,その配列は空のときがある※1)。しかも,たちの悪いことに,その後はもうイベントが起こらない。したがって,配列が空のときは改めてイベントリスナーを定め,テクスチャも読込み直さなければならない。その修正を加えたのが,以下の関数だ。

図2 テクスチャが貼り損なわれた球体

図2 テクスチャが貼り損なわれた球体

function onResourceComplete(eventObject) {
  var assets = eventObject.assets;
  if (assets.length > 0) {
    var material = sphere.material;
    material.texture = assets[0];
    view.render();
  } else {
    var AssetLibrary = away.library.AssetLibrary;
    var RESOURCE_COMPLETE = away.events.LoaderEvent.RESOURCE_COMPLETE;
    AssetLibrary.removeEventListener(RESOURCE_COMPLETE, onResourceComplete);
    AssetLibrary.addEventListener(RESOURCE_COMPLETE, onResourceComplete);
    AssetLibrary.load(new away.net.URLRequest(eventObject.url));
  }
}

LoaderEvent.RESOURCE_COMPLETEイベントのリスナー関数(onResourceComplete())が呼び出されたら,LoaderEvent.assetsプロパティの配列の長さを調べる。そして,エレメントの素材があることを確かめて,MaterialBase.textureプロパティに与える。配列が空のときはイベントリスナーは定め直して,改めてAssetLibrary.load()メソッドで素材ファイルを読み込んだ。なお,そのURLはLoaderEvent.urlプロパティで得られる。これでようやく球体にビーチボールのテクスチャが貼られた図3)。ここまでをコード1として以下にまとめておこう。

図3 球体にビーチボールのテクスチャが貼られた

図3 球体にビーチボールのテクスチャが貼られた

コード1 3次元空間に置いた球体にテクスチャを貼る

var view;
var sphere;
var imageDiffuse = "assets/beachball_diffuse.jpg";
function initialize() {
  var directionalLight = createDirectionalLight(0.25, 0xFFFFFF);
  var AssetLibrary = away.library.AssetLibrary;
  view = createView(240, 180, 0x0);
  sphere = createSphere(300, 32, 24, directionalLight);
  view.scene.addChild(sphere);
  AssetLibrary.addEventListener(away.events.LoaderEvent.RESOURCE_COMPLETE, onResourceComplete);
  AssetLibrary.load(new away.net.URLRequest(imageDiffuse));
  view.render();
  view.render();
}
function createView(width, height, backgroundColor) {
  var defaultRenderer = new away.render.DefaultRenderer();
  var view = new away.containers.View(defaultRenderer);
  view.width = width;
  view.height = height;
  view.backgroundColor = backgroundColor;
  return view;
}
function createSphere(radius, segmentsH, segmentsV, light) {
  var material = new away.materials.TriangleMethodMaterial();
  var sphere = new away.prefabs.PrimitiveSpherePrefab(radius, segmentsH, segmentsV)
  .getNewObject();
  sphere.material = material;
  material.lightPicker = new away.materials.StaticLightPicker([light]);
  return sphere;
}
function createDirectionalLight(ambient, color) {
  var light = new away.entities.DirectionalLight();
  light.ambient = ambient;
  light.color = color;
  return light;
}
function onResourceComplete(eventObject) {
  var assets = eventObject.assets;
  if (assets.length > 0) {
    var material = sphere.material;
    material.texture = assets[0];
    view.render();
  } else {
    var AssetLibrary = away.library.AssetLibrary;
    var RESOURCE_COMPLETE = away.events.LoaderEvent.RESOURCE_COMPLETE;
    AssetLibrary.removeEventListener(RESOURCE_COMPLETE, onResourceComplete);
    AssetLibrary.addEventListener(RESOURCE_COMPLETE, onResourceComplete);
    AssetLibrary.load(new away.net.URLRequest(eventObject.url));
  }
}
※1
詳しくはAway3D 14/08/18: LoaderEvent.RESOURCE_COMPLETEイベントで素材ファイルが読込めないを参照してほしい。なお,この問題についてAway3D Forumに問合せてみたところ回答は得られていない(「On the LoaderEvent.RESOURCE_COMPLETE event asset files are not loaded」)。

著者プロフィール

野中文雄(のなかふみお)

ソフトウェアトレーナー,テクニカルライター,オーサリングエンジニア。上智大学法学部卒,慶応義塾大学大学院経営管理研究科修士課程修了(MBA)。独立系パソコン販売会社で,総務・人事,企画,外資系企業担当営業などに携わる。その後,マルチメディアコンテンツ制作会社に転職。ソフトウェアトレーニング,コンテンツ制作などの業務を担当する。2001年11月に独立。Web制作者に向けた情報発信プロジェクトF-siteにも参加する。株式会社ロクナナ取締役(非常勤)。

URLhttp://www.FumioNonaka.com/

著書

コメント

コメントの記入