Away3D--------------關於太陽(光暈和上帝之光)的表現

一. 太陽光暈概述:



當我們把鏡頭對準某一發光體的時候,會有光暈現象產生,這種光暈現象也稱作“鏡頭眩光”。 在晴天的場景中實現這樣一個光暈特效,往往會使得場景更爲真實逼真。

二. 光暈效果顯示的基本原理:


        光暈特效實現的原理其實挺簡單,它可以由一個主光暈加一系列鏡頭光環組成。主光暈以光源爲中心,鏡頭光環則散列在由光源及鏡頭中心決定的直線上。

爲實現逼真的光暈效果首先需要準備一組大小形狀不同的光暈素材,以Away3D自帶的實例爲例:

               

在每幀繪製時,如果當前太陽的位置在屏幕中,則在太陽和屏幕中心點所在直線均勻放置若干個光暈素材的公告板即可
實現較爲真實的太陽光暈效果。




三. 光暈效果實現代碼:


定義光暈對象:

class FlareObject
{
private var flareSize:Number = 144;

public var sprite:Bitmap;

public var size:Number;

public var position:Number;

public var opacity:Number;

/**
* Constructor
*/
public function FlareObject(sprite:Bitmap, size:Number, position:Number, opacity:Number) 
{
this.sprite = new Bitmap(new BitmapData(sprite.bitmapData.width, sprite.bitmapData.height, true, 0xFFFFFFFF));
this.sprite.bitmapData.copyChannel(sprite.bitmapData, sprite.bitmapData.rect, new Point(), BitmapDataChannel.RED, BitmapDataChannel.ALPHA);
this.sprite.alpha = opacity/100;
this.sprite.smoothing = true;
this.sprite.scaleX = this.sprite.scaleY = size*flareSize/sprite.width;
this.size = size;
this.position = position;
this.opacity = opacity;
}
}
該類中包含sprite光暈圖片,通過設置其位置大小和alpha值來表現一個光暈效果.


初始化時創建一組光暈對象

private function initLensFlare():void
{
flares.push(new FlareObject(new Flare10(),  3.2, -0.01, 147.9));
flares.push(new FlareObject(new Flare11(),  6,    0,     30.6));
flares.push(new FlareObject(new Flare7(),   2,    0,     25.5));
flares.push(new FlareObject(new Flare7(),   4,    0,     17.85));
flares.push(new FlareObject(new Flare12(),  0.4,  0.32,  22.95));
flares.push(new FlareObject(new Flare6(),   1,    0.68,  20.4));
flares.push(new FlareObject(new Flare2(),   1.25, 1.1,   48.45));
flares.push(new FlareObject(new Flare3(),   1.75, 1.37,   7.65));
flares.push(new FlareObject(new Flare4(),   2.75, 1.85,  12.75));
flares.push(new FlareObject(new Flare8(),   0.5,  2.21,  33.15));
flares.push(new FlareObject(new Flare6(),   4,    2.5,   10.4));
flares.push(new FlareObject(new Flare7(),   10,   2.66,  50));
}


每幀根據太陽位置計算是否顯示光暈 以及顯示光暈的位置等信息

private function updateFlares():void
{
var flareVisibleOld:Boolean = flareVisible;

var sunScreenPosition:Vector3D = view.project(sun.scenePosition);
var xOffset:Number = sunScreenPosition.x - stage.stageWidth/2;
var yOffset:Number = sunScreenPosition.y - stage.stageHeight/2;

var earthScreenPosition:Vector3D = view.project(earth.scenePosition);
var earthRadius:Number = 190*stage.stageHeight/earthScreenPosition.z;
var flareObject:FlareObject;

flareVisible = (sunScreenPosition.x > 0 && sunScreenPosition.x < stage.stageWidth && sunScreenPosition.y > 0 && sunScreenPosition.y  < stage.stageHeight && sunScreenPosition.z > 0 && Math.sqrt(xOffset*xOffset + yOffset*yOffset) > earthRadius)? true : false;

//update flare visibility
if (flareVisible != flareVisibleOld) {
for each (flareObject in flares) {
if (flareVisible)
addChild(flareObject.sprite);
else
removeChild(flareObject.sprite);
}
}

//update flare position
if (flareVisible) {
var flareDirection:Point = new Point(xOffset, yOffset);
for each (flareObject in flares) {
flareObject.sprite.x = sunScreenPosition.x - flareDirection.x*flareObject.position - flareObject.sprite.width/2;
flareObject.sprite.y = sunScreenPosition.y - flareDirection.y*flareObject.position - flareObject.sprite.height/2;
}
}
}


四. 光暈效果:







五. 上帝之光效果:




當我們把透過樹葉等縫隙觀察發光源時會出現一道道明顯的光線,被稱爲上帝之光效果。如下圖:




五. 上帝之光實現原理:


我們對上帝之光的繪製採用後處理的方式,首先在每幀繪製時先繪製到紋理(rtt),定位到發光源(如太陽)在屏幕空間的位置,然後對每個頂點到發光源位置的線段上進行n次採樣並加以權重值求和作爲該點的最終顏色信息,最終再將此紋理會知道後臺緩衝區。


五. 上帝之光AS3濾鏡方式實現代碼:


package away3d.filters.tasks
{
import flash.display3D.Context3DProgramType;
import flash.display3D.textures.Texture;

import away3d.cameras.Camera3D;
import away3d.core.managers.Stage3DProxy;
import away3d.filters.tasks.Filter3DTaskBase;

/**
* GodRays Effect
* @author vancopper

*/
public class Filter3DGodRayTask extends Filter3DTaskBase
{
private var _numSteps:int = 30;
private var _lightPos:Vector.<Number> = Vector.<Number>([.5, .5, 1, 1]);
private var _values1:Vector.<Number> = Vector.<Number>([1, 1, 1, 1]);//numsamples, density, numsamples*density, 1 / numsamples * density
private var _values2:Vector.<Number> = Vector.<Number>([1, 1, 1, 1]);//weight, decay, exposure

private var _lightX:Number = 0;
private var _lightY:Number = 0;
private var _weight:Number = .5;
private var _decay:Number = .87;
private var _exposure:Number = .35;
private var _density:Number = 2.0;

public function Filter3DGodRayTask()
{
// super(requireDepthRender);
}

override protected function getFragmentCode():String
{
var code:String = "";

// Calculate vector from pixel to light source in screen space.
code += "sub ft0.xy, v0.xy, fc0.xy \n";

// Divide by number of samples and scale by control factor.  
code += "mul ft0.xy, ft0.xy, fc1.ww \n";

// Store initial sample.  
code += "tex ft1,  v0, fs0 <2d, clamp, linear, mipnone> \n";

// Set up illumination decay factor.  
code += "mov ft2.x, fc0.w \n";

// Store the texcoords
code += "mov ft4.xy, v0.xy \n";

for (var i:int = 0; i < _numSteps; i++)
{
// Step sample location along ray. 
code += "sub ft4.xy, ft4.xy, ft0.xy \n";

// Retrieve sample at new location.  
code += "tex ft3,  ft4.xy, fs0 <2d, clamp, linear, mipnone> \n";

// Apply sample attenuation scale/decay factors.  
code += "mul ft2.y, ft2.x, fc2.x \n";
code += "mul ft3.xyz, ft3.xyz, ft2.yyy \n";

// Accumulate combined color.  
code += "add ft1.xyz, ft1.xyz, ft3.xyz \n";

// Update exponential decay factor.  
code += "mul ft2.x, ft2.x, fc2.y \n";
}

// Output final color with a further scale control factor. 
code += "mul ft1.xyz, ft1.xyz, fc2.zzz \n";
code += "mov oc, ft1";
return code;
}

override public function activate(stage3DProxy:Stage3DProxy, camera:Camera3D, depthTexture:Texture):void
{
// light position
_lightPos[0] = this._lightX / stage3DProxy.width;
_lightPos[1] = this._lightY / stage3DProxy.height;

// numsamples, density, numsamples * density, 1 / numsamples * density
_values1[0] = _numSteps;
_values1[1] = this._density;
_values1[2] = _numSteps * _values1[1];
_values1[3] = 1 / _values1[2];

// weight, decay, exposure
_values2[0] = this._weight;
_values2[1] = this._decay;
_values2[2] = this._exposure;

stage3DProxy.context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, _lightPos, 1 );
stage3DProxy.context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 1, _values1,  1 );
stage3DProxy.context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 2, _values2,  1 );

}

public function set lightX(value:Number):void { this._lightX = value; }
public function get lightX():Number { return this._lightX; }

public function set lightY(value:Number):void { this._lightY = value; }
public function get lightY():Number { return this._lightY; }

public function set decay(value:Number):void { this._decay = value; }
public function get decay():Number { return this._decay; }

public function set exposure(value:Number):void { this._exposure = value; }
public function get exposure():Number { return this._exposure; }

public function set weight(value:Number):void { this._weight = value; }
public function get weight():Number { return this._weight; }

public function set density(value:Number):void { this._density = value; }
public function get density():Number { return this._density; }
}
}


package away3d.filters
{
import away3d.filters.Filter3DBase;
import away3d.filters.tasks.Filter3DGodRayTask;


/**
* GodRays Effect
* @author vancopper

*/
public class GodRayFilter3D extends Filter3DBase
{

private var _godRaysTask:Filter3DGodRayTask;


public function GodRayFilter3D()
{
super();
_godRaysTask = new Filter3DGodRayTask();
addTask(_godRaysTask);
}

public function set lightX(value:Number):void { _godRaysTask.lightX = value; }
public function get lightX():Number { return _godRaysTask.lightX; }

public function set lightY(value:Number):void { _godRaysTask.lightY = value; }
public function get lightY():Number { return _godRaysTask.lightY; }

public function set decay(value:Number):void { _godRaysTask.decay = value; }
public function get decay():Number { return _godRaysTask.decay; }

public function set exposure(value:Number):void { _godRaysTask.exposure = value; }
public function get exposure():Number { return _godRaysTask.exposure; }

public function set weight(value:Number):void { _godRaysTask.weight = value; }
public function get weight():Number { return _godRaysTask.weight; }

public function set density(value:Number):void { _godRaysTask.density = value; }
public function get density():Number { return _godRaysTask.density; }
}
}

只需要在必要的時候開啓此濾鏡即可實現上帝之光效果
發佈了25 篇原創文章 · 獲贊 2 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章