株式会社antsのホームページへようこそ。

[AS3.0]GreatWhite(Papervision3D 2.0 Alpha)を試してみた

0
Posted in Lab. By kiyokazk

こんにちは、kiyokazkです。
前回のエントリーで言ったとおり、Papervision3D 2.0 Alpha (GreatWhite) を試してみました。今回使用したのはActionScript 3.0 + Papervision3D Public Alpha 2.0 – Great White (24.03.08)です。

前回の目玉をそのまま移植するつもりでしたが、試しているといろいろとできないことがあることが分かったので何ができるか検証しつつ、ついでに目玉の挙動も変えてみました。こっちの方がオリジナル目玉の挙動に近いですね。

ちなみに今回GreatWhiteで試してみたかったことは二つ。ライトが使えるらしいということと便利な(フレームワーク的な)Viewクラスがあるらしいということです。


■Papervision3D 2.0 の準備
さてそれでは、まずは前回と同じようにPapervision3D 2.0を準備するところから始めましょう。

Papervision3D 2.0はSubversionで入手するしかないようです。なので適当なSubversionクライアントを準備して、以下のリポジトリからチェックアウトします。
http://papervision3d.googlecode.com/svn

無事にチェックアウトできたらFlashにパスを通しておきます。今回はGreatWhiteを使うので、チェックアウトしたディレクトリ/trunk/branches/GreatWhite/src/ にパスを設定しましょう。

※ちなみに僕の環境だけかもしれませんが、Papervision3D 1.5にもパスを通しているとコンパイルエラーが起こったので、同じ症状が出る方はPapervision3D 1.5をハズすと幸せになるかもしれません(僕のところはそれで通りました)。

■BasicViewクラス
GreatWhiteではBasicViewという便利なクラスが追加されています。
中身を見ればわかりますが、カメラ、シーン、ビューポート、レンダラーという3D描画に必要なものをまとめたクラスになっていて、さらにSpriteクラスを継承しているので(正確には間にAbstractViewというクラスがあります)、今回作成するクラスはこのBasicViewクラスを継承して作ります。

■ライト&シェーディング
GreatWhiteではライトの概念が導入されました。これでテクスチャなしでものっぺりとした見た目にならないようにできます。使い方としては、ライトを作成→そのライトを使ってシェードマテリアルを作成→そのマテリアルを使ってモデルを作成、という流れです。

このバージョンのGreatWhiteで使えるものは、ライトはポイントライト、マテリアルはフラットシェーディング、グーローシェーディング、フォンシェーディング、セルシェーディング、環境マップです。それぞれのASファイルはライトはlightsの下、マテリアルはmaterials/shadematerialsの下にあります。必要なものをインポートしましょう。

今回作成するswfではフォンシェーディングを使ってみることにします。スペキュラーハイライトがあるツヤツヤした質感に見えるシェーディングです。

■半透明+ライトは使えない?
今回は白目部分を半透明のフォンシェーディングで作ろうと思ったんですが、どうもうまくいかないようです。普通にPhongMaterialのfillAlphaをいじればできるかなと思っていたんですが、ライティングのための色計算が優先されているのか、アルファ値を変更しても見た目はまったく変化なしです。何か別のやり方があるのかな…?軽く中身をみてみたけどいいやり方はわかりませんでした。誰かいいやり方があれば教えてください。

また、前回の白目はMayaでColladaファイルを作成して使用していたんですが、今回も同じようにやろうとしてモデルのマテリアルにPhongMaterialを設定したら表示でこけるようになってしまいました。

仕方がないので、白目部分はプリミティブのSphereを二つ使用して不透明のPhongMaterialでいくことにします。二つの球は一つのモデルのように扱いたいので、空のDisplayObject3Dを用意してその子として追加します。移動や回転はこのDisplayObject3Dに対して行います。黒目部分もプリミティブのSphereを使って作ります。こちらは独立して動くのでシーンの子として追加します。

■動きを作る
前回は白目を半透明で作ったので黒目をその中に入れて動かしていましたが、今回は白目が不透明なため同じように中に入れたら見えなくなってしまいます。なので今回はマウスの動きに応じて白目の表面をなぞるように移動するようにします。

また、白目部分もただ回転させておくのはやめて、マウスの方向を向くようにしましょう。ターゲットをDisplayObject3Dで作成してマウスの位置に応じて位置を決めてやって、lookAt()でその方向に向ければOKです。

■完成
今回作成したswfはコチラです。
前回に比べて3Dっぽくなってるのが分かると思います。動きもオリジナルの目玉に近くなっているんじゃないでしょうか。ただ黒目の動きは相変わらず手抜きです(ステージの中心あたりにマウスカーソルを持っていくとバレバレです)。

■ソース
今回のソースコードを以下に載せておきます。BasicViewを使用したのでかなり短いですね。ちょっとしたものを作るのにはBasicViewはかなり便利だと思います。
[as3]
package {
import flash.events.Event;
import org.papervision3d.view.BasicView;
import org.papervision3d.lights.PointLight3D;
import org.papervision3d.objects.DisplayObject3D;
import org.papervision3d.objects.primitives.Sphere;
import org.papervision3d.materials.shadematerials.PhongMaterial;
public class Main extends BasicView {
private var _light:PointLight3D;
private var _materialBalls:PhongMaterial;
private var _materialEyes:PhongMaterial;
private var _eyesContainer:DisplayObject3D;
private var _eyeL:Sphere;
private var _eyeR:Sphere;
private var _ballL:Sphere;
private var _ballR:Sphere;
private var _target:DisplayObject3D;
private static const EYE_SIZE:Number = 100;
private static const EYE_POS_X:Number = 100;
private static const BALL_SIZE:Number = 15;
private static const BALL_POS_X:Number = 100;
private static const BALL_MOVE_LEN:Number = 110;
public function Main() {
super(320, 240);
camera.z = -300;
//ライトを作成
_light = new PointLight3D();
_light.x = -200;
_light.y = 300;
_light.z = -500;
//白目を作成
_eyesContainer = new DisplayObject3D();
scene.addChild( _eyesContainer );
_materialEyes = new PhongMaterial( _light, 0xFFFFFF, 0x808080, 1 );
_eyeL = new Sphere(_materialEyes, EYE_SIZE, 20, 20);
_eyeL.x = -EYE_POS_X
_eyesContainer.addChild( _eyeL );
_eyeR = new Sphere( _materialEyes,EYE_SIZE, 20, 20 );
_eyeR.x = EYE_POS_X;
_eyesContainer.addChild( _eyeR );
//黒目を作成
_materialBalls = new PhongMaterial( _light, 0xFFFFFF, 0x000000, 1 );
_ballL = new Sphere( _materialBalls, BALL_SIZE, 20, 20 );
_ballL.x = -BALL_POS_X;
scene.addChild( _ballL );
_ballR = new Sphere( _materialBalls, BALL_SIZE, 20, 20 );
_ballR.x = BALL_POS_X;
scene.addChild( _ballR );
//白目が向く方向のターゲットを作成
_target = new DisplayObject3D();
//毎フレームの処理用ハンドラを登録
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
//レンダリング開始
startRendering();
}
private function enterFrameHandler(event:Event):void {
var ratioX:Number = this.mouseX / stage.stageWidth * 2.0 – 1.0;
var ratioY:Number = this.mouseY / stage.stageHeight * 2.0 – 1.0;
var ratioXZ:Number = Math.sin( Math.acos( ratioX ) );
var ratioYZ:Number = Math.sin( Math.acos( ratioY ) );
//黒目の位置決め
var centerX:Number = BALL_POS_X * Math.cos( _eyesContainer.rotationY * Math.PI / 180 );
var centerY:Number = 0;
var centerZ:Number = BALL_POS_X * Math.sin( _eyesContainer.rotationY * Math.PI / 180 );
var offsetX:Number = ratioX * BALL_MOVE_LEN;
var offsetY:Number = -( ratioY * BALL_MOVE_LEN * ratioXZ );
var offsetZ:Number = -( ratioXZ * ratioYZ * BALL_MOVE_LEN );
_ballL.x = -centerX + offsetX;
_ballL.y = centerY + offsetY;
_ballL.z = -centerZ + offsetZ;
_ballR.x = centerX + offsetX;
_ballR.y = centerY + offsetY;
_ballR.z = centerZ + offsetZ;
//マウス位置方向に白目を向ける
_target.x = ratioX * 320;
_target.y = 0;
_target.z = -300;
_eyesContainer.lookAt( _target );
}
}
}
[/as3]