Away3D Basics 5 – Primitives(Part 2) 本 ド キ ュ メ ン ト は 、 http

Away3D Basics 5 – Primitives(Part 2)
本
ド
キ
ュ
メ
ン
ト
は
、
http://www.flashmagazine.com/Tutorials/detail/away3d_basics_5_­_primitives_part_2/で
公開されている「Away3D Basics 5 – Primitives(Part 2)」を、ヒム・カンパニー 永井勝則が自主的
に日本語に訳したものです。
http://www.himco.jp/
[email protected]
(2009/11)
原文は、
http://www.flashmagazine.com/Tutorials/detail/away3d_basics_5_­_primitives_part_2/
で読むことが出来ます。
Away3D の基本5: プリミティブ(パート2)
本チュートリアルでも、Away3D で使用可能な、単純でしかも非常に有用なプリミティブの探求をつづけて
いきます。前のチュートリアルと同様に、プリミティブの使用方法を基本を超えたコードで示すサンプルもあり
ます。たとえば簡単なパノラマビューワを作成する方法を示すサンプルでは Sphere や Skybox プリミティブ
と、ホバーカメラを組み合わせることでこれを構築する方法を学びます。またパノラマを作成するときに役立
つクロスプラットフォームのソフトウェアについても見ていきます。
本チュートリアルは、2つの基本的なプリミティブから始め、パノラマビューワの作成に使用できる複雑なプリミ
ティブに進みます。
必要とされる予備知識
本チュートリアルはわれわれのAway3D チュートリアル上に構築されています。Flash 3D が初めての方は
本チュートリアル以前のチュートリアルに一通り目を通されたほうがよいでしょう。各サンプルには完全なソー
スファイルをつけています。付随する ActionScript ファイルへのリンクをクリックするとそのサンプルのクラスファ
イルがダウンロードできます。また多くのサンプルでは Cover.as ファイルが必要です。これは多くの Flash フ
ァイルを一度に表示しようとする際に発生する可能性のあるマシンのフリーズを避けるために使用します
(Flash 3D は多くのマシンリソースを消費するので、一度に多くの 3D を再生するとマシンが固まる恐れが
あります)。サンプルのクラスファイルの使用方法がよく分からないという方は、このチュートリアルにまず目を
通してください。
本チュートリアルのサンプルの中にはテクスチャを使うものがあります。テクスチャとマテリアルについては別のチ
ュートリアルでもっと詳しく見ていきますが、Flash CS3 や CS4 でソースファイルを作成したい場合には、こ
のチュートリアルを先に読んでください。
トライデント(Trident)
3D 空間で事物がどのように表示されるのかを明確にする必要がありますか? その場合にはトライデント
(3軸の矢印)を追加します。トライデントはシーン内で x、y、z 軸を示すだけのいたって単純なプリミティブ
です。非常に軽量で、複雑なシーンの設定のデバッグに便利です。ロードするモデルの回転をどうしようか
考えている場合には、モデルにトライデントを追加すると、その回転量が簡単に分かります。次のサンプルを
クリックしてドラッグし、トライデントによって 3D 空間に3つの球の配置が容易になることを実感してください。
ムービー:Triaxe.as
デフォルトのパラメータでトライデントを作成するには次のようにします。
var axis:Trident = new Trident();
view.scene.addChild(axis);
Trident のコンストラクタは各自の長さと、各軸の文字(x、y、z)を表示するかどうかの2つのパラメータをオ
プションで取ります。
var axis:Trident = new Trident(200,true);
このコードのよって、各軸の長さが 200 単位で、各軸の文字を表示する新しい Trident の axis が作成さ
れます。ただし Trident は一旦作成してしまうと、長さや文字の調整は行えません。これはほかの
Away3D プリミティブとは異なる点です。
Triaxe.as
package {
import away3d.cameras.HoverCamera3D;
import away3d.containers.*;
import away3d.core.base.*;
import away3d.core.math.*;
import away3d.events.*;
import away3d.materials.*;
import away3d.primitives.*;
import flash.display.Sprite;
import flash.display.Stage;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.MouseEvent;
[SWF(width="500", height="400",
frameRate="30", backgroundColor="#FFFFFF")]
public class Triaxe extends Sprite {
private var View:View3D;
private var swfStage:Stage;
private var cover:Cover;
// ホバーカメラの制御
private var camera:HoverCamera3D;
private var lastMouseX:Number;
private var lastMouseY:Number;
private var lastPanAngle:Number;
private var lastTiltAngle:Number;
private var move:Boolean = false;
public function Triaxe() {
super();
// 拡大縮小しない
swfStage = this.stage;
swfStage.scaleMode = StageScaleMode.NO_SCALE;
swfStage.align = StageAlign.TOP;
// ホバーカメラとビューを作成する
camera = new HoverCamera3D(
{zoom:2, focus:100, distance:250});
View = new View3D({camera:camera,x:250, y:200});
// ビューを追加
addChild(View);
camera.lookAt( new Number3D(0, 0, 0) );
camera.targetpanangle = camera.panangle = 45;
camera.targettiltangle = camera.tiltangle = 20;
// 3つの球を追加
var mat:ColorMaterial = new ColorMaterial(0xffff00);
var sphere1:Sphere = new Sphere(
{radius:10, material:mat, x:100,y:­150,bothsides:true});
View.scene.addChild(sphere1);
mat = new ColorMaterial(0xff00ff);
var sphere2:Sphere = new Sphere(
{radius:10, material:mat, y:200,z:150,bothsides:true});
View.scene.addChild(sphere2);
mat = new ColorMaterial(0x00ffff);
var sphere3:Sphere = new Sphere(
{radius:10, material:mat, z:100,x:­150,bothsides:true});
View.scene.addChild(sphere3);
View.render();
camera.hover();
// 軸を示す 180 は軸の長さ
var axis:Trident = new Trident(180);
View.scene.addChild(axis);
cover = new Cover(this);
addChild(cover);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
this.stage.addEventListener(
MouseEvent.MOUSE_DOWN, MouseDown);
this.stage.addEventListener(
MouseEvent.MOUSE_UP, MouseUp);
}
private function MouseDown(event:MouseEvent):void {
lastPanAngle = camera.targetpanangle;
lastTiltAngle = camera.targettiltangle;
lastMouseX = stage.mouseX;
lastMouseY = stage.mouseY;
move = true;
}
private function MouseUp(event:MouseEvent):void {
move = false;
}
private function onEnterFrame(e:Event):void {
if(!cover.visible) {
var cameraSpeed:Number = 0.3;
if (move) {
camera.targetpanangle =
cameraSpeed*(stage.mouseX ­ lastMouseX) + lastPanAngle;
camera.targettiltangle =
cameraSpeed*(stage.mouseY ­ lastMouseY) + lastTiltAngle;
}
camera.hover();
View.render();
}
}
}
}
Basic02.as Basic02_view.as
このサンプルでは、同じシーンを4つのビューで表示しています。このシーン自体は単純なものですが、トライ
デントによって、4つの各ビューでカメラの設定がどうなっているのか容易に分かります。
レギュラーポリゴン(RegularPolygon)
レギュラーポリゴンは、実際そう見えるので、“皿”(ディスク)と呼ぶこともできます。平面と同様にレギュラー
ポリゴンも奥行きを持たないので、辺の数(3 は三角形、4 は四角形、5 は五角形)や半径を設定します。
polygon = new RegularPolygon({radius:200,sides:3});
view.scene.addChild(polygon);
辺(sides)には2以上ならどんな数でも設定できます。次のムービーではこの設定の実験を行っています。
Basic08_regular_polygon.as
ポリゴンに使用するテクスチャが歪曲するときには、ポリゴンのサブディビジョン(subdivision)を多くすること
もできます。
polygon.subdivision = 3;
これによって各三角形が3つに分割されます。ダウンロードした上記のファイルで、subdivision を追加して
結果を確認してみてください。
球(Sphere)
直方体は 12 個の三角形で表示できますが、球はそれよりもかなり複雑なオブジェクトです。球を作成する
にはもっと多くの三角形が要ります。使用する三角形の数は球の視覚的な厳密性で決まります。
Away3D ではデフォルトで、小さな数が使用されます。球を丸く見せたい場合には、segmentsW と
segmentsH を設定します。
ムービー:SphereTri.as
+と­ボタンをクリックすると、球を構成する三角形の数が増減できます。球には半径が必要なので、球を作
成するときには通常、次のようなパラメータが必要です。
var sphere:Sphere = new Sphere({radius:50,segmentsW:10,segmentsH:10});
これらの値は、ほかのプリミティブと同様に、作成したオブジェクトのプロパティとして設定することもできます。
var sphere:Sphere = new Sphere();
sphere.radius = 50;
sphere.segmentsW = 10;
sphere.segmentsH = 10;
また yUp プロパティを使用すると、球の方向を変えることができます。
sphere.yUp = false;
この設定によって、球のてっぺんがこちらに向くように、球の向きが変わります。
SphereTri.as
package {
import away3d.containers.*;
import away3d.core.base.*;
import away3d.core.math.*;
import away3d.core.render.Renderer;
import away3d.events.*;
import away3d.materials.*;
import away3d.primitives.*;
import com.bit101.components.*;
import flash.display.Sprite;
import flash.display.Stage;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.utils.getTimer;
[SWF(width="500", height="400",
frameRate="60", backgroundColor="#FFFFFF")]
public class SphereTri extends Sprite {
private var View:View3D;
private var baseObject:Sphere;
private var swfStage:Stage;
private var cover:Cover;
private var planeComplexity:Number = 0;
private var label2:Label;
private var lastTime:Number;
public function SphereTri() {
super();
// 拡大縮小しない
swfStage = this.stage;
swfStage.scaleMode = StageScaleMode.NO_SCALE;
swfStage.align = StageAlign.TOP;
View = new View3D({x:250, y:200});
View.renderer = Renderer.BASIC;
addChild(View);
View.camera.position = new Number3D(400, 500, 400);
View.camera.lookAt( new Number3D(0, 0, 0) );
// 球を作成して追加
// 横方向のセグメント数を6個、縦方向のセグメント数を4個で作成
baseObject = new Sphere(
{material:"red#",radius:150,segmentsW:6,segmentsH:4});
// デフォルト(yUp = true)では y の上方向が球の上方向、
// yUp = false で、z の上方向が球の上方向になる
// baseObject.yUp = false;
View.scene.addChild(baseObject);
View.render();
addControls();
cover = new Cover(this);
addChild(cover);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onEnterFrame(e:Event):void {
if(!cover.visible) {
// ビューをレンダリング
View.render();
// 球を回転
baseObject.rotationY += 1;
//1 秒間に画面が書き換えられる回数 =
// 1000 ミリ秒 / 1 回の描画間隔時間
var fps:Number =
Math.floor( 1000/(getTimer()­lastTime) );
label2.text =
( (6+planeComplexity)*(4+planeComplexity­1)*2 )+" Triangles (fps: "+fps+")";
//label2.text =
( (planeComplexity)*(planeComplexity­1)*2 )+" Triangles (fps: "+fps+")";
lastTime = getTimer();
}
}
// コントロールを追加
private function addControls():void {
var pad:Number = 10;
var label1:Label = new Label(this, pad, pad);
label1.autoSize = false;
label1.width = 140;
label1.text = "Increase/decrease complexity";
// 複雑性を追加するボタン
var plusButt:PushButton = new PushButton(
this, pad, label1.height+label1.y, "+", increaseComplexity);
plusButt.width = plusButt.height = 20;
// 複雑性を減らすボタン
var minButt:PushButton = new PushButton(
this, 50, label1.height+label1.y, "­", decreaseComplexity);
minButt.width = minButt.height = 20;
label2 = new Label(this, pad, pad);
label2.autoSize = false;
label2.width = 140;
label2.y = 50;
addChild(label1);
addChild(label2);
addChild(plusButt);
addChild(minButt);
}
// 複雑性を減らす
private function decreaseComplexity(e:MouseEvent):void
// planeComplexity が 0 より大きいなら 1 を引き、
// そうでない場合には 0 にする
planeComplexity > 0 ?
planeComplexity ­= 1 : planeComplexity = 0;
// segmentsW は最低でも 6 を維持する(最初に作った球)
baseObject.segmentsW = 6+planeComplexity;
// segmentsH は最低でも 4 を維持する(最初に作った球)
baseObject.segmentsH = 4+planeComplexity;
// baseObject.segmentsW = planeComplexity;
// baseObject.segmentsH = planeComplexity;
}
{
// 複雑性を増す
private function increaseComplexity(e:MouseEvent):void
{
planeComplexity += 1;
baseObject.segmentsW = 6+planeComplexity;
baseObject.segmentsH = 4+planeComplexity;
// baseObject.segmentsW = planeComplexity;
// baseObject.segmentsH = planeComplexity;
}
}
}
ここまでで球が作成できるようになりましたが、ではその中に入りたい場合はどうすればよいのでしょう? 球
の内部に HoverCamera3D を配置し、球をその内部から見るように設定すると、事実上球のパノラマビ
ューワができあがります。オブジェクトをその内部から見えるようにするには、すべてのプリミティブで使用できる
invertFaces()メソッドを使う必要があります。
sphere. invertFaces();
次のサンプルでは、オブジェクトの内部から見えるようにテクスチャを裏返しています。
Basic08_sphere.as
これはサンジェルマン・アン・レーの教会の前で撮った写真です。ここで使っているパノラマイメージはCC
ByNeSa 2.0 ライセンス下で配布されているSeb Przdによるものです。Seb はクールなパノラマを多く作成
しているので、彼のFlickr アカウントをぜひ訪れてみてください。
このサンプルでは W と S キーにズームインとズームアウト、矢印キーにパンとティルト機能をつけています。
NOTE:Away3D の多くのバージョンで、invertFaces()メソッドが何も引き起こさないというバグがあるよう
です。これは次の方法で代用することができます。
sphere.scaleX = ­1;
invertFaces()メソッドの使用で何か問題がある場合には、この方法を試してみてください。ただしオブジェ
クトを内部と外部両方から見えるようにしたい場合には、bothsides プロパティを true に設定する必要が
あります。
sphere.bothsides = true;
Basic08_sphere.as
package {
import away3d.cameras.HoverCamera3D;
import away3d.containers.View3D;
import away3d.core.base.Object3D;
import away3d.events.MouseEvent3D;
import away3d.primitives.Sphere;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.ui.Keyboard;
[SWF(width="500", height="400",
frameRate="50", backgroundColor="#FFFFFF")]
public class Basic08_sphere extends Sprite {
private var cam:HoverCamera3D;
private var lastKey:uint;
private var keyIsDown:Boolean = false;
private var View:View3D;
private var cover:Cover;
private var sphere:Sphere;
[Embed(source="resources/panoramaSpherical.jpg")]
private var texture:Class;
public function Basic08_sphere() {
// ホバーカメラの作成
cam = new HoverCamera3D();
// cam.z = ­1000;
cam.panangle = 0;
cam.tiltangle = 0;
cam.targetpanangle = 0;
cam.targettiltangle = 0;
cam.mintiltangle = ­90;
cam.zoom = 4;
// ビューポートの作成
View = new View3D({camera:cam,x:250,y:200});
addChild(View);
// カメラを飲み込む巨大な球の作成
var largeSphere:Sphere = new Sphere(
{radius:1500,material:texture,segmentsW:14,segmentsH:28});
// 現時点では invertFaces()の動作にバグがあるので、
// scaleX = ­1 で代用
//largeSphere.invertFaces();
// largeSphere.bothsides = true;
largeSphere.scaleX = ­1;
View.scene.addChild(largeSphere);
cam.hover();
View.render();
cover = new Cover(this,500,400,
"Roll over and Click to activate. Use S, W and arrow keys to navigate");
addChild(cover);
this.stage.addEventListener(
KeyboardEvent.KEY_DOWN,onKeyDown);
this.stage.addEventListener(
KeyboardEvent.KEY_UP,onKeyUp);
this.addEventListener(
Event.ENTER_FRAME,onEnterFrame);
}
private function onEnterFrame(e:Event):void {
if(!cover.visible) {
if(keyIsDown){
// キーが押されつづけている場合には、
//移動をつづける
switch(lastKey){
case Keyboard.UP:
cam.targettiltangle ­= 5; break;
case Keyboard.DOWN:
cam.targettiltangle += 5; break;
case 87:
cam.zoom += 0.3; break;
case 83:
if(cam.zoom > 1.4){cam.zoom ­= 0.3}; break;
case Keyboard.LEFT:
cam.targetpanangle ­= 5; break;
case Keyboard.RIGHT:
cam.targetpanangle += 5; break;
}
}
// ビューのレンダリング
cam.hover();
View.render();
}
}
private function onKeyDown(e:KeyboardEvent):void {
lastKey = e.keyCode;
keyIsDown = true;
}
private function onKeyUp(e:KeyboardEvent):void {
keyIsDown = false;
}
}
}
このサンプルでは、球を使ったパノラマビューワの作成方法を見ました。よく用いられるもう1つのパノラマに直
方体による形式があり、Away3D にはまさにこの目的をかなえる2つのプリミティブがあります。
Skybox と Skybox6
Skybox と Skybox6 は実際には、パノラマに使用できるように“操作された”巨大な直方体です。
Skybox と Skybox6 の違いは、Skybox には6つの個別のイメージが指定でき、Skybox6 には 3 x 2 で
あらかじめ定義された矩形の単一イメージを指定する点です。Skybox6 を作成するには、次のようにマテ
リアルを与えます。
var mat:BitmapMaterial = new BitmapMaterial(
(new texture() as Bitmap).bitmapData );
largeCube = new Skybox6(mat);
通常の Skybox プリミティブを使用する場合には、6つのマテリアルを個別に作成し、次のシンタックスを使
用します。
largeCube = new Skybox(
frontMaterial,
leftMaterial,
backMaterial,
rightMaterial,
upMaterial,
downMaterial
);
次のサンプルでは、Skybox6 を使ってパノラマを作成しています。
Basic08_skybox6.as
訳者注:
訳文のサンプルで使っているイメージは Web 上のサンプルと異なります。Web 上のサンプルでは、この後著
者が説明する Pano2vr を使って、前のサンプルのイメージを Skybox6 用に加工したものを使っています。
訳文のサンプルでは、訳者の手元で動作を確認する必要があるので実際に入手できるイメージ(この後
著者が紹介しています)を使っています。
ソースコードを見ると、次のコードが記述されています。
// Face オブジェクトを4つの均等なサイズの Face オブジェクトに分割
largeCube.quarterFaces();
これによって Skybox6 の largeCube のフェースが分割され、largeCube がさらに多くの三角形で構成さ
れます。これを行わないと、回転させるときテクスチャにアーチファクト(ノイズなどの不自然な結果)が発生し
ます。このメソッドはロードしたモデルのほかすべてのマテリアルで使用できます。パノラマの作成で球でなく
Skybox を使用するメリットは、Skybox は球より少ない三角形で構成されるので、その分効率的になり、
しかも球より歪曲が少なくてすむことにあります。ほとんどの人々は気づかないでしょうが、厳密なパノラマが
求められる場合には、球を使って、直方体と同じくらいこれを効率的にするありとあらゆる方策を講じた方
がよいでしょう。
ここまでは難しくないですね? では6つのイメージがあればそれで準備できたと言えるのでしょうか? 実際
にはそうではありません。イメージには、それを切れ目なく見せるための適用する特別な歪曲が必要なので
す。Skybox6 のテクスチャは実際には次のようなものです(訳文の Basic08_skybox6 サンプルではこれを
使っています)。イメージがこのように見えない場合には、Skybox6 はイメージを期待したように表示してく
れません。
3 x 2 の my_skybox.jpg
では 3 x 2 のイメージはどうやって作成すればよいのでしょう? 球体パノラマのイメージを作成するソフトウェ
アは多くありますが、この 3 x 2 という形式は少々特殊です。わたしはこのチュートリアルを書くための調査を
行ったとき、手頃な価格の Pano2vr というソフトウェアを見つけました。これはクロスプラットフォーム
(Windows、Mac、Linux)で使用でき、あるパノラマ形式から 16 種類のパノラマ形式に変換する作業
をやってのけるすぐれたツールです。Pano2vr は QuickTime やビットマップ、Flash 形式にも書き出しま
す。Flash を書き出すオプションでは SWF とイメージ、HTML が作成できるので、パノラマにホットスポット
が設定できます。シングルユーザーライセンスは 59 ユーロ($107、¥10,000 ほどか?)なので、その美しい
変換結果と機能からして、高い買い物ではないでしょう。(Web 上の)サンプルのパノラマイメージの作成に
は Pano2vr の体験版を使っているので、いたるところに透かしが入っています。もちろん完全版にはこのよ
うな透かしは入りません。
Basic08_skybox6.as
package {
import away3d.cameras.HoverCamera3D;
import away3d.containers.View3D;
import away3d.materials.BitmapMaterial;
import away3d.primitives.Skybox6;
import away3d.core.utils.Cast;
import flash.display.Bitmap;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.ui.Keyboard;
[SWF(width="500", height="400",
frameRate="50", backgroundColor="#FFFFFF")]
public class Basic08_skybox6 extends Sprite {
private var cam:HoverCamera3D;
private var lastKey:uint;
private var keyIsDown:Boolean = false;
private var View:View3D;
private var cover:Cover;
private var largeCube:Skybox6;
[Embed(source="resources/panoramaSpherical_out.jpg")]
private var texture:Class;
public function Basic08_skybox6() {
cam = new HoverCamera3D();
cam.panangle = 0;
cam.tiltangle = 0;
cam.targetpanangle = 0;
cam.targettiltangle = 0;
cam.mintiltangle = ­90;
cam.zoom = 4;
View = new View3D({camera:cam,x:250,y:200});
addChild(View);
// Skybox6 を追加
var mat:BitmapMaterial = new BitmapMaterial(
Cast.bitmap(texture) );
largeCube = new Skybox6(mat);
// Face オブジェクトを4つの均等なサイズの Face オブジェクトに分割
largeCube.quarterFaces();
View.scene.addChild(largeCube);
cam.hover();
View.render();
cover = new Cover(this,500,400,
"Roll over and Click to activate. Use the arrow keys to navigate and S, W to zoom");
addChild(cover);
this.stage.addEventListener(
KeyboardEvent.KEY_DOWN,onKeyDown);
this.stage.addEventListener(
KeyboardEvent.KEY_UP,onKeyUp);
this.addEventListener(
Event.ENTER_FRAME,onEnterFrame);
}
private function onEnterFrame(e:Event):void {
if(!cover.visible) {
if(keyIsDown){
switch(lastKey){
case Keyboard.UP:
cam.targettiltangle ­= 5; break;
case Keyboard.DOWN:
cam.targettiltangle += 5; break;
case 87:
cam.zoom += 0.3; break;
case 83:
if(cam.zoom > 1.4){
cam.zoom ­= 0.3}; break;
case Keyboard.LEFT:
cam.targetpanangle ­= 5; break;
case Keyboard.RIGHT:
cam.targetpanangle += 5; break;
}
}
cam.hover();
View.render();
}
}
private function onKeyDown(e:KeyboardEvent):void {
lastKey = e.keyCode;
keyIsDown = true;
}
private function onKeyUp(e:KeyboardEvent):void {
keyIsDown = false;
}
}
}