[Away3D] 前面にのみフィルターを適用する
Away3D 4 goldでView3Dを2つ作って前面にフィルタをかけた時に背面が映らなくなる問題について
どうやらフィルタをかけた時にフィルタ面がアルファに対応していなかったようで、
Filter3DRendererを少し書き換えてみました
Filter3DRendererのメンバがほとんどprivateだったせいでほぼ丸々コピペせざるを得ず
フィルタ面のView3DのbackgroundAlphaは0に設定
フィルタかけた後に透けさせるのは微妙な出来で、
他のフィルタの組み合わせなどではどうなるのかわからないので注意
/**
* Copyright 9balls ( http://wonderfl.net/user/9balls )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/mjsy
*/
// forked from romatica's [Away3D] 部分的にフィルターを適用する方法
/**
* copyright (c) 2012 www.romatica.com
* @author itoz
* @since 2012/09/17 1:11:37
*
* [Away3D] 部分的にフィルターを適用する方法
*
* View3Dを複数作成。
*
* View3Dの、
* - stage3DProxyを共通にする。
* - shareContextをtrueにする。
* - 場合によってはカメラなどを共有する。
*
*/
package
{
import away3d.cameras.Camera3D;
import away3d.core.managers.Stage3DManager;
import away3d.core.managers.Stage3DProxy;
import away3d.events.Stage3DEvent;
import away3d.filters.BloomFilter3D;
import away3d.lights.PointLight;
import away3d.materials.lightpickers.StaticLightPicker;
import frocessing.color.ColorHSV;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Vector3D;
import flash.utils.getTimer;
[SWF(backgroundColor="#FFFFFF", frameRate="60", width="465", height="465")]
/**
*
*/
public class WonderflAway3DFilter extends Sprite
{
private static const ZERO : Vector3D = new Vector3D(0, 0, 0);
private var stage3DManager : Stage3DManager;
private var stage3DProxy : Stage3DProxy;
private var createFlag : Boolean;
private var background3d : BackGroundView3D;
private var front3d : FrontView3D;
private var camera : Camera3D;
private var _lightColor0HSV : ColorHSV;
private var _lightColor0 : PointLight;
private var _lightColor1HSV : ColorHSV;
private var _lightColor1 : PointLight;
private var _lightPicker : StaticLightPicker;
// wonderfl capture
private var _capture : Bitmap;
public function WonderflAway3DFilter()
{
if (stage) _initialize(null);
else addEventListener(Event.ADDED_TO_STAGE, _initialize);
}
private function _initialize(event : Event) : void
{
removeEventListener(flash.events.Event.ADDED_TO_STAGE, _initialize);
initProxies();
}
private function initProxies() : void
{
// Stage3Dマネージャを取得
stage3DManager = Stage3DManager.getInstance(stage);
// stage3Dプロキシを取得
stage3DProxy = stage3DManager.getFreeStage3DProxy();
stage3DProxy.addEventListener(Stage3DEvent.CONTEXT3D_CREATED, onContextCreated);
}
private function onContextCreated(event : Stage3DEvent) : void
{
if (createFlag) return;
createFlag = true;
stage3DProxy.width = stage.stageWidth;
stage3DProxy.height = stage.stageHeight;
// ----------------------------------
// init away3d
// ----------------------------------
initAway3D();
// ----------------------------------
// render start
// ----------------------------------
stage3DProxy.addEventListener(Event.ENTER_FRAME, update);
// wonderfl capture
Wonderfl.disable_capture();
//_capture = addChild(new Bitmap(new BitmapData(465, 465, false, 0x000000))) as Bitmap ;
}
/**
* init Away3D
*/
private function initAway3D() : void
{
// ----------------------------------
// lights
// ----------------------------------
_lightColor0HSV = new ColorHSV(0xcc0000);
_lightColor0 = new PointLight();
_lightColor0.ambientColor = _lightColor0HSV.value;
_lightColor0.ambient = 10;
_lightColor0.specular = 10;
_lightColor0.diffuse = 10;
_lightColor1HSV = new ColorHSV(0x00cc00);
_lightColor1 = (new PointLight()) ;
_lightColor1.ambientColor = _lightColor1HSV.value;
_lightColor1.ambient = 100;
_lightColor1.specular = 100;
_lightColor1.diffuse = 100;
_lightPicker = new StaticLightPicker([_lightColor0, _lightColor1]);
//----------------------------------
// ★ background (filter3dを適用する背景)
//----------------------------------
background3d = new BackGroundView3D(_lightPicker);
background3d.stage3DProxy = stage3DProxy;
background3d.shareContext = true;
addChild(background3d);
// filter3d
background3d.filters3d = [new BloomFilter3D(32, 32)];
// common camera
camera = background3d.camera;
// ----------------------------------
// ★ front
// ----------------------------------
front3d = new FrontView3D(_lightPicker);
front3d.camera = camera;
front3d.stage3DProxy = stage3DProxy;
front3d.shareContext = true;
addChild(front3d);
// stats
// addChild(new AwayStats(background3d));
}
/**
* update
*/
private function update(event : Event) : void
{
var t : Number = getTimer();
// ----------------------------------
// light
//----------------------------------
if (_lightColor0) {
_lightColor0.x = Math.cos(t / 1024) * 800;
_lightColor0.z = Math.sin(t / 1024) * 400 + 700;
_lightColor0HSV.h += 2;
_lightColor0.ambientColor = _lightColor0HSV.value;
}
if (_lightColor1) {
_lightColor1.x = Math.sin(t / 1024) * 400;
_lightColor1.y = Math.cos(t / 1024) * 200;
_lightColor1.z = 1800;
_lightColor1HSV.h += 2;
_lightColor1.ambientColor = _lightColor1HSV.value;
}
//----------------------------------
// camera
//----------------------------------
camera.x = Math.cos(t / 512) * 100;
camera.y = Math.sin(t / 512) * 100;
camera.z = -600;
camera.lookAt(ZERO);
// ----------------------------------
// 3D update
// ----------------------------------
if (front3d) front3d.update();
if (background3d) background3d.update();//描画順変更
// wonderfl capture
if (_capture) stage3DProxy.context3D.drawToBitmapData(_capture.bitmapData)
}
}
}
////////////////////////////////////////////////////////////////////////////
//ここからAway3Dクラスを修正
//--------------------------------------------------------------------------
//
// Filter3DRenderer2 フィルタ部分を透過させるためにFilter3DRendererを継承して作るが、実際の変更点はrenderメソッドで3箇所だけ
//
//--------------------------------------------------------------------------
import away3d.cameras.Camera3D;
import away3d.core.managers.RTTBufferManager;
import away3d.core.managers.Stage3DProxy;
import away3d.core.render.Filter3DRenderer;
import away3d.filters.Filter3DBase;
import away3d.filters.tasks.Filter3DTaskBase;
import flash.display3D.Context3DBlendFactor;
import flash.display3D.Context3D;
import flash.display3D.Context3DVertexBufferFormat;
import flash.display3D.IndexBuffer3D;
import flash.display3D.VertexBuffer3D;
import flash.display3D.textures.Texture;
import flash.events.Event;
class Filter3DRenderer2 extends Filter3DRenderer
{
private var _filters : Array;
private var _tasks : Vector.<Filter3DTaskBase>;
private var _filterTasksInvalid : Boolean;
private var _mainInputTexture : Texture;
private var _requireDepthRender : Boolean;
private var _rttManager : RTTBufferManager;
private var _stage3DProxy : Stage3DProxy;
private var _filterSizesInvalid : Boolean = true;
public function Filter3DRenderer2(stage3DProxy : Stage3DProxy)
{
super(stage3DProxy)//スパークラスに渡してるだけで意味なし
_stage3DProxy = stage3DProxy;
_rttManager = RTTBufferManager.getInstance(stage3DProxy);
_rttManager.addEventListener(Event.RESIZE, onRTTResize);
}
private function onRTTResize(event : Event) : void
{
_filterSizesInvalid = true;
}
override public function get requireDepthRender() : Boolean
{
return _requireDepthRender;
}
override public function getMainInputTexture(stage3DProxy : Stage3DProxy) : Texture
{
if (_filterTasksInvalid) updateFilterTasks(stage3DProxy);
return _mainInputTexture;
}
override public function get filters() : Array
{
return _filters;
}
override public function set filters(value : Array) : void
{
_filters = value;
_filterTasksInvalid = true;
_requireDepthRender = false;
if (!_filters) return;
for (var i : int = 0; i < _filters.length; ++i)
_requireDepthRender ||= _filters[i].requireDepthRender;
_filterSizesInvalid = true;
}
private function updateFilterTasks(stage3DProxy : Stage3DProxy) : void
{
var len : uint;
if (_filterSizesInvalid) updateFilterSizes();
if (!_filters) {
_tasks = null;
return;
}
_tasks = new Vector.<Filter3DTaskBase>();
len = _filters.length - 1;
var filter : Filter3DBase;
for (var i : uint = 0; i <= len; ++i) {
// make sure all internal tasks are linked together
filter = _filters[i];
filter.setRenderTargets(i == len? null : Filter3DBase(_filters[i+1]).getMainInputTexture(stage3DProxy), stage3DProxy);
_tasks = _tasks.concat(filter.tasks);
}
_mainInputTexture = _filters[0].getMainInputTexture(stage3DProxy);
}
override public function render(stage3DProxy : Stage3DProxy, camera3D : Camera3D, depthTexture : Texture) : void
{
var len : int;
var i : int;
var task : Filter3DTaskBase;
var context : Context3D = stage3DProxy.context3D;
var indexBuffer : IndexBuffer3D = _rttManager.indexBuffer;
var vertexBuffer : VertexBuffer3D = _rttManager.renderToTextureVertexBuffer;
if (!_filters) return;
if (_filterSizesInvalid) updateFilterSizes();
if (_filterTasksInvalid) updateFilterTasks(stage3DProxy);
len = _filters.length;
for (i = 0; i < len; ++i) _filters[i].update(stage3DProxy, camera3D);
len = _tasks.length;
if (len > 1) {
context.setVertexBufferAt(0, vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_2);
context.setVertexBufferAt(1, vertexBuffer, 2, Context3DVertexBufferFormat.FLOAT_2);
}
for (i = 0; i < len; ++i) {
task = _tasks[i];
stage3DProxy.setRenderTarget(task.target);
if (!task.target) {
stage3DProxy.scissorRect = null;
vertexBuffer = _rttManager.renderToScreenVertexBuffer;
context.setVertexBufferAt(0, vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_2);
context.setVertexBufferAt(1, vertexBuffer, 2, Context3DVertexBufferFormat.FLOAT_2);
}else
{
//変更点1 : clerをif (!task.target)のelseにいれる...この位置にしないとフィルタViewの描写前に描かれたもの(GeoSpereのレイヤー)が破棄されてしまう
context.clear(0.0, 0.0, 0.0, 0.0);//変更点2 : clearの第四引数を0にする...アルファを0でセットしないとフィルタの下レイヤーが見えない
}
stage3DProxy.setTextureAt(0, task.getMainInputTexture(stage3DProxy));
stage3DProxy.setProgram(task.getProgram3D(stage3DProxy));
//context.clear(0.0, 0.0, 0.0, 1.0);//変更点1
context.setBlendFactors(Context3DBlendFactor.SOURCE_ALPHA, Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA);//変更点3 : setBlendFactorsでアルファブレンドの設定にする...これがないとアルファブレンドが適応されず、フィルタのしたレイヤーが見えない
task.activate(stage3DProxy, camera3D, depthTexture);
context.drawTriangles(indexBuffer, 0, 2);
task.deactivate(stage3DProxy);
}
stage3DProxy.setTextureAt(0, null);
stage3DProxy.setSimpleVertexBuffer(0, null, null, 0);
stage3DProxy.setSimpleVertexBuffer(1, null, null, 0);
}
private function updateFilterSizes() : void
{
for (var i : int = 0; i < _filters.length; ++i) {
_filters[i].textureWidth = _rttManager.textureWidth;
_filters[i].textureHeight = _rttManager.textureHeight;
}
_filterSizesInvalid = true;
}
override public function dispose() : void
{
_rttManager.removeEventListener(Event.RESIZE, onRTTResize);
_rttManager = null;
_stage3DProxy = null;
}
}
//--------------------------------------------------------------------------
//
// View3D2 フィルタがセットされたときにレンダラーをFilter3DRenderer2にすり替えるために継承して設定したもの フィルタを使うViewはこれを継承する
//
//--------------------------------------------------------------------------
import away3d.cameras.Camera3D;
import away3d.containers.Scene3D;
import away3d.containers.View3D;
import away3d.core.render.RendererBase;
class View3D2 extends View3D
{
public function View3D2(scene:Scene3D=null, camera:Camera3D=null, renderer:RendererBase=null, forceSoftware:Boolean=false)
{
super(scene, camera, renderer, forceSoftware);
}
override public function get filters3d():Array
{
return _filter3DRenderer? _filter3DRenderer.filters : null;
}
override public function set filters3d(value:Array):void
{
if (value && value.length == 0)
value = null;
if (_filter3DRenderer && !value) {
_filter3DRenderer.dispose();
_filter3DRenderer = null;
} else if (!_filter3DRenderer && value) {
_filter3DRenderer = new Filter3DRenderer2(stage3DProxy);//すり替える
_filter3DRenderer.filters = value;
}
if (_filter3DRenderer) {
_filter3DRenderer.filters = value;
_requireDepthRender = _filter3DRenderer.requireDepthRender;
} else {
_requireDepthRender = false;
if (_depthRender) {
_depthRender.dispose();
_depthRender = null;
}
}
}
}
////////////////////////////////////////////////////////////////////////////
import a24.tween.Ease24;
import a24.tween.Tween24;
import away3d.containers.ObjectContainer3D;
import away3d.containers.View3D;
import away3d.core.base.Geometry;
import away3d.entities.Mesh;
import away3d.materials.ColorMaterial;
import away3d.materials.lightpickers.LightPickerBase;
import away3d.materials.methods.FogMethod;
import away3d.primitives.CubeGeometry;
import away3d.primitives.WireframeSphere;
import flash.events.Event;
import flash.utils.getTimer;
//--------------------------------------------------------------------------
//
// FrontView3D 球体部分(フィルタ適用しないView3D)
//
//--------------------------------------------------------------------------
class FrontView3D extends View3D
{
private static const SPEED : int = 3;
private var _sphere : WireframeSphere;
private var _lightPicker : LightPickerBase;
public function FrontView3D(lightPicker : LightPickerBase = null)
{
super();
_lightPicker = lightPicker;
if (stage) _initialize(null);
else addEventListener(Event.ADDED_TO_STAGE, _initialize);
}
private function _initialize(event : Event) : void
{
removeEventListener(flash.events.Event.ADDED_TO_STAGE, _initialize);
antiAlias = 4;
createSphere();
}
/**
* create sphere
*/
private function createSphere() : void
{
// var cubeGeo : SphereGeometry = new SphereGeometry(64, 64);
// var cubeMat : ColorMaterial = new ColorMaterial(0xffffff);
// if (_lightPicker) cubeMat.lightPicker = _lightPicker;
// _sphere = new Mesh(cubeGeo, cubeMat);
_sphere = new WireframeSphere(128);
_sphere.color = 0xffffff;
scene.addChild(_sphere);
}
/**
* update
*/
public function update() : void
{
if (!_sphere) return;
_sphere.rotationX += (_sphere.rotationX + SPEED > 360 ) ? SPEED - 360 : SPEED ;
_sphere.rotationY += (_sphere.rotationY + (SPEED * 0.5) > 360 ) ? (SPEED * 0.5) - 360 : (SPEED * 0.5) ;
_sphere.rotationZ += (_sphere.rotationZ + (SPEED * 0.3) > 360 ) ? (SPEED * 0.3) - 360 : (SPEED * 0.3) ;
_sphere.z = Math.cos(getTimer() / 1000) * 300;
render();
}
}
//--------------------------------------------------------------------------
//
// BackGroundView3D(フィルタを適用するView3D)
//
//--------------------------------------------------------------------------
class BackGroundView3D extends View3D2
{
private static const CUBE_SIZE : int = 64;
private static const CUBE_NUM_IN_CIRCLE : int = 16;
private static const CIRCLE_R : int = 300;
private static const CIRCLE_NUM : int = 16;
private static const MIN_DISTANCE : int = -400;
private static const MAX_DISTANCE : int = 2400;
private static const DISTANCE : int = (MAX_DISTANCE + Math.abs(MIN_DISTANCE));
private static const Z_SPEED : Number = 20;
private static const countMax : int = 60;
private var count : int = 0;
private var cubes : Vector.<Mesh>;
private var circles : Vector.<EffectCircle>;
private var circleSpan : Number = (DISTANCE / CIRCLE_NUM);
private var _lightPicker : LightPickerBase;
public function BackGroundView3D(lightPicker : LightPickerBase = null)
{
super();
_lightPicker = lightPicker;
if (stage) _initialize(null);
else addEventListener(Event.ADDED_TO_STAGE, _initialize);
}
private function _initialize(event : Event) : void
{
removeEventListener(flash.events.Event.ADDED_TO_STAGE, _initialize);
antiAlias = 2;
backgroundColor = 0x0;
backgroundAlpha = 0.0;
camera.lens.far = 2400;
createCubes();
}
/**
* create cubes
*/
private function createCubes() : void
{
// ----------------------------------
// material
// ----------------------------------
var material : ColorMaterial = new ColorMaterial();
material.addMethod(new FogMethod(MIN_DISTANCE, MAX_DISTANCE, 0x000000));
material.lightPicker = _lightPicker;
material.gloss = 100;
// ----------------------------------
// objects
// ----------------------------------
circles = new Vector.<EffectCircle>();
cubes = new Vector.<Mesh>();
for (var i : int = 0; i < CIRCLE_NUM; i++) {
// ----------------------------------
// Circle
// ----------------------------------
var circle : EffectCircle = scene.addChild(new EffectCircle()) as EffectCircle;
circle.z = circleSpan * i;
circle.id = i;
circles.push(circle);
for (var j : int = 0; j < CUBE_NUM_IN_CIRCLE; j++) {
// ----------------------------------
// Cube
// ----------------------------------
var cubeGeo : Geometry = new CubeGeometry(CUBE_SIZE, CUBE_SIZE, CUBE_SIZE);
var cube : Mesh = circle.addChild(new Mesh(cubeGeo, material)) as Mesh;
var rad : Number = (360 / CUBE_NUM_IN_CIRCLE * j) * (Math.PI / 180);
cube.x = Math.cos(rad) * CIRCLE_R;
cube.y = Math.sin(rad) * CIRCLE_R;
cubes.push(cube);
}
}
}
public function update() : void
{
if (!circles) return;
var t : Number = getTimer();
var max : int = circles.length;
var half : int = int(max * 0.5);
var cos : Number = Math.cos(t / 36000) * 720 ;
for (var i : int = 0; i < max; i++) {
var circle : EffectCircle = circles[i] as EffectCircle;
if (!circle) break;
// ----------------------------------
// Z回転
// ----------------------------------
var val : int;
if (i == half) val = half;
else if (i < half) val = i % half;
else val = half - i % half;
circle.rotationZ = cos * val ;
// ----------------------------------
// 手前にZ移動
// ----------------------------------
circle.z -= Z_SPEED ;
if (circle.z < MIN_DISTANCE) circle.z = circle.z + DISTANCE;
if (count >= countMax) {
var delay : Number = i * 0.05;
var scale : Number = 0.75;
Tween24.serial(
Tween24.wait(delay)
, Tween24.tween(circle,0.45,Ease24._BackIn).scaleX(scale).scaleY(scale).scaleZ(scale)
, Tween24.tween(circle, 0.75, Ease24._BackOut).scaleX(1).scaleY(1).scaleZ(1)
).play();
}
}
count = (count + 1 > countMax) ? 0 : count + 1;
render();
}
}
class EffectCircle extends ObjectContainer3D
{
public var id : int = 0;
public function EffectCircle()
{
super();
}
}