Realistic Sunlight through Clouds
Brad Sedito
Cloud and sun blending to imitate nature for realism
Feel free to add suggestions :)
Let's begin....
/**
* Copyright bradsedito ( http://wonderfl.net/user/bradsedito )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/dUzj
*/
// Brad Sedito
// Cloud and sun blending to imitate nature for realism
// Feel free to add suggestions :)
// Let's begin....
package {
import flash.display.*;
import flash.events.*;
import flash.geom.*;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.media.SoundLoaderContext;
import flash.media.SoundTransform;
import flash.net.URLRequest;
import flash.utils.Timer;
import idv.cjcat.stardust.twoD.emitters.Emitter2D;
import com.greensock.*;
import com.greensock.easing.*;
import net.hires.debug.Stats;
public class HORIZON extends Sprite
{
public static const WIDTH: Number = 450 ;
public static const HEIGHT: Number = 450 ;
private var debug:Boolean = false;
private var sun:SunLight;
private var entities:Vector.<Entity> = new Vector.<Entity>();
private var renderedScene:BitmapData = new BitmapData(WIDTH, HEIGHT);
private var scene:Sprite = new Sprite;
public static const CLOUD_NUM:int = 15;
public static const ERROR_SEEDS:Array = [346, 514, 1155, 1519, 1690, 1977, 2327, 2337, 2399, 2860, 2999, 3099, 4777, 4952, 5673, 6265, 7185, 7259, 7371, 7383, 7717, 7847, 8032, 8350, 8676, 8963, 8997, 9080, 9403, 9615, 9685];
public var loading:LoadingScene;
// perlinNoise
public function HORIZON():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
loading = new LoadingScene();
addChild(loading);
addEventListener(Event.ENTER_FRAME, onLoading);
addEventListener(Event.ADDED_TO_STAGE, loop);
/* var mc:Sprite = new Sprite();
mc.graphics.drawCircle(0,0,30);
mc.graphics.beginFill(0x00000000, 1);
// mc.x = stage.mouseX; mc.y = stage.mouseY;
mc.x = 465/2; mc.y = 465/2;
addChild(mc);
TweenMax.to(mc, 3, {x:mc.x + 30, yoyo:true, ease:Cubic.easeInOut});
}
*/
}
// Cloud Constants
public static const FOCUS:Number = 300;
public static const MAX_Z:Number = 2000;
public static const MIN_Z:Number = 400;
private function onLoading(...arg):void {
for (var i:int = 0; i < 3; i++) {
if(Clouds.bmps.length >= CLOUD_NUM){
start();
break;
}
var seed:int = Math.random() * 10000 + 1;
if (ERROR_SEEDS.indexOf(seed) >= 0) seed++;
var ct:Number = (Math.random() < 0.2)? Math.random() * 0.3 : Math.random() * 1.5;
var z:Number = MAX_Z - (MAX_Z-MIN_Z)/CLOUD_NUM*(Clouds.bmps.length);
var w:Number = 400/(z/FOCUS);
var h:Number = 200/(z/FOCUS);
var bmp:Bitmap = new Bitmap(Painter.createCloud(w, h, seed, ct, Color.cloudBase, Color.cloudLight, Color.cloudShadow));
bmp.x = Math.random() * (HORIZON.WIDTH + bmp.width) - bmp.width;
bmp.y = HEIGHT*0.6 - HEIGHT*0.5 * FOCUS/z;
Clouds.bmps.push(bmp);
loading.setProgress(Clouds.bmps.length / CLOUD_NUM);
}
}
public function start():void
{
removeEventListener(Event.ENTER_FRAME, onLoading);
removeChild(loading);
stage.quality = StageQuality.BEST;
var matrix:Matrix = new Matrix();
matrix.createGradientBox(WIDTH, HEIGHT, Math.PI / 2);
graphics.beginGradientFill(GradientType.LINEAR, [0x3A6CFF, 0x53ADFF], null, [0, 128], matrix);
// graphics.beginGradientFill(GradientType.LINEAR, [0x51484A, 0x96644E], null, [0, 128], matrix);
graphics.drawRect(0, 0, WIDTH, HEIGHT);
graphics.endFill();
var clouds:Clouds = new Clouds;
entities.push(scene.addChild(clouds));
var fogR:Number = 0x70;
var fogG:Number = 0x70;
var fogB:Number = 0x70;
addChild(scene);
addChild(sun = new SunLight(renderedScene));
var outline:Shape = new Shape();
var g:Graphics = outline.graphics;
g.lineStyle(1, 0x808080);
g.drawRect( -1, -1, WIDTH + 2, HEIGHT + 2);
addChild(outline);
restoreFilters(debug);
// Play Audio
// playSound();
stage.addEventListener(MouseEvent.CLICK, clickHandler);
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
private function clickHandler(e:MouseEvent):void
{
debug = !debug;
var matrix:Matrix = new Matrix();
if (debug) {
// Transform Matrix。
matrix.scale(0.2, 0.2);
matrix.translate(WIDTH * 0.4, HEIGHT * 0.4);
}
transform.matrix = matrix;
restoreFilters(debug);
}
private function restoreFilters(debug:Boolean):void
{
for each (var entity:Entity in entities)
{
entity.restoreFilter(debug);
}
}
private function enterFrameHandler(e:Event):void
{
for each (var entity:Entity in entities)
{
entity.update();
}
renderedScene.fillRect(renderedScene.rect, 0);
renderedScene.draw(scene);
sun.update();
}
}
}
import flash.display.*;
import flash.filters.*;
import flash.geom.*;
class Entity extends Sprite
{
public function update():void { };
public function restoreFilter(debug:Boolean):void { };
}
class SunLight extends Entity {
public static const FXW:int = 0x100;
public static const FXH:int = 0x100;
private var src:BitmapData = new BitmapData(FXW, FXH, true, 0);
private var dst:BitmapData = new BitmapData(FXW, FXH, true, 0);
private var sun:Shape = new Shape;
private var obstruction:Bitmap;
private var scaleDown:Matrix = new Matrix;
private var scaleUp:Matrix = new Matrix;
private var mtx:Matrix = new Matrix;
private var canvas:Bitmap = new Bitmap(dst);
private var blur:BlurFilter = new BlurFilter(3, 3, 3);
public function SunLight(obstruction:BitmapData) {
this.blendMode = "add";
this.obstruction = new Bitmap(obstruction);
var m:Matrix = new Matrix;
m.createGradientBox(FXW, FXH, 0, 0, 0);
sun.graphics.beginGradientFill("radial", [0x0C0a08, 0x0a0806, 0x060504, 0x020201, 0], [1, 1, 1, 1, 1], [0, 10, 34, 64, 255], m);
sun.graphics.drawRect(0, 0, FXW*4, FXH);
sun.graphics.endFill();
sun.cacheAsBitmap = true;
scaleDown.scale(FXW/HORIZON.WIDTH, FXH/HORIZON.HEIGHT);
scaleUp.scale(HORIZON.WIDTH/FXW, HORIZON.HEIGHT/FXH);
addChild(canvas);
transform.matrix = scaleUp;
}
public override function update():void {
src.lock();
dst.lock();
src.fillRect(src.rect, 0);
src.draw(sun);
src.draw(obstruction, scaleDown, null, "erase");
canvas.bitmapData = process(src);
src.unlock();
dst.unlock();
// this.x = (FXW/mouseX)/2;
// this.y = mouseY;
}
private function process(src:BitmapData):BitmapData {
var dst:BitmapData = this.dst;
mtx.identity();
mtx.translate(-FXW/34, -FXH/34);
mtx.scale(17/16, 17/16);
var cnt:int = 5;
var tmp:BitmapData;
while(cnt--) {
mtx.concat(mtx);
dst.copyPixels(src, src.rect, src.rect.topLeft);
dst.draw(src, mtx, null, "add");
dst.applyFilter(dst, dst.rect, dst.rect.topLeft, blur);
tmp = src;
src = dst;
dst = tmp;
}
return src;
}
}
import flash.display.*;
import flash.events.*;
class Clouds extends Entity
{
public static var bmps:Array = [];
public function Clouds()
{
for each(var bmp:Bitmap in bmps) addChild(bmp);
}
public override function update():void {
for each(var bmp:Bitmap in bmps) {
bmp.x -= bmp.width * 0.001;
if(bmp.x + bmp.width < 0) {
bmp.x = HORIZON.WIDTH;
}
}
}
}
const SPEED:Number = 300;
function lerp(n0:Number, n1:Number, p:Number):Number
{
return n0 * (1 - p) + n1 * p;
}
// [min, max
function rnd(min:Number, max:Number):Number
{
return min + Math.random() * (max - min);
// return lerp(min, max, Math.random());
}
class Color {
static public var cloudBase :uint = 0xFFFFFF;
static public var cloudLight :uint = 0xE0E0E0;
static public var cloudShadow :uint = 0x8C8C8C;
}
class LoadingScene extends Sprite {
private var _lineWidth:Number = 200;
private var _loadedLine:Sprite;
public function LoadingScene() {
var bg:Sprite = addChild(Painter.createGradientRect(HORIZON.WIDTH, HORIZON.HEIGHT, [0x000000], [1])) as Sprite;
var baseLine:Sprite = addChild(Painter.createGradientRect(_lineWidth, 2, [0x444444], [1])) as Sprite;
_loadedLine = addChild(Painter.createGradientRect(_lineWidth, 2, [0x7DA3C8], [1])) as Sprite;
baseLine.x = _loadedLine.x = int((HORIZON.WIDTH - _lineWidth) / 2);
baseLine.y = _loadedLine.y = int((HORIZON.HEIGHT - baseLine.height) / 2);
setProgress(0);
}
public function setProgress(per:Number):void {
_loadedLine.width = _lineWidth * per;
}
}
class Painter {
/**
* @param width
* @param height
* @param seed
* @param contrast
* @param color
* @param light
* @param shadow
*/
static public function createCloud(width:int, height:int, seed:int, contrast:Number = 1, color:uint = 0xFFFFFF, light:uint = 0xFFFFFF, shadow:uint = 0xDDDDDD):BitmapData {
var gradiation:Sprite = new Sprite();
var drawMatrix:Matrix = new Matrix();
drawMatrix.createGradientBox(width, height);
gradiation.graphics.beginGradientFill("radial", [0x000000, 0x000000], [0, 1], [0, 255], drawMatrix);
gradiation.graphics.drawRect(0, 0, width, height);
gradiation.graphics.endFill();
var alphaBmp:BitmapData = new BitmapData(width, height);
alphaBmp.perlinNoise(width / 3, height / 2.5, 5, seed, false, true, 1|2|4, true);
var zoom:Number = 1 + (contrast - 0.1) / (contrast + 0.9);
if (contrast < 0.1) zoom = 1;
if (contrast > 2.0) zoom = 2;
var ctMatrix:Array = [contrast + 1, 0, 0, 0, -128 * contrast, 0, contrast + 1, 0, 0, -128 * contrast, 0, 0, contrast + 1, 0, -128 * contrast, 0, 0, 0, 1, 0];
alphaBmp.draw(gradiation, new Matrix(zoom, 0, 0, zoom, -(zoom - 1) / 2 * width, -(zoom - 1) / 2 * height));
alphaBmp.applyFilter(alphaBmp, alphaBmp.rect, new Point(), new ColorMatrixFilter(ctMatrix));
var image:BitmapData = new BitmapData(width, height, true, 0xFF << 24 | color);
image.copyChannel(alphaBmp, alphaBmp.rect, new Point(), 4, 8);
image.applyFilter(image, image.rect, new Point(), new GlowFilter(light, 1, 4, 4, 1, 3, true));
var bevelSize:Number = Math.min(width, height) / 30;
image.applyFilter(image, image.rect, new Point(), new BevelFilter(bevelSize, 45, light, 1, shadow, 1, bevelSize/5, bevelSize/5, 1, 3));
var image2:BitmapData = new BitmapData(width, height, true, 0);
image2.draw(Painter.createGradientRect(width, height, [light, color, shadow], [1, 0.2, 1], null, 90), null, null, BlendMode.MULTIPLY);
image2.copyChannel(alphaBmp, alphaBmp.rect, new Point(), 4, 8);
image.draw(image2, null, null, BlendMode.MULTIPLY);
alphaBmp.dispose();
return image;
}
static public function
createGradientRect(width:Number, height:Number, colors:Array, alphas:Array, ratios:Array = null, rotation:Number = 0):Sprite {
var i:int, rts:Array = new Array();
if(ratios == null) for (i = 0; i < colors.length; i++) rts.push(int(255 * i / (colors.length - 1)));
else for (i = 0; i < ratios.length; i++) rts[i] = Math.round(ratios[i] * 255);
var sp:Sprite = new Sprite();
var mtx:Matrix = new Matrix();
mtx.createGradientBox(width, height, Math.PI / 180 * rotation, 0, 0);
if (colors.length == 1 && alphas.length == 1) sp.graphics.beginFill(colors[0], alphas[0]);
else sp.graphics.beginGradientFill("linear", colors, alphas, rts, mtx);
sp.graphics.drawRect(0, 0, width, height);
sp.graphics.endFill();
return sp;
}
}