fc2ブログ

9ballsyndrome

Away3D 4.0 カスタムフィルタの作り方(後編)

Away3D 4.0 カスタムフィルタの作り方(前編)

 

引き続きカスタムフィルタを作る。

 

 


次にキラキラするTwinkleFilter3Dだが、そもそものキラキラの元祖?はおそらくこちら。

wonderfl Saqoosha "Snow"

Saqooshaさんのキラキラする雪が文字に積もるというもの。

キラキラのロジックは画面を1/4の大きさにしながらBitmapData.draw()でキャプチャして、4倍に引き延ばしたものを加算合成するという単純な操作。

なぜこれだけでキラキラしたりしなかったりするのかというと、おそらく

  1. 1/4のBitmapDataにdraw()する時点で、4の倍数ピクセル以外の雪ピクセルの情報が抜け落ちる
  2. それを4倍に拡大する際、抜け落ちなかった雪ピクセルは4*4ピクセルに引き延ばされる
  3. 加算合成するので、抜け落ちた雪ピクセルはそのままに、圧縮される際に残った雪ピクセルはぼやけたように見える

といった仕組みかと思われる。

簡単な処理で絶大な視覚的効果を発揮する驚嘆すべき発想で、以来様々な人が利用している。まさにwonderflの理想的な使われ方の例だろう。

最初はStage3Dでは、通常のstageと違うレイヤー、BitmapDataにdrawできない(drawToBitmapDataでできることはできるが遅い)のでこの方法が使えないのではと思っていた。

しかし今回のようなフィルタの観点から見ると、この縮小(抜け落ち)→拡大(補間)→加算合成の手順はfragmentシェーダにうってつけの処理だ。

縮小したテクスチャを作っておき、そこにsetRenderToTextureして拡大したものを加算すればいいだけだからだ。

 

 

TwinkleFilter3D

package {
    import away3d.cameras.Camera3D;
    import away3d.containers.View3D;
    import away3d.core.managers.Stage3DProxy;
    import away3d.debug.Debug;
    import away3d.filters.Filter3DBase;
    import com.adobe.utils.AGALMiniAssembler;
    import flash.display3D.Context3D;
    import flash.display3D.Context3DProgramType;
    import flash.display3D.Context3DTextureFormat;
    import flash.display3D.Context3DVertexBufferFormat;
    import flash.display3D.Program3D;
    import flash.display3D.textures.Texture;
 
    /**
     * ...
     * @author
     */
    public class TwinkleFilter3D extends Filter3DBase {
        private var _shrinkProgram3D:Program3D;
        private var _compositeProgram3D:Program3D;
        private var _shrinkTexture:Texture;
        private var _strength:uint;
 
        public function TwinkleFilter3D(strength:uint = 4){
            super(false);
            _strength = strength < 2 ? 2 : strength;
        }
 
        override public function render(stage3DProxy:Stage3DProxy, target:Texture, camera:Camera3D, depthRender:Texture = null):void {
            var context:Context3D = stage3DProxy.context3D;
 
            super.render(stage3DProxy, target, camera);
 
            if (!_shrinkProgram3D)
                initProgram(context);
 
            stage3DProxy.setProgram(_shrinkProgram3D);
            context.setRenderToTexture(_shrinkTexture, false, 0, 0);
            context.clear(0.0, 0.0, 0.0, 1.0);
            context.setVertexBufferAt(0, _vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_2);
            context.setVertexBufferAt(1, _vertexBuffer, 2, Context3DVertexBufferFormat.FLOAT_2);
            stage3DProxy.setTextureAt(0, _inputTexture);
            context.drawTriangles(_indexBuffer, 0, 2);
 
            if (target)
                context.setRenderToTexture(target, false, 0, 0);
            else
                context.setRenderToBackBuffer();
 
            stage3DProxy.setProgram(_compositeProgram3D);
            context.clear(0.0, 0.0, 0.0, 1.0);
            stage3DProxy.setTextureAt(0, _inputTexture);
            stage3DProxy.setTextureAt(1, _shrinkTexture);
            context.drawTriangles(_indexBuffer, 0, 2);
 
            stage3DProxy.setTextureAt(0, null);
            stage3DProxy.setTextureAt(1, null);
            stage3DProxy.setSimpleVertexBuffer(0, null);
            stage3DProxy.setSimpleVertexBuffer(1, null);
        }
 
        override protected function initTextures(context:Context3D, view:View3D):void {
            var w:int;
            var h:int;
 
            super.initTextures(context, view);
 
            w = _textureWidth >> _strength;
            h = _textureHeight >> _strength;
            if (w < 1)
                w = 1;
            if (h < 1)
                h = 1;
 
            _shrinkTexture = context.createTexture(w, h, Context3DTextureFormat.BGRA, true);
 
        }
 
        private function initProgram(context:Context3D):void {
            _shrinkProgram3D = context.createProgram();
            _shrinkProgram3D.upload(new AGALMiniAssembler(Debug.active).assemble(Context3DProgramType.VERTEX, getVertexCode()), new AGALMiniAssembler(Debug.active).assemble(Context3DProgramType.FRAGMENT, getShrinkFragmentCode()));
            //
            _compositeProgram3D = context.createProgram();
            _compositeProgram3D.upload(new AGALMiniAssembler(Debug.active).assemble(Context3DProgramType.VERTEX, getVertexCode()), new AGALMiniAssembler(Debug.active).assemble(Context3DProgramType.FRAGMENT, getCompositeFragmentCode()));
        }
 
        protected function getVertexCode():String {
            return "mov op, va0\n" + "mov v0, va1";
        }
 
        protected function getShrinkFragmentCode():String {
            var code:String;
            code = "mov ft0, v0    \n";
            code += "tex ft0 ft0 fs0<2d,linear,clamp>\n";
            code += "mov oc, ft0\n";
            return code;
        }
 
        protected function getCompositeFragmentCode():String {
            var code:String;
            code = "tex ft0, v0, fs0 <2d,linear,clamp>\n"
            code += "tex ft1, v0, fs1 <2d,linear,clamp>\n"
            code += "add oc, ft0, ft1\n"
            return code;
        }
    }
 
}

まず大事なことはfragmentシェーダの出力先は1つしか設定できないことだ。

テクスチャに描画する場合、当然バックバッファに描画+テクスチャにも描画ということはできないし、テクスチャ2枚に描画することもできない。

 

そしてテクスチャに出力できるのがfragmantシェーダの最後の処理である以上、"テクスチャに縮小したスクリーンを描画し、それを拡大して加算"の処理は1つのシェーダでは実現できない。

よって今回は

  1. 入力テクスチャを縮小して一時テクスチャに描画
  2. 補間拡大した一時テクスチャと入力テクスチャを加算合成

の2つのシェーダプログラムが必要になる。

 

 

override protected function initTextures(context:Context3D, view:View3D):void {
    var w:int;
    var h:int;
 
    super.initTextures(context, view);
 
    w = _textureWidth >> _strength;
    h = _textureHeight >> _strength;
    if (w < 1)
        w = 1;
    if (h < 1)
        h = 1;
 
    _shrinkTexture = context.createTexture(w, h, Context3DTextureFormat.BGRA, true);
 
}

まずは一時保存用のテクスチャを作る。スーパークラスでinitTexturesが呼ばれ、そこで画面サイズも得られるのでoverrideして縮小テクスチャもそこで作っておく。

strengthはコンストラクタで決める2以上の整数で、縮小、拡大の際の1辺の長さの倍率だ。大きければ大きいほどキラキラも拡大されるが抜け落ちる情報も多くなる。

両辺1/strengthにしたテクスチャを作る。この際、第四引数をtrueにしておく。この第四引数は作成したテクスチャに描画することがあるかどうかで、trueにされると最適化されるようだ。

この_shrinkTextureに描画し、拡大する。

 

 

protected function getShrinkFragmentCode():String {
    var code:String;
    code = "mov ft0, v0    \n";
    code += "tex ft0 ft0 fs0<2d,linear,clamp>\n";
    code += "mov oc, ft0\n";
    return code;
}
 
protected function getCompositeFragmentCode():String {
    var code:String;
    code = "tex ft0, v0, fs0 <2d,linear,clamp>\n"
    code += "tex ft1, v0, fs1 <2d,linear,clamp>\n"
    code += "add oc, ft0, ft1\n"
    return code;
}

縮小するシェーダの_shrinkProgram3Dと合成するシェーダの_compositeProgram3Dを作るが、vertexシェーダは共通のものを使う。

getShrinkFragmentCodeではテクスチャを読み込み、そのまま出力するだけのコードだが、出力対象を縮小したテクスチャにsetRenderToTextureすることで勝手に縮小してくれる。

getCompositeFragmentCodeでは1枚のテクスチャを読み込み、それぞれRGB成分足して出力するコードだが、_shrinkTextureは拡大する際に補間される。

 

 

stage3DProxy.setProgram(_shrinkProgram3D);
context.setRenderToTexture(_shrinkTexture, false, 0, 0);
context.clear(0.0, 0.0, 0.0, 1.0);
context.setVertexBufferAt(0, _vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_2);
context.setVertexBufferAt(1, _vertexBuffer, 2, Context3DVertexBufferFormat.FLOAT_2);
stage3DProxy.setTextureAt(0, _inputTexture);
context.drawTriangles(_indexBuffer, 0, 2);
 
if (target)
    context.setRenderToTexture(target, false, 0, 0);
else
    context.setRenderToBackBuffer();
 
stage3DProxy.setProgram(_compositeProgram3D);
context.clear(0.0, 0.0, 0.0, 1.0);
stage3DProxy.setTextureAt(0, _inputTexture);
stage3DProxy.setTextureAt(1, _shrinkTexture);
context.drawTriangles(_indexBuffer, 0, 2);
 
stage3DProxy.setTextureAt(0, null);
stage3DProxy.setTextureAt(1, null);
stage3DProxy.setSimpleVertexBuffer(0, null);
stage3DProxy.setSimpleVertexBuffer(1, null);

 

実際のレンダリングの部分だが、まず_shrinkProgram3Dをセットし、出力対象を_shrinkTextureにする。
元の画像は_inputTextureに入っているのでこれを0版のsamplerにセットして描写する。

次に、レンダリング対象を次のフィルタがあればそのフィルタの_inputTextureに、なければバックバッファにし、_compositeProgram3Dをセットする。

加算する元の_inputTextureと縮小された_shrinkTextureをsamplerにセットし、描画する。

最後に後かたづけだ。Away3Dに限らず、setTextureAtでsamplerにセットしてあるテクスチャを次のシェーダプログラムで使わないと、実行時にエラーが投げらので、使ったテクスチャのsampler番号にsetTextureAtでnullを入れる。
これハマりやすく、エラーの原因を探すのに無駄に悩むことになるので注意。

 

 

 

 

あーちなみにBloomFilter3Dとの違いがわからない。効果は似たような感じだけどあっちはどんな処理やってるのか読んでない

 

 

これで複数枚のテクスチャの扱い、テクスチャに一時的に出力して複数のシェーダプログラムを使うことに慣れたはずだ。

最後にモザイク効果をかけるMosaicFilter3Dだ。

モザイク画像は、画像をあるピクセル単位で分割し、その単位内のピクセルカラーの平均を取り、単位内すべてのピクセルを平均カラーで埋めることでできる。

 

 

ところで、fragmentシェーダで処理中のピクセルから他のピクセルにアクセスするにはどうしたらいいだろう。

普通のstageならforループでBitmapDataをgetPixelすればいいのだが、Stage3Dではこれができない。

fragmentシェーダの各ピクセルでの処理はGPUが持つ複数のコアで並列に行われるからだ。むしろそのためのハードウェアがグラフィックボードと言ってもいいくらい。

並列に実行される以上、処理に依存関係があってはならない。なのでfragmentシェーダ実行中に他のピクセルにアクセスすることはできないというわけだ。

 

 

fragmentシェーダがアクセスできるのは、そのピクセルに割り当てられたvaryingレジスタの値と、それをuv座標として読み込んだテクスチャの該当ピクセル、constantレジスタの定数のみだ。

ならばuv座標をずらして同じテクスチャをサンプリングすることで他のピクセルにアクセスできるのではないか。

具体的にはuv座標はテクスチャのサイズに対応した0~1までの値なので、1/テクスチャの幅 だけu座標に足してからテクスチャを読み込めば、1ピクセル右のテクスチャカラーを読み込むことができる。

同じテクスチャを何度も読み込むことはできるので、uv座標をずらして複数回読み込みなおすことでモザイクのピクセルカラーの平均化処理をすることができる。

 

 

MosaicFilter3D

package {
    import away3d.cameras.Camera3D;
    import away3d.containers.View3D;
    import away3d.core.managers.Stage3DProxy;
    import away3d.debug.Debug;
    import away3d.filters.Filter3DBase;
    import com.adobe.utils.AGALMiniAssembler;
    import flash.display3D.Context3D;
    import flash.display3D.Context3DProgramType;
    import flash.display3D.Context3DTextureFormat;
    import flash.display3D.Context3DVertexBufferFormat;
    import flash.display3D.Program3D;
    import flash.display3D.textures.Texture;
 
    /**
     * ...
     * @author
     */
    public class MosaicFilter3D extends Filter3DBase {
        private var _numPixel:uint;
        private var _simpleMosaic:Boolean;
        private var _mosaicTexture:Texture;
        private var _averageProgram3D:Program3D;
        private var _mosaicProgram3D:Program3D;
 
        public function MosaicFilter3D(numPixel:uint = 4, simpleMosaic:Boolean = false){
            if (numPixel < 1){
                _numPixel = 1;
            } else {
                _numPixel = numPixel;
            }
            _simpleMosaic = simpleMosaic;
            super(false);
        }
 
        override public function render(stage3DProxy:Stage3DProxy, target:Texture, camera:Camera3D, depthRender:Texture = null):void {
            var context:Context3D = stage3DProxy.context3D;
 
            super.render(stage3DProxy, target, camera);
 
            if (!_averageProgram3D)
                initProgram(context);
 
            stage3DProxy.setProgram(_averageProgram3D);
            context.setVertexBufferAt(0, _vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_2);
            context.setVertexBufferAt(1, _vertexBuffer, 2, Context3DVertexBufferFormat.FLOAT_2);
 
            if (!_simpleMosaic){
                var tmp:Texture;
                var wh:uint;
                for (var i:int = 0; i < _numPixel; i++){
                    wh = 1 << i;
                    context.setRenderToTexture(_mosaicTexture);
                    context.clear(0.0, 0.0, 0.0, 1.0);
                    context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, Vector.<Number>([wh / _textureWidth, wh / _textureHeight, 4, 0]), 1);
                    stage3DProxy.setTextureAt(0, _inputTexture);
                    context.drawTriangles(_indexBuffer, 0, 2);
                    tmp = _inputTexture;
                    _inputTexture = _mosaicTexture;
                    _mosaicTexture = tmp;
                }
            }
 
            if (target)
                context.setRenderToTexture(target, false, 0, 0);
            else
                context.setRenderToBackBuffer();
 
            stage3DProxy.setProgram(_mosaicProgram3D);
            context.clear(0.0, 0.0, 0.0, 1.0);
            context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, Vector.<Number>([_textureWidth, _textureHeight, 1 << _numPixel, 0]), 1);
            stage3DProxy.setTextureAt(0, _inputTexture);
            context.drawTriangles(_indexBuffer, 0, 2);
 
            stage3DProxy.setTextureAt(0, null);
            stage3DProxy.setSimpleVertexBuffer(0, null);
            stage3DProxy.setSimpleVertexBuffer(1, null);
        }
 
        private function initProgram(context:Context3D):void {
            _averageProgram3D = context.createProgram();
            _averageProgram3D.upload(new AGALMiniAssembler(Debug.active).assemble(Context3DProgramType.VERTEX, getVertexCode()), new AGALMiniAssembler(Debug.active).assemble(Context3DProgramType.FRAGMENT, getAverageFragmentCode()));
            _mosaicProgram3D = context.createProgram();
            _mosaicProgram3D.upload(new AGALMiniAssembler(Debug.active).assemble(Context3DProgramType.VERTEX, getVertexCode()), new AGALMiniAssembler(Debug.active).assemble(Context3DProgramType.FRAGMENT, getMosaicFragmentCode()));
        }
 
        override protected function initTextures(context:Context3D, view:View3D):void {
            super.initTextures(context, view);
            _mosaicTexture = context.createTexture(_textureWidth, _textureHeight, Context3DTextureFormat.BGRA, true);
        }
 
        protected function getVertexCode():String {
            return "mov op, va0\n" + "mov v0, va1";
        }
 
        protected function getAverageFragmentCode():String {
            var code:String;
            code = "";
            code += "tex ft0 v0 fs0<2d,linear,clamp>\n";
            code += "mov ft1 v0\n";
            code += "add ft1.x ft1.x fc0.x\n";
            code += "tex ft1 ft1 fs0<2d,linear,clamp>\n";
            code += "add ft0, ft0, ft1\n";
            code += "mov ft1 v0\n";
            code += "add ft1.y ft1.y fc0.y\n";
            code += "tex ft1 ft1 fs0<2d,linear,clamp>\n";
            code += "add ft0, ft0, ft1\n";
            code += "mov ft1 v0\n";
            code += "add ft1.xy ft1.xy fc0.xy\n";
            code += "tex ft1 ft1 fs0<2d,linear,clamp>\n";
            code += "add ft0, ft0, ft1\n";
            code += "div ft0, ft0, fc0.z\n";
            code += "mov oc, ft0\n";
            return code;
        }
 
        protected function getMosaicFragmentCode():String {
            var code:String;
            code = "";
            code += "mul ft0 v0, fc0\n";
            code += "div ft1.xy ft0.xy, fc0.z\n";
            code += "frc ft1.xy ft1.xy\n";
            code += "mul ft1.xy ft1.xy, fc0.z\n";
            code += "sub ft0.xy ft0.xy, ft1.xy\n";
            code += "div ft0.xy ft0.xy, fc0.xy\n";
            code += "tex ft0 ft0 fs0<2d,linear,clamp>\n";
            code += "mov oc, ft0\n";
            return code;
        }
    }
 
}

先ほどと同じようにinitTexturesで、一時的に平均化した画像を保存する_mosaicTextureを今度は同じサイズで作る。

 

 

protected function getAverageFragmentCode():String {
    var code:String;
    code = "";
    code += "tex ft0 v0 fs0<2d,linear,clamp>\n";
    code += "mov ft1 v0\n";
    code += "add ft1.x ft1.x fc0.x\n";
    code += "tex ft1 ft1 fs0<2d,linear,clamp>\n";
    code += "add ft0, ft0, ft1\n";
    code += "mov ft1 v0\n";
    code += "add ft1.y ft1.y fc0.y\n";
    code += "tex ft1 ft1 fs0<2d,linear,clamp>\n";
    code += "add ft0, ft0, ft1\n";
    code += "mov ft1 v0\n";
    code += "add ft1.xy ft1.xy fc0.xy\n";
    code += "tex ft1 ft1 fs0<2d,linear,clamp>\n";
    code += "add ft0, ft0, ft1\n";
    code += "div ft0, ft0, fc0.z\n";
    code += "mov oc, ft0\n";
    return code;
}

getAverageFragmentCodeで平均化の部分のfragmentシェーダを作る。

v0にはuv座標、fc0には[1 / _textureWidth, 1 / _textureHeight, 4, 0]が入っていると考えると、

  1. ft0にv0をuv座標としてテクスチャカラーをサンプリング
  2. ft1にv0をコピー
  3. ft1.xにfc0.xを足す(u座標に1/w足す)
  4. ft1にft1をuv座標としてテクスチャカラーをサンプリング(uが1ピクセルずれて取得される)
  5. ft0にft1のカラーを足す(ft1はもう用済み)
  6. ft1にv0をコピー
  7. ft1.yにfc0.yを足す(v座標に1/h足す)
  8. ft1にft1をuv座標としてテクスチャカラーをサンプリング(vが1ピクセルずれて取得される)
  9. ft0にft1のカラーを足す(ft1はもう用済み)
  10. ft1にv0をコピー
  11. ft1.xyにfc0.xyを足す(u座標に1/w、v座標に1/h足す)
  12. ft1にft1をuv座標としてテクスチャカラーをサンプリング(uvが1ピクセルずつずれて取得される)
  13. ft0にft1のカラーを足す
  14. ft0をfc0.zで割る(4で割って平均化)
  15. 出力

という処理になる。

ここで、右端と下端のピクセルは4で割ってはいけなかったりするのだが今回は無視。使わないしね。

これで各ピクセルは1ピクセル右、1ピクセル下、1ピクセルずつ右下、のピクセルカラーの平均が出力テクスチャに描画された。

 

このままバックバッファに出力しないのは、これがモザイク画像ではないから。

本来なら4ピクセルの正方形が同じ平均色でなくてはならないのだが、そうなっていない。

(0,0)のピクセルには(0,0)(1,0)(0,1)(1,1)の平均色が
(1,0)のピクセルには(1,0)(2,0)(1,1)(2,1)の平均色が
(0,1)のピクセルには(0,1)(1,1)(0,2)(1,2)の平均色が
(1,1)のピクセルには(1,1)(2,1)(1,2)(2,2)の平均色が

それぞれ入っているが、これら分割された単位としての4つのピクセルは全て(0,0)の色を入れるべきだ。なので_mosaicProgram3Dでこの処理を行う。

 

 

protected function getMosaicFragmentCode():String {
    var code:String;
    code = "";
    code += "mul ft0 v0, fc0\n";
    code += "div ft1.xy ft0.xy, fc0.z\n";
    code += "frc ft1.xy ft1.xy\n";
    code += "mul ft1.xy ft1.xy, fc0.z\n";
    code += "sub ft0.xy ft0.xy, ft1.xy\n";
    code += "div ft0.xy ft0.xy, fc0.xy\n";
    code += "tex ft0 ft0 fs0<2d,linear,clamp>\n";
    code += "mov oc, ft0\n";
    return code;
}

今度はv0にはuv座標、fs0には先ほどの平均化されたテクスチャ、fc0には[_textureWidth, _textureHeight, 2 , 0]が入っていると考えると、

  1. v0にfc0をかけたものをft0に入れる(u座標は_textureWidth倍、v座標は_textureHeight倍される、つまりuv座標が0~1からピクセル単位に戻る)
  2. ft1.xyにft0.xyをfc0.zで割った値を入れる(2で割るので10ピクセル目なら5、11ピクセル目なら10.5になる)
  3. ft1.xyの小数部をとる(もとが10なら0、11なら0.5)
  4. ft1.xyにfc0.zをかける(もとが10なら0、11なら1)
  5. ft0.xyからft1.xyを引く(もとが10なら10、11なら10、つまりint(u/2)*2のようになり、意図したピクセルになる)
  6. ft0.xyをfc0.xyで割る(元のuv範囲に戻る)
  7. ft0にft0をuv座標としてテクスチャカラーをサンプリング
  8. 出力

この処理でfc0.zを一辺とする正方形の範囲がすべて正方形の一番左上のピクセルカラーになる。

実は平均をとらなくてもこの処理だけで多少モザイクっぽく見えるので、高速化のためにこの処理だけを行うモザイクフィルタもコンストラクタで選べるようにしてみた。

 

 

実際のレンダリング処理では、まず平均をとる。

stage3DProxy.setProgram(_averageProgram3D);
context.setVertexBufferAt(0, _vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_2);
context.setVertexBufferAt(1, _vertexBuffer, 2, Context3DVertexBufferFormat.FLOAT_2);
 
if (!_simpleMosaic){
    var tmp:Texture;
    var wh:uint;
    for (var i:int = 0; i < _numPixel; i++){
        wh = 1 << i;
        context.setRenderToTexture(_mosaicTexture);
        context.clear(0.0, 0.0, 0.0, 1.0);
        context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, Vector.<Number>([wh / _textureWidth, wh / _textureHeight, 4, 0]), 1);
        stage3DProxy.setTextureAt(0, _inputTexture);
        context.drawTriangles(_indexBuffer, 0, 2);
        tmp = _inputTexture;
        _inputTexture = _mosaicTexture;
        _mosaicTexture = tmp;
    }
}

_averageProgram3Dと4頂点をセットし、_simpleMosaicでなければ_numPixelぶん平均化レンダリングを繰り返す。

_numPixelはコンストラクタで指定し、一辺が2の_numPixel乗ピクセルの正方形のモザイクになる。

処理が面倒なので今回はモザイクのサイズを2の乗数ピクセル限定にしてしまった。

whは1*2^iで、何ピクセル隣のピクセルカラーを平均化に使うかだ。

最初はwh=1なので先ほど説明したように平均化される。

次はwh=2なので1ピクセルとばして2ピクセル隣のピクセルカラーを使う。この2ピクセル隣もすでに周囲4ピクセルの平均なので、これらを使うことで周囲16ピクセルの平均をとったことになる。

 

_mosaicTextureに出力するように設定し、_inputTextureをfs0に設定しているので、ループの最後にこれらのテクスチャの参照を入れ替えている。

これで常にループ内では使うテクスチャが前回出力されたテクスチャに設定される。参照が変わっているので毎回セットしなおすことを忘れずに。

 

 

if (target)
    context.setRenderToTexture(target, false, 0, 0);
            else
    context.setRenderToBackBuffer();
 
stage3DProxy.setProgram(_mosaicProgram3D);
context.clear(0.0, 0.0, 0.0, 1.0);
context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, Vector.<Number>([_textureWidth, _textureHeight, 1 << _numPixel, 0]), 1);
stage3DProxy.setTextureAt(0, _inputTexture);
context.drawTriangles(_indexBuffer, 0, 2);
 
stage3DProxy.setTextureAt(0, null);
stage3DProxy.setSimpleVertexBuffer(0, null);
stage3DProxy.setSimpleVertexBuffer(1, null);

次のフィルタの有無で出力先をバックバッファかテクスチャか選び、_mosaicProgram3Dをセットする。

先ほどのループでは最終的に出力されたテクスチャは_inputTextureに入っているのでこれをfs0として使う。

fc0.zに1 << _numPixelを入れることで、何ピクセル四方にわたって同じピクセルカラーを使うかを指定する。

で最後に後かたづけ。

 

 

 

 

uv座標にn/wを加えることでnピクセルずらしてアクセスすることができるようになった。

これと今までやってきた、複数シェーダを使う、複数テクスチャを使う、カラーのRGBにそれぞれ違う処理をする、を合わせればアイディア次第でかなり幅広い種類のフィルタが作れるようになると思う。

また、Filter3DBaseの仕組みを真似すればAway3Dに限らずStage3Dのフィルタ効果が作れる。

 

 

RGBをHSVに変換する方法を考え中だが、元の式をAGALオペコードだけで再現するのはかなり大変そうだ…。

 

 

スポンサーサイト



  1. 2011/07/26(火) 20:53:20|
  2. Away3D 4
  3. | トラックバック:0
  4. | コメント:14
<<Molehill Stage3D API リファレンスカード | ホーム | Away3D 4.0 カスタムフィルタの作り方(前編)>>

コメント

はじめまして。

Flash Player 11に関連したサイトを廻っていてたどり着いたものです。
私はFlash player 10でPaperVision3Dでゲームを開発している途中にFlash Player 11の存在を知り、移植と一緒に勉強を始めました。
このブログではFlash Player 11のかなり詳しいところまで触れられているので、参考にさせていただいています。
というか、日本語のサイトの中で一番詳しいんじゃないのかとも思っていますw

それはさておき、私もFlashPlayer11への移行をしていたのですが、
移行したのがAlternativa3Dだったのですよね。
しかし、9ballsynさんもおそらくご存じだと思いますが、Alternativa3DはFlash Player 11 Betaに対応をしていないわけで・・・。
このままではソフトが作れない!という状況なのです。

Flashplayer11betaの公開から2週間以上経ちましたが、Alternativa3Dのフォーラムからは何の応答も無いので正直困っています。
そんなときにこのブログで、Away3DはBeta版に対応しているというのを目にしたのですが、
ぶっちゃけ、Away3Dの使い心地ってどのような感じでしょうか?
私はシステムでColladaとかPlaneとかを使っていましたが、Away3Dでも当然そのようなものは使えるのですよね?
ただ、まだAway3Dも不完全なものなようなので、どこまで使えるのかというのも気になります。
Alternativa3D8.8のColladaは、UVマップのシステムが未完成でしたし、Away3Dでもそのようなものはありますかね。

Alternativa3Dの更新があまりにも遅いので(というか無いような気さえもする)、Away3Dに移行しようか悩んでいるのですが、
踏ん切りがつかないという現状です。
Away3Dに関する知識は全くないので申し訳ありませんが、よろしければ意見をお聞かせください。
  1. 2011/07/29(金) 23:45:44 |
  2. URL |
  3. シャイん #-
  4. [ 編集 ]

はじめまして。
勝手に推理しながら書いている記事を読んでくださってありがとうございます。
後々発覚した間違った部分を修正しなくてはと常々思っています。

Alternativa3Dですが、フォーラムを1日3回、ロシア語の方まで翻訳してチェックする毎日です。
開発者の方は2,3日中にアップデートすると仰っていたのに。
最近は弄れないので低レベルな方の勉強を初めることができてまあいい機会だなと思っていましたがさすがに長いですね。
大幅な機能向上はいいのでとりあえずviewport周りの修正だけしてほしいです。


Away3D4.0は少しさわった程度ですが、Papervision3Dとなかなか似通った部分が多いように感じました。
クラスの数もAlternativa3Dと比べ多く、多機能だと思います。
Plane等の基本的なプリミティブも一通り揃っています。

3Dモデルを作った経験がないので、そちら周りには疎くてすみません。
Colladaのパースですが、現状未実装のようです。リファレンスにはあるので実装予定ではあると思います。
どちらのモデリングソフトを使われているかわかりませんが、BlenderならAway3D4.0で読み込める形式にできるようです。
インキュベータの時点ですが、透過度をサポートしたテクスチャにするとソーティングがおかしくなる、といった問題もあるようです。( http://www.96neco.com/blog/log/188 )

Alternativa3Dと違ってStage3DやContext3Dに直接触ったり、リソースを明示的にアップロードするようなことがないようで、パっと見では前時代?の3Dライブラリと同じように使えそうです。
深いところをいじるならStage3Dの知識は必要でしょうが。

asファイルでの配布がありますので、ソースを直接見ることができるのも利点かと思います。

フォーラムも活発なようですし、PV3Dからならスムーズに移行できるのではないでしょうか。


アップデート来ても自分側のソースはほぼ修正ないだろうとタカをくくって
plaerglobal.swcとflash playerをインキュベーターに戻してAlternativa3Dで進めるという手もあるかもしれません。
  1. 2011/07/30(土) 19:21:54 |
  2. URL |
  3. 9ballsyn #-
  4. [ 編集 ]

返信ありがとうございます。
Away3DをDLして、中身を見てみました。
Alternativa3Dと比べてClassが沢山ありますね。
確かに、
実際にソースを見れるのもいいと思いました。

ただ、このソースを前提に弄っていると、更新の時にトラブルが起こったりしそうですね。
extendsするのが怖い・・・。

未実装だということを聞いて、慌ててColladaParser()を確認しました。
ColladaParserには以下のようなソースがあったのですが、
switch (input_xml.@semantic.toString()) {
case 'VERTEX':
if (source_xml.localName().toString() == 'vertices')
source_xml = resolve(source_xml.input.@source.toString(), geom_xml.mesh[0]);
verts = parseVertexSourceVector(source_xml);
break;
case 'NORMAL':
norms = parseVertexSourceVector(source_xml);
break;
}
実際のcolladaの形式では、inputタグで、'VERTEX'と'NORMAL'と、'TEXCOORD'(テクスチャのUVマップ)の3つのパラメータがあるのですよね。
見た感じTEXCORDは読み込んでいないようなので、テクスチャを利用してないColladaのデータ以外読み込めなそうですね。
実際に動作確認はしていませんが。
TEXCORDを読み込めないのはAlternativa3Dも同じだったので、今慌ててAway3Dに変更することもないかなあ。

>透過度をサポートしたテクスチャにするとソーティングがおかしくなる
以前、Alternativa3Dでもソートがおかしくなるという記事を書かれておりましたよね?
もしかしたらインキュベーター本体のバグ?

Away3Dに移すメリットがいまいち無いような感じもするので、
とりあえずインキュベーター版にダウングレードしてみようと思います。

ただ、Away3Dの方がユーザー数が多いみたいですね。
日本語書籍でもAway3Dがちょくちょくありますし。
Alternativa3D8は日本語サイトが無くて大変だったというのに。
PaperVision3Dの次にAlternativa3Dが早いという話を聞いて始めたのですが、
使ってる人がAway3Dより少ないことに驚きました。

しかし、私も毎日Alternativa3Dのサイトをチェックしているのですが、
全く応答が無いですよね。
ロシア語、英語のフォーラムでも更新遅いって書き込まれてますし(機械翻訳での確認ですが
早く更新されないかなあ・・。。
  1. 2011/07/30(土) 21:38:30 |
  2. URL |
  3. シャイん #-
  4. [ 編集 ]

ColladaParserあったっけ??と思ったら以前の4.0には入ってたんですね
こちら( http://away3d.com/forum/viewthread/269/ )のaway3d-core-fp11がβ対応で、こちらには入ってなかったので未実装かと思っていました。名前が変わったとかかな?

どちらもまだUV座標がモデルから読めないとのことですが、デモのは違うのでしょうか?(よくわからずに聞いてすみません)


Alternativaの透明度の話はこちら( http://forum.alternativaplatform.com/posts/list/7524.page )ですね。realized yetだったので後回しにしてるのかな。
こちらの質問者さんは日本人の方でAlternativa3D(Stage3Dではありませんが)の日本語TIPS書かれてる方だったり。


以前の勢力図がPV>Away>Alternativaだと思うので、Stage3Dにシフトする時はPV以外の人はそのまま上位版にシフトして、PVの人は2つ(あるいはそれ以上)に割れて散る感じでしょうからAway3Dの方が多くなりそう。
Alternativaは開発者がロシアの方?でフォーラムも2つに割れてしまってますし自分が開発者だったらやっぱり自国語のほうをよく見ると思うんですよね。そんなところもあるのかも。

Away3Dにしても3.5.3で仕様が大きく変更されてたりするようなので4.0ではあてにならない資料も多いかと。
どちらにせよStage3D版の日本語サイトはまだ見かけませんし。


フォーラムのみんなも痺れを切らしてますね。
まあ開発者も商売ではないようですし仕方ないのかも。
開発者のTwitterとかあるのかな。
  1. 2011/07/30(土) 22:59:37 |
  2. URL |
  3. 9ballsyn #-
  4. [ 編集 ]

あ、古い方のaway3dを入れてしまっていたようです。
ご指摘ありがとうございます。

しかし・・・。colladaの文字もなくなっていますね。非対応になってしまったのでしょうか。

>こちらの質問者さんは日本人の方でAlternativa3D(Stage3Dではありませんが)の日本語TIPS書かれてる方だったり。
日本語TIPSを書かれてる方もAlternativa3Dをやっていたのですね。
Alternativa3Dをやってみようかなと思うきっかけを作ってくれた方でした。
ただ、勉強し始めた時にはかなりライブラリが変わっていましたが・・・。


>どちらもまだUV座標がモデルから読めないとのことですが、デモのは違うのでしょうか?(よくわからずに聞いてすみません)
私がやった時のことですが、
Alternativa3DのParserColladaクラスで、parse()したときにエラーが出ました。


collada内部の話になるので、こちらのサイトの一部を引用しますね。
http://ameblo.jp/take-shix/entry-10562553314.html

<triangles count="2" material="cube_jpg" >
<input offset="0" semantic="VERTEX" source="#Cube_007-Vertex"/ >
<input offset="1" semantic="NORMAL" source="#Cube_007-Normals"/ >
<input offset="2" semantic="TEXCOORD" source="#Cube_007-UV"/ >
<p >1 5 20 5 5 21 2 5 22 5 6 23 6 6 24 2 6 25 </p >
</triangles >
このように記述されているColladaファイルをparseしたときに、何故か「オフセットが1を超えています」というエラーメッセージが出るのです。
オフセットの数は2なんだから当たり前だろ!と思ってしまいましたが・・・。
仕方がないのでプログラム中にXMLでsemantic="TEXCORD"は削除して<p>の中身を減らす処理を組み込んだら、エラーは出なくなりました。

<triangles count="2" material="cube_jpg" >
<input offset="0" semantic="VERTEX" source="#Cube_007-Vertex"/ >
<input offset="1" semantic="NORMAL" source="#Cube_007-Normals"/ >
<p >1 5 5 5 2 5 5 6 6 6 2 6 </p >
</triangles >
↑このような記述であれば問題もなく読み取れるようです。
ポリゴンの設定のTEXCORDを消しただけで、
実際のUVマップの座標数値や画像のURLを消したわけでないのにロードできてしまうというのが不思議なところなのですが。
(しかもURLのある画像はダウンロードされるから、わけがわからない)


ただ、使用しているcolladaのデータは、PaperVision3Dでも、Alternativa3D7.8でも読み込めたものなので、
ロードできない原因はswcの中にあると思うのですが・・・。


デモに使われている3Dファイルは3DS Maxのものじゃないのですかね?
ただ、私はあまりお金が無いので3DS Maxは買えないんで、実際のところはどうなのか確かめられませんが。。。
  1. 2011/07/31(日) 01:33:07 |
  2. URL |
  3. シャイん #-
  4. [ 編集 ]

↑オフセットの数が2を超えています というエラーだったかもしれません。
何しろ3週間以上前のようなので・・・。
ただTEXCOORDを消したら動いたというのは事実です
  1. 2011/07/31(日) 01:45:39 |
  2. URL |
  3. シャイん #-
  4. [ 編集 ]

久々に読み返しましたが、デモのやつはA3DとDAE両方とも読み込めているようです。
http://9ballsyndrome.blog.fc2.com/blog-entry-4.html


同じColladaでも出力ソフトによって微妙に形式違ったりする気がします。3DS MAXで出力したColladaなのかな
お持ちのモデルがAlternativa内部のパーサー的に必要なものが記述されていないのかも。
Alternativaのparsersexampleサンプルに付属のmodel.DAEの中身をみて違いを探してみたり。
  1. 2011/07/31(日) 11:41:57 |
  2. URL |
  3. 9ballsyn #-
  4. [ 編集 ]

parsersexampleの中身を確認しないでプログラムを打ち込んでました・・・。
dae表示システムのコードは普通に公開されてたんですね。

今のままではテストしようがないので、インキュベーター版にダウングレードして開発再開しました。
これで、32BitIEはインキュベーター版、64BitIEはβ版になりました。
普通のサイトは64bit版を使えば、何とか開発はできそうです。

先ほどのエラーメッセージの内容は、以下のような感じです。
RangeError: Error #1125: インデックス 2 は 2 の範囲外です。
at alternativa.engine3d.loaders.collada::DaePrimitive/isEqual()[G:\works\Alternativa3D\src\alternativa\engine3d\loaders\collada\DaePrimitive.as:244]
at alternativa.engine3d.loaders.collada::DaePrimitive/fillGeometry()[G:\works\Alternativa3D\src\alternativa\engine3d\loaders\collada\DaePrimitive.as:213]
at alternativa.engine3d.loaders.collada::DaeGeometry/parseImplementation()[G:\works\Alternativa3D\src\alternativa\engine3d\loaders\collada\DaeGeometry.as:58]
at alternativa.engine3d.loaders.collada::DaeElement/parse()[G:\works\Alternativa3D\src\alternativa\engine3d\loaders\collada\DaeElement.as:44]
at alternativa.engine3d.loaders.collada::DaeNode/parseObjects()[G:\works\Alternativa3D\src\alternativa\engine3d\loaders\collada\DaeNode.as:251]
at alternativa.engine3d.loaders.collada::DaeNode/parseImplementation()[G:\works\Alternativa3D\src\alternativa\engine3d\loaders\collada\DaeNode.as:133]
at alternativa.engine3d.loaders.collada::DaeElement/parse()[G:\works\Alternativa3D\src\alternativa\engine3d\loaders\collada\DaeElement.as:44]
at alternativa.engine3d.loaders::ParserCollada/parseNodes()[G:\works\Alternativa3D\src\alternativa\engine3d\loaders\ParserCollada.as:192]
at alternativa.engine3d.loaders::ParserCollada/parseNodes()[G:\works\Alternativa3D\src\alternativa\engine3d\loaders\ParserCollada.as:223]
at alternativa.engine3d.loaders::ParserCollada/parseNodes()[G:\works\Alternativa3D\src\alternativa\engine3d\loaders\ParserCollada.as:223]
at alternativa.engine3d.loaders::ParserCollada/parse()[G:\works\Alternativa3D\src\alternativa\engine3d\loaders\ParserCollada.as:122]
at ParsersExample/onColladaLoad()[C:\Users\Shine\Desktop\Alternativa3D_8.8.0\Alternativa3DExamples\src\parsersexample\ParsersExample.as:277]


parsersexampleの表示システムではTEXCOORDで正常に表示されているので、色々と試してみました。
複数のcolladaファイルで実験していると、正常にパースできるテクスチャとパースできないテクスチャがありました。
それで中身を見比べてみたら、原因がなんとなくわかってみました。

1つのnode(オブジェクト)に1つのTextureMaterialがある場合→正常にパースできる
1つのnodeに複数のTextureMaterialがある場合→正常にパースできる
1つのnodeに複数のFillMaterialがある場合→正常にパースできる
1つのnodeに複数のFillMaterialとTextureMaterialが混在する場合→エラーが出る

検証結果ではこんな感じでした。同一のnodeではテクスチャのみを使うようにすれば大丈夫のようです。
・・・というか、これって明らかにバグですよね?
フォーラムで報告したほうがいいのでしょうか。
というか既にこのバグが報告されてるのか確かめるのも大変そうなのですが。
  1. 2011/08/01(月) 22:56:08 |
  2. URL |
  3. シャイん #-
  4. [ 編集 ]

なるほど。
FillMaterialとTextureMaterialというのはAlternativa3D側のマテリアルの話ですよね。
そのマテリアルの種類の選択は勝手にAlternativa3D内部でされるのですか?
Collada側でそういった記述があるのでしょうか



FillMaterialってわざわざ一色のテクスチャを作ってるのかな。
  1. 2011/08/02(火) 18:55:44 |
  2. URL |
  3. 9ballsyn #-
  4. [ 編集 ]

すみません、間違っていました。
一色で塗りつぶされてたのでFillMaterialかと思いましたが、よくよく調べてみると、ParserMaterialになっていたようです。
ParserMaterialでもFillMaterialみたいな挙動できるんですね。
まあ、parser().materialsを書き換えればいいだけなのですが・・・。

>FillMaterialってわざわざ一色のテクスチャを作ってるのかな。
これはあくまで私のやり方なのですが、
顔にはテクスチャを使っているのですが、テクスチャの肌の色は塗りつぶしなので、特にパーツの無い肌の部分は肌色のFillMaterialを使用していました。
肌にグラデーションをかけると、顔の周りはグラデーションがかかってないのに、他の部分がグラデーションがかかっちゃったりするためです。

ところで、3DStageは、2DのStageの下に必ず表示されるんですかね?
Flash11の説明で、そのような風に書かれていたのを見たことはあるのですが・・・。
先ほど3Dグラフィックが表示されない! と困っていたらそれが原因っぽかったので。

でも、そうなると、3DStageの背景に2DのBitmapDataを利用することとかはできないんですかね。
BitmapDataの背景を表示するとしたら、3DStageのアルファ値を読み取って、反転させて2DのBitmapDataにマスクをかけるとかになるんでしょうか。
かなり無理矢理な気がするけど。
  1. 2011/08/04(木) 01:26:51 |
  2. URL |
  3. シャイん #-
  4. [ 編集 ]

>1つのnode(オブジェクト)に1つのTextureMaterialがある場合→正常にパースできる
>1つのnodeに複数のTextureMaterialがある場合→正常にパースできる
>1つのnodeに複数のFillMaterialがある場合→正常にパースできる
>1つのnodeに複数のFillMaterialとTextureMaterialが混在する場合→エラーが出る
についてはどういった条件だったのでしょうか。
自分で指定されていたのですか?


>>FillMaterialってわざわざ一色のテクスチャを作ってるのかな。

についてですが、Stage3Dの一番簡単なデモhttp://9ballsyndrome.blog.fc2.com/blog-entry-22.htmlにあるように各頂点の色を頂点バッファに直接指定することで塗りつぶしならばテクスチャを使わずにできます。
FillMaterialはそうしてるのか、それともわざわざ一色べた塗りのテクスチャを作ってるのかどちらだろう。という意味です。後者のような気がしますが。
暇があったらAway3DのColorMaterial周りを見てみます。


>3DStageのアルファ値を読み取って
各ピクセルごとにアルファ値を読むということですよね。これも一筋縄じゃいかなそうです。

Context3D.drawToBitmapDataでBitmapDataにレンダリング結果を書き出すことができる(重いらしいけど)ので、3DStageに書き出さずにBitmapDataに書き出してそのBitmapDataを2DStageに貼り付けることはできます。
Alternativa3D8ではViewコンストラクタの引数renderToBitmap:Booleanとcanvas:BitmapData [read-only]プロパティが怪しいかんじ。

背景にしたいBitmapDataをテクスチャにして3D世界に持っていくという手もあるかも。

  1. 2011/08/04(木) 16:33:57 |
  2. URL |
  3. 9ballsyn #-
  4. [ 編集 ]

返信遅れてしまいました。

>についてはどういった条件だったのでしょうか。
>自分で指定されていたのですか?
メタセコイアで組んで、http://vixar.jp/motion/index.htmlのソフトで出力したものを使いました。
最初のうちはソフトの問題かと思って調べていたのですが、それにしては何か不自然なエラーだったので・・・。
あるオブジェクトに、テクスチャを入れたポリゴンとテクスチャを入れてないポリゴンを居れたとき、
下のようなColladaコードになります。

~省略~
<geometry id="obj5-lib" name="obj5">
<mesh>
~省略~
<triangles count="40" material="SKINSG">
<input semantic="VERTEX" source="#obj1-lib-vertices" offset="0"/>
<input semantic="NORMAL" source="#obj1-lib-normals" offset="1"/>
<p>1 1 5 5 2 2 1 1 4 4 5 5 4 4 8 8 5 5 4 4 7 7 8 8 7 7 11 11 8 8 7 7 10 10 11 11 10 10 14 14 11 11 10 10 13 13 14 14 12 12 17 17 13 13 13 13 17 17 14 14 15 15 17 17 16 16 19 19 18 18 15 15 19 19 14 14 18 18 19 19 11 11 14 14 19 19 15 15 16 16 20 20 18 18 14 14 20 20 15 15 18 18 20 20 17 17 15 15 22 22 21 21 19 19 23 23 22 22 19 19 23 23 19 19 16 16 27 27 31 31 30 30 27 27 28 28 31 31 12 12 30 30 17 17 30 30 31 31 17 17 32 32 16 16 17 17 35 35 32 32 34 34 35 35 31 31 28 28 35 35 34 34 31 31 35 35 16 16 32 32 36 36 31 31 34 34 36 36 32 32 17 17 36 36 34 34 32 32 38 38 35 35 37 37 38 38 28 28 29 29 38 38 37 37 28 28 39 39 35 35 38 38 39 39 29 29 33 33 39 39 38 38 29 29 39 39 16 16 35 35 </p>
</triangles>
<triangles count="16" material="mat6_2SG">
<input semantic="VERTEX" source="#obj1-lib-vertices" offset="0"/>
<input semantic="NORMAL" source="#obj1-lib-normals" offset="1"/>
<input semantic="TEXCOORD" source="#obj1-lib-map1" offset="2"/>
<p>0 0 0 4 4 2 1 1 1 0 0 3 3 3 5 4 4 4 3 3 12 7 7 14 4 4 13 3 3 15 6 6 17 7 7 16 6 6 24 10 10 26 7 7 25 6 6 27 9 9 29 10 10 28 9 9 36 13 13 38 10 10 37 9 9 39 12 12 41 13 13 40 0 0 87 25 25 89 3 3 88 0 0 90 24 24 92 25 25 91 3 3 93 26 26 95 6 6 94 3 3 96 25 25 98 26 26 97 6 6 99 27 27 101 9 9 100 6 6 102 26 26 104 27 27 103 9 9 105 30 30 107 12 12 106 9 9 108 27 27 110 30 30 109 </p>
</triangles>
</mesh>
</geometry>
~省略~
"SKINSG"のマテリアルはテクスチャを利用してないのでTEXCOORDは無し、mat6_2SGはテクスチャを利用しているのでTEXCOORDがあります。
どうやら、このようにポリゴンの<input>の数が違うColladaでは、パースしたときにエラーが発生するようです。

このファイルを編集して、SKINSGにTEXCOORDを追加した場合と、mat6_2SGのTEXCOORDを削除した場合はパースでエラーが発生しませんでした。
(このファイルは7.8では正常にパースできます。)

>FillMaterialはそうしてるのか、それともわざわざ一色べた塗りのテクスチャを作ってるのかどちらだろう。という意味です。後者のような気がしますが。
一色ベタ塗りだとは思いますが・・・、わかりませんね。
DirectX9的には一色ポリゴンはテクスチャを使わないのが常識だとは思うのですが・・・。

>Alternativa3D8ではViewコンストラクタの引数renderToBitmap:Booleanとcanvas:BitmapData [read-only]プロパティが怪しいかんじ
Viewした直後にcanvasにアクセスするとエラーを吐いたので使えないのかな?と思っていたのですが、
レンダリングした後にアクセスしたら普通に3DデータをBitmapData化させることができました・・・。
お騒がせしてすみません。
  1. 2011/08/07(日) 12:02:52 |
  2. URL |
  3. シャイん #-
  4. [ 編集 ]

鏡面表示について

鏡のように反射して表示したいと思い、View3Dについて調べていたらここにたどり着きました。
正直言って私は初心者なので上記のコードを理解しきること自体できていない身ですが、とりあえず今回の正式リリースでフィルターあたりの仕様変更も行われたようです。
フィルターはそのタスクを登録するクラスになり、登録されたクラスでテクスチャの操作を行うような感じになったように感じました。
上記のコードはすでにコンパイルが通らなくなってしまっているので、色々いじって理解するということができず残念ですOTL

それはそれとして、表示内容をY軸で反転させて表示する(鏡のような)というのは実現可能なのでしょうか?
  1. 2011/11/13(日) 05:37:56 |
  2. URL |
  3. BPL #/ti6hurY
  4. [ 編集 ]

最新版のフィルタクラスを覗いてみましたが、レンダリングのパス(上記TwinkleFilter3Dで言うcontext.drawTriangles()の回数)それぞれをTaskとして分割してクラス化したもののようです。
各フィルタのsetRenderTargetsでタスクごとに入力テクスチャの参照と出力テクスチャの参照を設定してます。

プリセットのBloomFilter3Dですと、
_brightPassTask・・・光らせるパス(タスク)
_blurTask・・・ずらすパス(タスク)
_compositeTask・・・合成するパス(タスク)
に分けることができ、それぞれのタスクが仕事をして次のタスクに結果(テクスチャ)を渡す流れになっています。

鏡面効果ですが、レンダリングの結果をそのままテクスチャとして得るのは比較的簡単だと思います。
が、そのテクスチャ(flash.display3D.textures.Texture)をどうやって配置するかというのは少し難しいかもしれません。

おそらくなさりたいことはそのテクスチャをマテリアルのようにしてPlaneに貼り付けて自由に3Dシーンに配置することでしょうが、レンダリング結果をテクスチャにした時点でそのフレームの3Dシーンのレンダリングは終わっています。
なのでそこからさらにPlaneを追加するにはもう一度最初からレンダリング作業が必要になるかと思います。

flash.display3D.textures.Textureをそのままマテリアルのテクスチャとして指定する方法はわかりませんが、それができるのならば

EnterFrame内で
[フィルタを設定
view.render();
フィルタの結果からTextureを抽出し、マテリアルにしてplaneに設定。Planeを3Dシーンに追加
フィルタを外す
view.render();
Planeを3Dシーンからremove]
という流れが一応思いつきます。レンダリング負荷が二倍になりますが。

もう一つはフィルタ内で、得られたTextureを自分で頂点を設定してプロジェクション変換しテクスチャ貼り付けして入力テクスチャの上から描画、というのが考えられるかもしれませんが、思ったように描画するのはさらに難しいと思います。

最近Away3Dはあまり触っていないので予想になりますが。

鏡面もなかなか要望が高いと思いますのでそのうち実装されるかもしれませんね。
  1. 2011/11/13(日) 10:17:18 |
  2. URL |
  3. 9ballsyn #-
  4. [ 編集 ]

コメントの投稿


管理者にだけ表示を許可する

トラックバック

トラックバック URL
http://9ballsyndrome.blog.fc2.com/tb.php/29-1513b131
この記事にトラックバックする(FC2ブログユーザー)