2007年07月22日

Papervision3Dがついに公式リリース

Papervision3D 1.5 (AS3) Official Release
ついに公式リリースですね!
これまではSVNから落としてこなければなりませんでしたが、1.5はリンク先のページから、ソースファイル一式を落とせます。

高速化や機能の追加など色々あるようで楽しみ。とりあえずこれまで書いたものを新バージョンでコンパイルし直してみたけど、どれも無事に通りました。(^^;

ビデオテクスチャを簡単に扱える VideoStreamMaterial クラスがサポートされていたので、「動画をテクスチャとして使う」の内容を書き直しておきました。materialの数がまたまた増えてますね。

今回の主な変更点は以下のようです。
  1. 主要なレンダリング処理のループを最適化。
  2. プラグイン可能なポリゴンカリングを実装。今回は1種類のカリングを利用可能。
  3. Face3Dからマテリアルへレンダリング処理を移動させた。すべてのマテリアルクラスは drawFace3D() 関数をオーバーライドしている。
  4. 新しいマテリアルクラスを追加: BitmapWire, BitmapColor, and CompositeMaterial
  5. fixed stars, vertices, DisplayObject3D 等々、各クラスに対するインスタンス全てを移動。
  6. ポリゴンの法線を実装
  7. BitmapFileMaterial のバグを修正。
  8. BitmapFileMaterial と Collada に ProgressEvent を追加。
  9. InteractiveScene3D を バージョンに1.5に統合。
  10. InteractiveScene3Dで使用する、Interactiveなマテリアルを追加。InteractiveSceneManagerを介してマウスイベントをディスパッチされる。

posted by おけ at 05:17| Comment(0) | TrackBack(0) | Papervision3D | このブログの読者になる | 更新情報をチェックする

2007年07月21日

3ds MAXのモデルを読み込んでみる

今までは立方体や球面のような単純なモデルを表示していましたが、3Dモデリングツールで作られたモデルを読み込んで表示してみます。Papervison3DはASEやCOLLADAに対応していますが、D3S Importer Libraryを使うと、M2Dや3dsフォーマットのモデルを表示できます。

今回もnote.xさんの記事を参考にさせていただきました。
note.x:Papervision3Dメモ #19 D3S Importer Library for Papervision3D

ライブラリを落としてきて設定するまでの手順はPapervision3Dの時と同じです。
  1. 公式サイトからライブラリを落とす。
  2. 解凍した後、d3sフォルダをFlex SDKのframeworks/sourceフォルダ以下に置く。

ライブラリの使い方はとても簡単で、Max3DSをimportしておいて、
import d3s.net.papervision3d.objects.Max3DS;
Max3DSクラスのコンストラクタにマテリアルとモデルファイルの名前を渡して、モデルオブジェクトを作成します。
作ったモデルを表示するにはいつものように、シーンに addChild() してあげます。
//  マテリアル設定
material = new WireframeMaterial( 0xffffff, 150 );
// .3dsファイルからモデルを作成
model = new Max3DS( material, "DINO.3ds", 3 );
scene.addChild( model );
注意しなければならない事として、D3S Importer Libraryはアニメーションに対応してません。また読み込むのは、頂点、テクスチャ座標、面の情報だけで、テクスチャなどは自分で読み込む必要があります。

(アニメーションするモデルを出したいなら、手っ取り早いのはnote.xさんがやられているように、MD2フォーマットのファイルを探してそちらを表示することでしょう。MD2はモデルの頂点アニメーションのデータを持っているので、Papervison3Dのような単純な3Dエンジンには向いてますね。とはいえ、古いゲームエンジンのフォーマットですし、日本語圏では馴染みが無いのがネックですね。)

実を言うと今回一番苦労したのは、適切な.3dsデータを探すことでした。ハイポリゴンのモデルは割とゴロゴロしてたんですが、さすがに1万ポリゴン前後だと、重くてブラウザの挙動もおかしくなるので……。

今回使用させていただいたのはNeval'sCGさんが公開しておられる恐竜のモデルです。(Neval'sCG→Models→DINOSAUR)
表示の際はワイヤーフレームで、背面を非表示にしています。

Load_3DS.swf
package
{
import d3s.net.papervision3d.objects.Max3DS;
import flash.display.*;
import flash.events.*;
import flash.text.*;
import org.papervision3d.cameras.Camera3D;
import org.papervision3d.core.Number3D;
import org.papervision3d.core.proto.MaterialObject3D;
import org.papervision3d.materials.*;
import org.papervision3d.scenes.Scene3D;

[SWF(backgroundColor = 0x3333ff)]

public class main extends Sprite
{
// 3Dライブラリ用
private var container : Sprite;
private var scene : Scene3D;
private var camera : Camera3D;
private var material : WireframeMaterial;
// 3Dモデル
private var model : Max3DS; // Max3DSモデル
// 回転制御
private var rot : Number = 0;
// モデル情報
private var sModelInfo : TextField;

public function main()
{
stage.frameRate = 60;
stage.quality = "MEDIUM";
stage.scaleMode = "noScale";
stage.align = StageAlign.TOP_LEFT;

sModelInfo = new TextField();
sModelInfo.backgroundColor = 0x000000;
sModelInfo.defaultTextFormat = new TextFormat("Verdana", 16, 0xFFFFFFFF);
sModelInfo.selectable = false;
sModelInfo.width = 300;
addChild( sModelInfo );

init3D();
this.addEventListener( Event.ENTER_FRAME, update );
}

// 3Dの初期化
private function init3D():void
{
// コンテナ生成
this.container = new Sprite();
addChild(this.container);
this.container.x = this.stage.stageWidth / 2;
this.container.y = this.stage.stageHeight / 2;

// シーン生成
scene = new Scene3D( container );

// カメラ設定
camera = new Camera3D();
camera.y = 30;
camera.z = 100;
camera.zoom = 10;
camera.focus = 100;

// マテリアル設定
material = new WireframeMaterial( 0xffffff, 150 );
material.oneSide = true;

// .3dsファイルからモデルを作成
model = new Max3DS( material, "DINO.3ds", 3 );
scene.addChild( model );
}

// 毎フレームの処理
private function update( event:Event ):void
{
// マウス座標でオブジェクトを回転
rot += this.container.mouseX / 150;
model.rotationX = 90;
model.rotationY = rot;

// モデルの情報を表示
sModelInfo.text = "vertices: " + model.geometry.vertices.length +
"\nfaces:" + model.geometry.faces.length;
// レンダリング
this.scene.renderCamera( camera );
}
}
}

posted by おけ at 18:55| Comment(0) | TrackBack(0) | Papervision3D | このブログの読者になる | 更新情報をチェックする

2007年06月28日

Papervision3Dの仕組みについて

Flashでは3D表示のための機能は特に提供されていません。
ではどのようにして、3D表示を実現しているのか。ライブラリ内のコードを少し覗いてみました。予想どおり、PCやゲーム機で10年以上昔に使われていた、かなり古典的なアルゴリズムを用いていました。

通常3Dの表示物はポリゴンという多角形で表現します。ふつうは三角形を基本単位とします。三角形は3次元空間内で、ねじれた形を取らないため、取り扱いやすいのです。

3D表示は簡単に言ってしまえば、2つのステップを実行するだけです。
  1. 3D空間の頂点が2D画面上のどこにくるかを、座標変換で計算する。
  2. 座標変換した結果にしたがって、三角形を表示する。ただし手前のポリゴンで隠される面は表示すべきではない(隠面消去)。

1番は行列の計算をするだけです。数学的な原理は割愛しますが、興味をお持ちの方はWeb上に資料がいくつもあります。
【CG教科書】3次元図形の描画

2番の隠面消去は、現在ではZバッファ法という方法を用いるのが一般的です。ただしこのアルゴリズムは、ソフトウェアで実現するとなると、かなりパフォーマンスを必要としますし、一般的には3D専用のハードウェアで実現されています。

Papervision3Dが使っているのはZソート法というアルゴリズムです。
【CG教科書】Zソート法
3次元の立体が三角形や四角形のポリゴンで構成されているとして、正しく立体を描くには、奥から手前に向かって、ポリゴンを順序正しく描画していけばいいよね、という考え方です。ゲーム機でいうと、初代プレイステーションがこの方法を用いていました。

このアルゴリズムの欠点は、奥行きを判定するのがポリゴン単位なため、交差しているポリゴンの奥行きを正しく描画できないことです。Papervision3Dでモデルを表示した際、ポリゴンが欠けて見える事があるのはこのためです。

Flashは2Dの多角形を描く機能はそろっています。またビットマップを多角形状に描画することも可能です。Papervisionのテクスチャマッピングはそうやって実現しています。

Papervision3Dを拡張して、もっと高機能にしたものとして「Away3D」という3Dエンジンが開発されていましたが、現在はその開発者がPapervision3Dに参加して、Away3Dの機能をPapervision3Dに統合しているそうです。期待しましょう。

Flashの3DエンジンはWebブラウザ上で動作するのが強みですが、ゲーム機どころか最近の携帯電話の表示能力にも劣ります。中身はFlashなので、フィルタが併用しやすかったり、FLV形式の動画をテクスチャとして使うのが簡単です。特徴をいかしたアプリケーションを制作するのがいいんじゃないかな、と思ってます。

以下のプログラムは、PlaneやCubeといったプリミティブクラスを使わず、自分でMesh3Dオブジェクトの中身を用意しています(三角形1枚だけですが)。PlaneやCubeでは、コンストラクタ内で同じように material を設定し、Vertex3D と Face3D を生成して Mesh3D オブジェクトに登録しています。

TriangleTest.swf
package
{
import flash.display.*;
import flash.events.*;
import flash.text.*;

import org.papervision3d.scenes.*;
import org.papervision3d.objects.*;
import org.papervision3d.cameras.*;
import org.papervision3d.materials.*;

import org.papervision3d.core.*;
import org.papervision3d.core.proto.*;
import org.papervision3d.core.geom.*;


[SWF(backgroundColor = 0x3333ff)]

public class main extends Sprite
{
// メンバ変数
private var container : Sprite;
private var scene : Scene3D;
private var camera : Camera3D;
private var rootNode : DisplayObject3D;
private var material : ColorMaterial;

// コンストラクタ
public function main():void
{
stage.frameRate = 60;
stage.quality = "MEDIUM";
stage.scaleMode = "noScale";
stage.align = StageAlign.TOP_LEFT;
this.addEventListener( Event.ENTER_FRAME, update );
this.stage.addEventListener( Event.RESIZE, resize );

init3D();
}


// 3Dの初期化
private function init3D():void
{
// コンテナ生成
this.container = new Sprite();
addChild(this.container);
this.container.x = this.stage.stageWidth / 2;
this.container.y = this.stage.stageHeight / 2;

// シーン生成
scene = new Scene3D( container );

// カメラ設定
camera = new Camera3D();
camera.z = -300;
camera.focus = 500;
camera.zoom = 1;


// rootNode生成
rootNode = scene.addChild( new DisplayObject3D( "rootNode" ) );

// マテリアル設定
material = new ColorMaterial( 0x33ff33 );

// メッシュを作成
var mesh: Mesh3D = new Mesh3D( material, new Array(), new Array(), null );

// まず頂点を登録
mesh.geometry.vertices.push( new Vertex3D( 0, 0, 0 ) );
mesh.geometry.vertices.push( new Vertex3D( 50, 100, 0 ) );
mesh.geometry.vertices.push( new Vertex3D( 100, 0, 0 ) );

// 頂点とテクスチャ座標から面を作成
var a: Vertex3D = mesh.geometry.vertices[0];
var b: Vertex3D = mesh.geometry.vertices[1];
var c: Vertex3D = mesh.geometry.vertices[2];

// 三角形の頂点の順番が半時計周りなことに注意
mesh.geometry.faces.push( new Face3D( [ a, c, b ], null, null ) );
mesh.geometry.ready = true;

rootNode.addChild( mesh );
}

// 毎フレームの処理
private function update( event:Event ):void
{
// レンダリング
this.scene.renderCamera( camera );
}

// 画面のリサイズ時の処理
private function resize(event:Event):void
{
this.container.x = this.stage.stageWidth / 2;
this.container.y = this.stage.stageHeight / 2;
}

}
}


posted by おけ at 02:33| Comment(0) | TrackBack(0) | Papervision3D | このブログの読者になる | 更新情報をチェックする

2007年06月24日

動画をテクスチャとして使う

FLASHなら.flv形式の動画を簡単に再生できるので、それをテクスチャとして使う方法を調べてみました。参考にしたのはこの3つの記事。
前回は静止画像を立方体のテクスチャに使っていましたが、今回は動画をテクスチャに用います。動画を再生した時の表示内容を、毎回テクスチャのビットマップに描画する必要があります。

この手法を用いれば、3Dモデルの映画館のスクリーンやテレビの画面に、動画を貼り付けることも可能ですよね。

VideoTexture.swf
package
{
import flash.display.*;
import flash.events.*;
import flash.media.*;
import flash.net.*;
import flash.text.*;
import flash.filters.*;
import flash.geom.Matrix;
import org.papervision3d.scenes.*;
import org.papervision3d.objects.*;
import org.papervision3d.cameras.*;
import org.papervision3d.materials.*;

[SWF(backgroundColor = 0xffffff)]

public class main extends Sprite
{
// 3D設定
private var container : Sprite;
private var scene : MovieScene3D;
private var camera : Camera3D;
private var rootNode : DisplayObject3D;
private var cubeObj : DisplayObject3D;
// ビデオストリーム用
private var connect : NetConnection;
private var stream : NetStream;
private var video : Video;

private var material : VideoStreamMaterial;
private var rotX : Number = 0;
private var rotY : Number = 0;

// コンストラクタ
public function main():void
{
stage.frameRate = 60;
stage.quality = "MEDIUM";
stage.scaleMode = "noScale";
stage.align = StageAlign.TOP_LEFT;
this.addEventListener( Event.ENTER_FRAME, update );
this.stage.addEventListener( Event.RESIZE, resize );

loadVideo();
init3D();
}

private function loadVideo(): void
{
// コネクションの作成
connect = new NetConnection();
connect.connect( null );

// ストリームの準備
stream = new NetStream( connect );

// 動画の準備
video = new Video();
video.attachNetStream( stream );

stream.play("VideoTexture.flv");
}

// 非同期エラーの処理
private function asyncErrorHandler(evt:AsyncErrorEvent):void
{
}

// 3Dの初期化
private function init3D():void
{
// コンテナ生成
this.container = new Sprite();
addChild(this.container);
this.container.x = this.stage.stageWidth / 2;
this.container.y = this.stage.stageHeight / 2;

// シーン生成
scene = new MovieScene3D( container );

// カメラ設定
camera = new Camera3D();
camera.z = -300;
camera.focus = 500;
camera.zoom = 0.6;

// rootNode生成
rootNode = scene.addChild( new DisplayObject3D( "rootNode" ) );

material = new VideoStreamMaterial( video, stream );
cubeObj = rootNode.addChild( new Cube( material, 300, 300, 300, 1, 1 ) );
}

// 毎フレームの処理
private function update( event:Event ):void
{
// マウス座標でオブジェクトを回転
rotX += this.container.mouseX / 150;
rotY += this.container.mouseY / 150;

cubeObj.rotationX = rotX;
cubeObj.rotationY = rotY;

// ビットマップを更新
material.updateBitmap();

// レンダリング
this.scene.renderCamera( camera );
}

// 画面のリサイズ時の処理
private function resize(event:Event):void
{
this.container.x = this.stage.stageWidth / 2;
this.container.y = this.stage.stageHeight / 2;
}

}
}

まず下準備として、FLVを再生します。この部分は3Dでもそうでなくても変わりありません。ActionScript3.0でFLVを再生するサンプルを参考に組みます。
// コネクションの作成
connect = new NetConnection();
connect.connect( null );
// ストリームの準備
stream = new NetStream( connect );
// 動画の準備
video = new Video();
video.attachNetStream( stream );
// 再生
stream.play("VideoTexture.flv");
NetConnectionクラスでサーバーへの接続を行い、NetStreamクラスでストリームを開き、制御します。Videoクラスはストリームから動画の再生をおこないます。詳しくは各クラスのリファレンスを参照してください。

通常なら addChild() で Video オブジェクトを表示リストに追加しますが、テクスチャとして使うので、あらかじめ確保したビットマップ領域に描画します。
video.attachNetStream( null );
bmpData.draw(video);
video.attachNetStream(stream);
VideoStreamMaterial はこうした処理をやってくれる便利なクラスです。
material = new VideoStreamMaterial( video, stream );
マテリアルを生成する時に、Video オブジェクトと Stream オブジェクトを渡してやり、あとは毎フレーム updateBitmap() 関数を呼んで、ビットマップを更新してあげます。
//  ビットマップを更新
material.updateBitmap();

動画素材はミニネットZOOさんが配布しておられる動画をRiva FLV Encoderで.flv形式に変換して使用しています。
posted by おけ at 03:22| Comment(0) | TrackBack(0) | Papervision3D | このブログの読者になる | 更新情報をチェックする

2007年06月18日

Papervision3Dを始めてみた

2D表現しかできないと思っていたFlashで、3D表示が実現!?
一躍話題になっていた「Papervision3D」。
Gigazineの紹介記事で初めて知りましたが、これだけの速度で動くのは驚異的!
ActionScript2.0版とActionScript3.0版(FlashPlayer9.0以降)があって、さすがにこれだけ高速に動くのはAS3版かららしい。
Gigazine:Flashで動作する3Dエンジン「Papervision3D」

僕もひさしぶりにプログラミングしてみたくなりました。
まずはActionScript3自体、初めてなので、開発環境を落とす所から。
[Z]ZAPAブロ〜グ2.0:はじめてのActionScript3.0プログラミング
このページを読んで、Flex2 SDKをインストールしました。

次にPapervision3Dを始めるに当たって、以下の先輩たちのブログを参考に。掲載しておられるソースコードを参考にして、実際に組んだのがこれ。平面、球面、立方体、円柱といった代表的なプリミティブを表示しています。

HelloWorld.swf
package
{
import flash.display.*;
import flash.events.*;
import flash.geom.*;
import flash.filters.*;

import org.papervision3d.scenes.*;
import org.papervision3d.objects.*;
import org.papervision3d.cameras.*;
import org.papervision3d.materials.*;

import org.papervision3d.core.*;
import org.papervision3d.core.proto.*;
import org.papervision3d.core.geom.*;

[SWF(backgroundColor = 0x3333aa)]

public class main extends Sprite
{
// メンバ変数
private var container : Sprite;
private var scene : Scene3D;
private var camera : Camera3D;
private var rootNode : DisplayObject3D;

private var planeObj : DisplayObject3D;
private var sphereObj : DisplayObject3D;
private var cubeObj : DisplayObject3D;
private var cylinderObj : DisplayObject3D;

private var material : BitmapFileMaterial;
private var rot : Number = 0;

// コンストラクタ
public function main():void
{
stage.frameRate = 60;
stage.quality = "MEDIUM";
stage.scaleMode = "noScale";
stage.align = StageAlign.TOP_LEFT;
this.addEventListener(Event.ENTER_FRAME, update );
this.stage.addEventListener(Event.RESIZE, resize );

init3D();
}

// 3Dの初期化
private function init3D():void
{
// コンテナ生成
this.container = new Sprite();
addChild(this.container);
this.container.x = this.stage.stageWidth / 2;
this.container.y = this.stage.stageHeight / 2;

// シーン生成
scene = new Scene3D( container );

// カメラ設定
camera = new Camera3D();
camera.y = 100;
camera.z = -800;
camera.focus = 1200;
camera.zoom = 0.3;

// rootNode生成
// シーンの下にrootNodeをつけ、その下に各モデルをくっつける
rootNode = scene.addChild( new DisplayObject3D( "rootNode" ) );

// マテリアル設定
material = new BitmapFileMaterial( "HelloWorld.jpg" );
material.doubleSided = true;

planeObj = rootNode.addChild( new Plane( material, 300, 300, 2, 2 ) );
sphereObj = rootNode.addChild( new Sphere( material, 150, 8, 8 ) );
cubeObj = rootNode.addChild( new Cube( material, 300, 300, 300, 1, 1 ) );
cylinderObj = rootNode.addChild( new Cylinder( material, 150, 300, 8, 8 ) );
}

// 毎フレームの処理
private function update( event:Event ):void
{
// マウス座標でオブジェクトを回転
rot += this.container.mouseY / 50;

planeObj.rotationY = rot;
sphereObj.rotationY = rot;
cubeObj.rotationY = rot;
cylinderObj.rotationY = rot;

planeObj.x = -600;
sphereObj.x = -200;
cubeObj.x = 200;
cylinderObj.x = 600;

//レンダリング
this.scene.renderCamera( camera );
}

// 画面のリサイズ時の処理
private function resize(event:Event):void
{
this.container.x = this.stage.stageWidth / 2;
this.container.y = this.stage.stageHeight / 2;
}

}
}
ソースコードを掲載するにあたってはAS2HTMLを使用させていただきました。

プログラムではまずsceneを作って、そこにシーンの各モデルの親となるrootNodeをぶら下げます。
scene = new Scene3D( container );
rootNode = scene.addChild( new DisplayObject3D( "rootNode" ) );

次にモデルの質感を定義するマテリアルを先に作ります。今回は画像ファイルからマテリアルを作る BitmapFileMaterial() 関数を使います。doubleSided はポリゴンの表側のみを描くか、表裏の両面を描くかを設定してます。
material = new BitmapFileMaterial( "HelloWorld.jpg" );
material.doubleSided = true;

いよいよモデルを作成します。Mesh3Dクラスのサブクラス、Plane、Sphere、Cube、Cylinderを使うと、簡単に代表的な形状を作れます。モデルを生成する関数は、Papervision3Dのorg.papervision3d.objects以下のasファイルに定義されています。
作ったモデルは先ほどの rootNode の下にぶら下げます。
planeObj = rootNode.addChild( new Plane( material, 300, 300, 2, 2 ) );
初期化はここまで。あとは scene の renderCamera() 関数を毎フレーム呼び出すだけです。
this.scene.renderCamera( camera );


関連サイト
svnリポジトリ
ここからAS3版を取ってきます。設定についてはnote.xさんのこちらの記事を参考にしました。

Papervision3D
Papervision3Dの開発者による公式ブログ。新しいリリースについての情報などが公開されています。

Papervision3D WIKI.
Papervision3Dに関するWiki。
サンプルソースや素材などがアップされています。

しばらく追いかけてみたいと思います!
posted by おけ at 00:21| Comment(0) | TrackBack(0) | Papervision3D | このブログの読者になる | 更新情報をチェックする

広告


この広告は60日以上更新がないブログに表示がされております。

以下のいずれかの方法で非表示にすることが可能です。

・記事の投稿、編集をおこなう
・マイブログの【設定】 > 【広告設定】 より、「60日間更新が無い場合」 の 「広告を表示しない」にチェックを入れて保存する。