リフラクションマッピング


DisplacementMapFilter と ConvolutionFilter使えば,
屈折マッピングも余裕で出来たりする.
ホントすげーよ,ActionScript3のビットマップ操作系関数.
このC++ライブラリが欲しい.


[10/4追記:ソース貼るの忘れてた]
以下ソースコード
ライセンスはPublic Domainです.

//----------------------------------------
// 2D metaball + reflaction map
//  written by keim +Si+
//  licence: PD
//----------------------------------------
package {
    import flash.display.*;
    import flash.events.*;
    import flash.geom.*;
    import flash.filters.*;
    import flash.utils.*;
    import flash.text.*;
    
    [SWF(width="240", height="240", backgroundColor="#406080", frameRate=30)]
    public class main_ref extends Sprite
    {
        static public  var base:BitmapData;     // base image
        static private var temp:BitmapData;     // tempolaly
        static private var bump:BitmapData;     // bump image
        static private var alph:BitmapData;     // alpha image
        static private var pixels:BitmapData;   // displaying image
        static private var screen:Bitmap;       // displaying image
        static private var balls:Array;         // meta-balls array

        static private var ptBallsCenter:Point = new Point();   // balls center position
        static private var matFace:Matrix = new Matrix();       // face matrix

        static private var convx:ConvolutionFilter;     // convolution filter for x differencial
        static private var convy:ConvolutionFilter;     // convolution filter for y differencial
        static private var disp:DisplacementMapFilter;  // displacement filter for refraction
        static private var conv:ConvolutionFilter;      // convolution filter for lighting
        static private var colt:ColorTransform;         // color matrix for cutout
        static private var color:ColorTransform;        // color matrix for coloring
        
        static private var face:BitmapData;     // face image 
        static private var floor:BitmapData;    // floor image 
        
        public function main_ref()
        {
            var i:int;
            
            // bitmaps
            base   = new BitmapData(240, 240, false, 0x000000);
            temp   = new BitmapData(240, 240, false, 0x000000);
            bump   = new BitmapData(240, 240, false, 0x000000);
            alph   = new BitmapData(240, 240, true, 0x000000);
            pixels = new BitmapData(240, 240, false, 0x000000);
            screen = new Bitmap(pixels);
            
            // create filter instance
            colt = new ColorTransform(32,32,32,1,-1280,-1280,-1280,0);
            conv  = new ConvolutionFilter(3, 3, [1,0,0,0,0,0,0,0,-1], 0.4, 128);
            convx = new ConvolutionFilter(3, 3, [0,0,0,-1,0,1,0,0,0], 0.2, 128);
            convy = new ConvolutionFilter(3, 3, [0,-1,0,0,0,0,0,1,0], 0.2, 128);
            disp = new DisplacementMapFilter(bump, bump.rect.topLeft, BitmapDataChannel.RED, BitmapDataChannel.GREEN, 40, 40);
            color = new ColorTransform(1,1,1,1,0,0,0,0);

            // draw meta-ball gradation
            ball.pat = new BitmapData(ball.R*2,ball.R*2,false,0);
            var shp:Shape = new Shape();
            var mtx:Matrix = new Matrix();
            mtx.createGradientBox(ball.R*2,ball.R*2,0,0);
            shp.graphics.beginGradientFill(GradientType.RADIAL, [0x808080,0x000000], [1,1], [0,255], mtx);
            shp.graphics.drawRect(0,0,ball.R*2,ball.R*2);
            shp.graphics.endFill();
            base.draw(shp);
            base.draw(shp, null, null, BlendMode.MULTIPLY);
            var blr:BlurFilter = new BlurFilter(4, 4, 4);
            ball.pat.applyFilter(base, ball.pat.rect, ball.pat.rect.topLeft, blr);

            // draw face
            face = new BitmapData(48, 16, true, 0x00000000);
            var tf:TextField = new TextField();
            tf.width = 48;
            tf.height = 16;
            tf.defaultTextFormat = new TextFormat("MS Gothic", 12, 0x000000, null, null, null, null, null, TextFormatAlign.CENTER);
            tf.text = "o゚ω゚o";
            face.draw(tf);
            
            // draw floor
            floor  = new BitmapData(240, 240, false, 0xffffff);
            shp.graphics.clear();
            shp.graphics.lineStyle(2, 0x000000);
            for (i=0; i<4; i++) {
                shp.graphics.moveTo(i*60+30, 0);
                shp.graphics.lineTo(i*60+30, 240);
                shp.graphics.moveTo(0, i*60+30);
                shp.graphics.lineTo(240, i*60+30);
            }
            floor.draw(shp);
            temp .applyFilter(floor, floor.rect, floor.rect.topLeft, blr);
            floor.applyFilter(temp,  temp.rect,  temp.rect.topLeft,  new ConvolutionFilter(3, 3, [-0.5,-0.5,0,-0.5,0,0.5,0,0.5,0.5], 0.5, 128));
            floor.draw(shp,null,new ColorTransform(1,1,1,0.4,0,0,0,0));
            temp.fillRect(temp.rect, 0x8090e0);
            floor.draw(temp, null, null, BlendMode.OVERLAY);
                        
            // create ball instance
            balls = new Array(16);
            for (i=0; i<balls.length; i++) { balls[i] = new ball(); }

            // set sprite/event
            addChild(screen);
            stage.quality   = StageQuality.HIGH;
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align     = StageAlign.TOP_LEFT;
            stage.doubleClickEnabled = true;
            stage.addEventListener(Event.ENTER_FRAME, _onEnterFrame);
            stage.addEventListener(MouseEvent.MOUSE_DOWN, _onMouseDown);
            stage.addEventListener(MouseEvent.MOUSE_UP,   _onMouseUp);
            stage.addEventListener(MouseEvent.DOUBLE_CLICK, _onDoubleClick);
        }

        
        private function _onEnterFrame(event:Event) : void
        {
            var i:int, j:int;
            
            // measure start
            //var start:int = getTimer(); for (var l:int=0; l<100; l++) {
                
            // execute
            ball.mouseX = mouseX;
            ball.mouseY = mouseY;
            for (i=0; i<balls.length; i++) { ball(balls[i]).gravity(); }
            for (i=0; i<balls.length-1; i++) for (j=i+1; j<balls.length; j++) { ball(balls[i]).interact(ball(balls[j])); }
            for (i=0; i<balls.length; i++) { ball(balls[i]).run(); }
            calcBallCenter();
            
            // draw balls
            base.fillRect(pixels.rect,0x000000);
            for (i=0; i<balls.length; i++) { ball(balls[i]).draw(base); }
            
            // refraction
            temp.applyFilter(base, base.rect, base.rect.topLeft, convx);
            bump.applyFilter(base, base.rect, base.rect.topLeft, convy);
            bump.copyChannel(temp, temp.rect, temp.rect.topLeft, BitmapDataChannel.RED, BitmapDataChannel.RED);
            temp.applyFilter(floor, floor.rect, floor.rect.topLeft, disp);
            alph.copyPixels(temp, temp.rect, temp.rect.topLeft);
            
            // lighting
            temp.applyFilter(base, base.rect, base.rect.topLeft, conv);
            alph.draw(temp,null,null,BlendMode.HARDLIGHT);
            
            // cutout
            temp.copyPixels(base, base.rect, base.rect.topLeft);
            temp.colorTransform(temp.rect, colt);
            alph.copyChannel(temp, temp.rect, temp.rect.topLeft, BitmapDataChannel.RED, BitmapDataChannel.ALPHA);
            
            // draw
            pixels.copyPixels(floor, floor.rect, floor.rect.topLeft);
            pixels.copyPixels(alph, alph.rect, alph.rect.topLeft);
            drawFace();
            
            // measure end
            //} trace(getTimer() - start);
        }

        private function _onMouseDown(event:MouseEvent) : void
        {
            for (var i:int=0; i<balls.length; i++) { ball(balls[i]).check_hold(event.localX, event.localY); }
        }
    
        private function _onMouseUp(event:MouseEvent) : void
        {
            for (var i:int=0; i<balls.length; i++) { ball(balls[i]).hold = false; }
        }
        
        private function _onDoubleClick(event:MouseEvent) : void
        {
            for (var i:int=0; i<balls.length; i++) { ball(balls[i]).blast(); }
        }
        
        private function calcBallCenter() : void
        {
            ptBallsCenter.x = 0;
            ptBallsCenter.y = 0;
            for (var i:int=0; i<balls.length; i++) { ptBallsCenter.offset(balls[i].x, balls[i].y); }
            var dv:Number=1/balls.length;
            ptBallsCenter.x *= dv;
            ptBallsCenter.y *= dv;
        }
        
        private function drawFace() : void
        {
            matFace.identity();
            matFace.translate(-24, -8);
            matFace.rotate(Math.atan2(balls[0].y-ptBallsCenter.y, balls[0].x-ptBallsCenter.x));
            matFace.translate(ptBallsCenter.x, ptBallsCenter.y);
            pixels.draw(face, matFace, null, null, null, true);
        }
    }
}


import flash.display.*;
import flash.geom.*;
class ball
{
    ...same as id:keim_at_Si:20070928...
}