Flash8 Perlin Marble Texture

warning: Cannot modify header information - headers already sent by (output started at /home/joel/public_html/blog/includes/bootstrap.inc:696) in /home/joel/public_html/blog/includes/common.inc on line 148.
Tags:

Last week, I wrote an article about using Flash 8 Perlin noise to generate a wood texture. Today, it's marble.

According to this link, the Perlin formula for marble is:

texture = cosine( x + perlin(x,y,z) )

Again, we don't want to do math on every pixel -- too slow. So, how do we do this with the existing Flash 8 API? First, let's pretend that the perlin term in the above equation is not there. Here's what our image would look like (this image is arbitrarily blue monochrome):

Sinusoid Base for Marble

One pixel scan line of the above image was generated with Math.cos() and BitmapData.setPixel(). The bitmap was then stretched vertially -- no need to call setPixel on w * h pixels -- w x 1 is enough.

The perlin term randomly distorts the phase. In the context of the above image, this means pixels are randomly displaced horizontally. A DisplacementMapFilter driven by perlin is exactly what we need:
Marble, Not-Color Corrected

Then we can map the colors to realistic marble colors using ColorMatrixFilter:
Marble, Corrected

I find the sinusoid basis for the marble to be too regular. We can use perlin noise instead of a sinusoid and our pre-DisplacementMapFilter image looks like this:
Perlin base marble

You can play with the settings here. BTW, I find it takes a lot of trial-and-error to find parameters that result in a decent marble.

Replacement image.

Here's the code:

/**
 * @author jmay
 * www.connectedpixel.com 
 * All original source code listed here is licensed under a Creative Commons License. 
*/
import flash.display.BitmapData;
import flash.geom.Rectangle;
import flash.geom.Point;
import flash.geom.Matrix;
import flash.filters.BlurFilter;
import flash.filters.BitmapFilter;
import flash.filters.ColorMatrixFilter;
import flash.filters.DisplacementMapFilter;
import flash.geom.ColorTransform;
 
 
class com.connectedpixel.texture.Marble {
    
    ////////////////////////////////////////////////////////////
    // Properties.  Settable via setter/getters below
    
    // Sinusoidal or Perlin floor
    private var _bSinusoidalFloor:Boolean = false;
    
    // Sinusoidal floor parameters
    private var _waveLength:Number = 100;
    private var _sinMidColorPt:Number = 128;
    private var _sinContrastMultiplier:Number = 1.0;
        
    // Perlin floor parameters
    private var _bPerlinFloorBaseX:Number = 50;
    private var _bPerlinFloorBaseY:Number = 150;
    private var _floorRandomSeed:Number = 317;
    private var _perlinMidColorPt:Number = 128;
    private var _perlinContrastMultiplier:Number = 2.0;
    
    // Perlin floor modifiers
    private var _contrastMultiplier:Number = 1.0;
    private var _colorMidLevel:Number = 128;  // offset
    private var _veinAngleDeg:Number = 0; 
    
    // Marble Distortion parameters
    private var _bEnableDistortion:Boolean = true;
    private var _baseX          :Number = 20;
    private var _baseY          :Number = 20;    
    private var _nOctaves       :Number = 2; 
    private var _randomSeed     :Number = 147;
    private var _bFractalNoise  :Boolean = false;
    private var _blurX          :Number = 5;
    private var _blurY          :Number = 5;
    
    // Distortion strength
    private var _displacementScaleX = 150;
        
    // Color mapping
    private var _rgb0:Number = 0x1c2a1f;
    private var _rgb1:Number = 0xadc8b4;
    
    ////////////////////////////////////////////////////////////
    
    private var _identityMatrix:Matrix;
    private var _identityColorTrans:ColorTransform;
    
    private var _marbleColorFilter:ColorMatrixFilter;
    
    private function invalidateMarbleColorFilter():Void
    {
        delete _marbleColorFilter;
        _marbleColorFilter = undefined;    
    }    
    
    //////////////////////////////////////////////////////////////////////
    // Properties.
    
    public function set sinusoidalFloor(bSin:Boolean):Void { _bSinusoidalFloor = bSin; }
    public function get sinusoidalFloor():Boolean          { return _bSinusoidalFloor; }
    
    public function set waveLength(len:Number):Void { 
        if (isNaN(len)) return;
        if (len < 2) len = 2;
        if (len > 1000) len = 1000;
        _waveLength = len; 
    }
    public function get waveLength():Number { return _waveLength; }    
    
    public function set sinMidColorPt(val:Number):Void { 
        if (isNaN(val)) return;
        if (val < 10) val = 10;
        if (val > 246) val = 246;
        _sinMidColorPt = val; 
    }
    public function get sinMidColorPt():Number { return _sinMidColorPt; }    
    
    public function set sinContrast(contrast:Number):Void 
    { 
        if (contrast < 0.1 ) contrast = 0.1;
        if (contrast > 10.0) contrast = 10.0;
        _sinContrastMultiplier = contrast; 
    }
    public function get sinContrast():Number { return _sinContrastMultiplier; }
    
    ///////////////////////////////////////////////////////////////////////
    
    public function set perlinMidColorPt(val:Number):Void { 
        if (isNaN(val)) return;
        if (val < 10) val = 10;
        if (val > 246) val = 246;
        _perlinMidColorPt = val; 
    }
    public function get perlinMidColorPt():Number { return _perlinMidColorPt; }    
    
    public function set perlinContrast(contrast:Number):Void 
    { 
        if (contrast < 0.1 ) contrast = 0.1;
        if (contrast > 10.0) contrast = 10.0;
        _perlinContrastMultiplier = contrast; 
    }
    public function get perlinContrast():Number { return _perlinContrastMultiplier; }
    
    public function set floorBaseX(bx:Number):Void { _bPerlinFloorBaseX = bx; }
    public function set floorBaseY(by:Number):Void { _bPerlinFloorBaseY = by; }
    
    public function get floorBaseX():Number { return _bPerlinFloorBaseX; }
    public function get floorBaseY():Number { return _bPerlinFloorBaseY; }
    
    public function set floorRandomSeed(seed:Number):Void 
                                                       { _floorRandomSeed = seed; } 
    public function get floorRandomSeed():Number { return _floorRandomSeed; } 
    
    ////////////////////////////////////////////////////////////////////////////
    public function set enableDistortion(bEnab:Boolean):Void 
                                                 { _bEnableDistortion = bEnab; }
    public function get enableDistortion():Boolean          
                                                    { return _bEnableDistortion; }
    
    public function set perlinBaseX(bx:Number):Void { 
        if (isNaN(bx)) return;
        if (bx < 3) bx = 3;
        if (bx > 1000) bx = 1000;
        _baseX = bx; 
    }
    
    public function get perlinBaseX():Number { return _baseX; }
    
    public function set perlinBaseY(by:Number):Void { 
        if (isNaN(by)) return;
        if (by < 3) by = 3;
        if (by > 1000) by = 1000;
        _baseY = by; 
    }
    public function get perlinBaseY():Number { return _baseY; }
     
    public function set octaves(nOct:Number):Void { _nOctaves = nOct; }
    public function get octaves():Number { return _nOctaves; }
    
    public function set rgb0(rgb:Number):Void { 
        _rgb0 = rgb; invalidateMarbleColorFilter();
    }
    public function get rgb0():Number { return _rgb0; }
    
    public function set rgb1(rgb:Number):Void { 
        _rgb1 = rgb; invalidateMarbleColorFilter();
    }
    public function get rgb1():Number { return _rgb1; }
    
    public function set seed(s:Number):Void { _randomSeed = s; }
    public function get seed():Number { return _randomSeed; }
    
    public function set fractalNoise(bFractal:Boolean):Void { _bFractalNoise = bFractal; }
    public function get fractalNoise():Boolean { return _bFractalNoise; }
    
    public function set displacementScaleX(dx:Number):Void { 
        if (isNaN(dx)) return;
        if (dx < 1) dx = 1;
        if (dx > 256) dx = 256;
        _displacementScaleX = dx; 
    }
    public function get displacementScaleX():Number { return _displacementScaleX; }
    
    ////////////////////////////////////////////////////////////////////////
    
    public function Marble()
    {
        _identityMatrix        = new Matrix();
        _identityColorTrans = new ColorTransform();
    }
    
    ////////////////////////////////////////////////////////////////////////
    // Convenience function.  Returns a bitmap of the desired
    // size using the current Marble settings.
    ///////////////////////////////////////////////////////////////////////
    
    public function createBitmap(w:Number,h:Number):BitmapData
    {
        var Marble_bmp:BitmapData = new BitmapData(w, h, false, 0x000000);
        render(Marble_bmp);
        return Marble_bmp;
    }
    
    /////////////////////////////////////////////////////////
    //  Render the Marble grain onto the bitmap using the 
    //  current property values.  
    //  buffer0_bmp and buffer1_bmp are optional.  If they 
    //  are not suppliedtemporary bitmaps will be created.  
    //  They MUST have the same width and height as 
    //  the destination bmp.
    ////////////////////////////////////////////////////////
    
    public function render(bmp:BitmapData):Void
    {
        var w:Number = bmp.width;
        var h:Number = bmp.height;
        
        // Needed in some of the following flash api calls.        
        var rect:Rectangle = new Rectangle(0,0,w,h);    
        var origin:Point = new Point(0,0);
        
        var mid:Number;
        var mult:Number;
        
        // The source bitmap needs to be larger than the destination bitmap because
        // DisplacementMapFilter will grab pixels from a larger area.
        
        var paddingX:Number = _displacementScaleX + 8; // Add 16 as slop
        
        var floor_bmp:BitmapData = new BitmapData(w+paddingX,h,false,0x000000);
        if (_bSinusoidalFloor){
            drawSine(floor_bmp, _waveLength);
            
            mid = _sinMidColorPt;
            mult = _sinContrastMultiplier;
        }
        else{
            floor_bmp.perlinNoise(_bPerlinFloorBaseX,_bPerlinFloorBaseY,1,
                                             _floorRandomSeed,false,true,4,false);
            mult = _perlinContrastMultiplier;
            mid = _perlinMidColorPt;
        }
        var offset:Number = mult * (mid-128);
        var ampColor:Array = [1, 0, 0, 0,   0,
                              0, 1, 0, 0,   0,
                              0, 0, mult, 0, offset,
                              0, 0, 0, 1,   0 ];
                               
        var ampColorFilter:ColorMatrixFilter = new ColorMatrixFilter(ampColor);
        floor_bmp.applyFilter(floor_bmp,floor_bmp.rectangle, origin, ampColorFilter);        
        
        /////////////////////////////////////////////////////////
        // Add the marble distortion.
        if (_bEnableDistortion){
            // Will hold perlin noise.
            var srcNoise_bmp:BitmapData = 
                              new BitmapData(w+paddingX, h, false, 0xffffffff);
        
            // channelOptions - 4 - blue only
            // grayscale - false
            srcNoise_bmp.perlinNoise(_baseX,_baseY,_nOctaves,_randomSeed,
                                                  false,_bFractalNoise,4,false);
        
            var filter:DisplacementMapFilter  = new 
                     DisplacementMapFilter(srcNoise_bmp,origin,4,1,
                                                      _displacementScaleX,0);
        
            var r:Rectangle = new Rectangle(paddingX/2,0,w,h);
            bmp.applyFilter(floor_bmp,r,new Point(0,0),filter);
        
            srcNoise_bmp.dispose();
        }
        else{
            bmp.copyPixels(floor_bmp,rect,origin);	
        }
        floor_bmp.dispose(); 
        
         // Change it from black and blue to the desired colors.
        bmp.applyFilter(bmp,rect, origin, getMarbleColorFilter());        
    }    
    
    private function drawSine(floor_bmp:BitmapData, wavelength:Number):Void
    {
        var w:Number = floor_bmp.width;
        
        // 1-pixel high bitmap.
        var tmp_bmp:BitmapData = new BitmapData(w, 1);
            
        var phaseInc:Number = 2 * Math.PI / wavelength;
        var phase:Number = 0;
        
        for (var x:Number = 0 ; x < w ; x++){
            phase += phaseInc;
            var colVal:Number = 127 * Math.cos(phase) + 128;
            colVal = Math.round(colVal);
            // We're dealing with blue only here.
            tmp_bmp.setPixel(x,0,colVal);
        }
        
        var stretchMatrix:Matrix = new Matrix();
        stretchMatrix.scale(1,floor_bmp.height);
        
        // Now, draw the movieclip onto the floor_bmp
        var blend:Object = 1; // normal
         floor_bmp.draw(tmp_bmp,stretchMatrix,_identityColorTrans, blend);
            
        // Clean up.
        tmp_bmp.dispose();
    }
    
    ///////////////////////////////////////////////////////////////////////
    // Map the black to blue colors to the desired Marble colors.
    ///////////////////////////////////////////////////////////////////////
    
    private function getMarbleColorFilter():ColorMatrixFilter
    {
        if (_marbleColorFilter != undefined){
            return _marbleColorFilter; 
        }    
        // Apply the desired colors to the bitmap.
        var r0:Number = (_rgb0 >> 16) & 0xff;
        var g0:Number = (_rgb0 >> 8 ) & 0xff;
        var b0:Number = _rgb0 & 0xff;
        var r1:Number = (_rgb1 >> 16) & 0xff;
        var g1:Number = (_rgb1 >> 8 ) & 0xff;
        var b1:Number = _rgb1 & 0xff;
        
        var marbleColor:Array = [0, 0, (r1-r0)/255, 0, r0,
                                 0, 0, (g1-g0)/255, 0, g0,
                                 0, 0, (b1-b0)/255, 0, b0,
                                 0, 0, 0, 1,    0 ];
                               
        _marbleColorFilter= new ColorMatrixFilter(marbleColor);
        
        return _marbleColorFilter;
    }    
    
}

Here are a few generated marbles.

Replacement image.

Comments

Woaaw, thanks for this source. Seems very powerful ;)
Cheers.
Ahmet

thank you source good post

rance industry abuses and denial of care. returned. Toporno film
-erotik film-inndir-şarkisini dinle-justin tv-yükle-full porno indir---konulu porno---liseli kizlar-sexsi-de the country, free land, and even newly-renovated “doctor living communities” that would be guarded for safety. Still, as our own financial meltdown has shown, all the money and property in the wfirikik-yesilcam pornosu-anal sikis-canli sikis--uzun pornolar----full porno indir

-recepivedik3sinemacekimi--kutsaldamacana2idman
xvideos--vidyo---">>günlükfilm-tikla indir-
pornotv
sicak videolar-kvp filistin--kurtlarvadisi filistin-xvideos--vidyo---">günlükfilm---tikla indir--ünlü pornosu---liseli porno--konulu pornolar--ysician isünlü pornosu quoted as saying çok filim hareketler bunlarthat youngerhint pornosu residents and interns are literally “studying the textbookslez porno # providing the largest middle class tax cut for health care in history, reducing premium costs for tens of millions of families and small business owners who are priced out of coverage today. justin izleThis helps over 31 million Americans afford health care who do not get it today – and makes coverage more affordable for many more.

. perilous swiss watches in stock the profits of the replica watches symptomatic dream up houses. designer replica This plant economy, according to Tim Phillips the watch replica writer of Knockoff: The best watch replica precarious bag effect pseudo Rolex watches replica Goods, would represent the world's biggest plan Gucci Wallet-Black GJ-029B-2725 if incarnate were a Louis Vuitton Wallet Monogram Denim named racket. Counterfeiting is replica handbag familiar to report for 7% of cosmos function further Sterling Silver Give Peace a Chance CZ Necklace outlet may equal for admirable Celebrity Inspired Earrings replicas because 10%.A excellent scale fake The Classic CZ Sterling Silver Dream Ring of traded goods, including garments further New Replica 2009 New Tiffany & Co Bracelets20090825032 accessories are sham. additional rolex watches alarming, is the thinking New Replica Tiffany & Co Bracelets tjewelrybracelet085 that these contents chalk up simulated Bedat & CO replica watches for sale goods that may act replica watch as impressed bag or replica watches used on the shape. admit - your body, my body, not

porno film izle porno izle
Forum forum programlar oyunlar
erotik hikayeler erotik hikayeler seks hikayeleri
dizi izle dizi izle yerli diziler online dizi izleme siteniz
bayan arkadaş partner ara, sevgili bul yata arkadası sex partner
sarkı indir
oteller oteller otel tanıtımı kralhotel