Smooth Shading Sphere Sample
Stage3DのAGALで着色のサンプルです。
スムースシェーディングの手法で光源との内積を使用して各頂点の色を決定しています。
それぞれアルファ、明度、彩度、コントラストを変化させてます。
click to change among Alpha, Brightness, Saturation, Contrast and None.
/**
* Copyright yuichiroharai ( http://wonderfl.net/user/yuichiroharai )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/seQd
*/
package
{
import com.adobe.utils.AGALMiniAssembler;
import com.adobe.utils.PerspectiveMatrix3D;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.Stage3D;
import flash.display.StageAlign;
import flash.display.StageQuality;
import flash.display.StageScaleMode;
import flash.display3D.Context3D;
import flash.display3D.Context3DBlendFactor;
import flash.display3D.Context3DCompareMode;
import flash.display3D.Context3DProgramType;
import flash.display3D.Context3DRenderMode;
import flash.display3D.Context3DTriangleFace;
import flash.display3D.Context3DVertexBufferFormat;
import flash.display3D.IndexBuffer3D;
import flash.display3D.Program3D;
import flash.display3D.VertexBuffer3D;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Matrix3D;
import flash.geom.Rectangle;
import flash.geom.Vector3D;
import flash.net.URLRequest;
import flash.system.ApplicationDomain;
import flash.system.LoaderContext;
import flash.system.SecurityDomain;
import flash.text.AntiAliasType;
import flash.text.Font;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.utils.getTimer;
import net.hires.debug.Stats;
[SWF(width="465", height="465", backgroundColor="0x000000")]
public class SmoothShadingSample extends Sprite
{
// ----------------------------------------------------------------------------------------------------
// 変数
//
// Stage 関連
private var _stageWidth:uint;
private var _stageHeight:uint;
private var _stageWidthHarf:uint;
private var _stageHeightHarf:uint;
// Stage3D 関連
static private const _SEGMENT:uint = 64;
private var _stage3D:Stage3D;
private var _context3D:Context3D;
private var _stage3DSize:uint;
private var _vertexList:Vector.<Number>;
private var _indexList:Vector.<uint>;
private var _indexBuffer:IndexBuffer3D;
private var _indexNum:uint;
private var _perspectiveMatrix3D:PerspectiveMatrix3D;
private var _time:uint;
private var _rotationX:Number = 0;
private var _rotationY:Number = 0;
private var _rotationHue:Number = 0;
// AGAL 関連
private var _agalVertexCommon:String;
private var _agalVertexNone:String;
private var _agalVertexAlpha:String;
private var _agalVertexBrightness:String;
private var _agalVertexSaturation:String;
private var _agalVertexContrast:String;
private var _agalFragment:String;
private var _agalType:uint;
// テキスト
static private const TEXT_LIST:Vector.<String> = Vector.<String>([
"NONE", "ALPHA (0~1)", "BRIGHTNESS (-1~1)", "SATURATION (-1~1)", "CONTRAST (-1~1)"
]);
private var _textField:TextField;
private var _textFormat:TextFormat;
// カラーサンプル
private const COLOR_SIZE:uint = 20;
private const COLOR_RECT:Rectangle = new Rectangle(0, 0, COLOR_SIZE, COLOR_SIZE);
private var _bmpColor:Bitmap;
// スクリーンショット
//private var _screenShot:Bitmap;
// ----------------------------------------------------------------------------------------------------
// コンストラクタ
//
// コンストラクタ
public function SmoothShadingSample():void
{
Wonderfl.disable_capture();
//addChild(_screenShot = new Bitmap());
// ステージ設定
stage.quality = StageQuality.BEST;
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.frameRate = 60;
// ステージイベント
stage.addEventListener(Event.FULLSCREEN, _atResize);
stage.addEventListener(Event.RESIZE, _atResize);
_atResize();
_initStage3D();
// スタッツ
addChild(new Stats());
}
// ----------------------------------------------------------------------------------------------------
// ステージイベント
//
// ステージのリサイズ
private function _atResize(e:Event=null):void
{
_stageWidth = stage.stageWidth;
_stageHeight = stage.stageHeight;
_stageWidthHarf = uint(_stageWidth/2);
_stageHeightHarf = uint(_stageHeight/2);
if (_context3D != null) _resizeStage3D();
if (_textField != null) _resizeText();
if (_bmpColor != null) _resizeColor();
}
// ----------------------------------------------------------------------------------------------------
// Stage3D
//
// Stage3D の初期化
private function _initStage3D():void
{
_stage3D = stage.stage3Ds[0];
_stage3D.addEventListener(Event.CONTEXT3D_CREATE, _onContext3dCreate);
_stage3D.requestContext3D(Context3DRenderMode.AUTO);
}
// Stage3D の初期化完了
private function _onContext3dCreate(e:Event):void
{
// Context3D 初期化
_context3D = _stage3D.context3D;
//_context3D.enableErrorChecking = true;
_context3D.setDepthTest(true, Context3DCompareMode.LESS);
_context3D.setCulling(Context3DTriangleFace.NONE);
_context3D.setBlendFactors(Context3DBlendFactor.SOURCE_ALPHA, Context3DBlendFactor.ZERO);
_resizeStage3D();
_context3D.clear(0, 0, 0);
_context3D.present();
_initAgal();
_setAGAL(_agalType = 2);
_initSphere();
_initText();
_initColor();
// 透視投影変換用 Matrix3D
_perspectiveMatrix3D = new PerspectiveMatrix3D();
_perspectiveMatrix3D.perspectiveFieldOfViewLH(Math.PI/4, 1, 0.1, 1000);
// 定数アップロード
_uploadVertexConstFromMatrix(0, _perspectiveMatrix3D);
_uploadVertexConstFromVector(8, Vector.<Number>([0, 0.5, 1, 3])); // 定数
_uploadVertexConstFromVector(10, Vector.<Number>([0, 1, 0, 4])); // 光源(xyz), 球体の画面奥への平行移動(w)
addEventListener(Event.ENTER_FRAME, _drawStage3D);
stage.addEventListener(MouseEvent.CLICK, _changeShadingType);
}
// 毎フレームの描画処理
private function _drawStage3D(e:Event):void
{
var currentTime:uint, rotationMatrix:Matrix3D, translationMatrix:Matrix3D, rgb:Vector.<Number>;
// 時間で回転角度を決定
_time = (currentTime = getTimer()) - _time;
if ((_rotationY += 0.12 * _time) > 360) _rotationY -= 360;
if ((_rotationX += 0.09 * _time) > 360) _rotationX -= 360;
if ((_rotationHue += 0.02 * _time/360) > 1) _rotationHue -= 1;
_time = currentTime;
// 色生成 コントラストの時のみ明るさを0.75へ
rgb = _hsbToRGB(_rotationHue, 1, (_agalType == 4) ? 0.75 : 1);
_changeColor(_uintRgb(rgb[0], rgb[1], rgb[2]));
// 回転の Matrix3D を生成
rotationMatrix = new Matrix3D();
rotationMatrix.appendRotation(_rotationY, Vector3D.X_AXIS);
rotationMatrix.appendRotation(_rotationX, Vector3D.Y_AXIS);
// 定数アップロード
_uploadVertexConstFromMatrix(4, rotationMatrix);
_uploadVertexConstFromVector(9, Vector.<Number>([rgb[0], rgb[1], rgb[2], 1])); // 着色用
// 描画
_context3D.clear(0, 0, 0);
_context3D.drawTriangles(_indexBuffer, 0, _indexNum);
//_context3D.drawToBitmapData(_screenShot.bitmapData);
_context3D.present();
}
// Stage3D のリサイズ
private function _resizeStage3D():void {
// サイズ
_stage3DSize = (_stageWidth > _stageHeight) ? _stageHeightHarf : _stageWidthHarf;
_stage3DSize = _stage3DSize * 2; // 偶数になるように調整
if (_stage3DSize < 466) _stage3DSize = 466;
// 配置
_stage3D.x = _stageWidthHarf - _stage3DSize/2;
_stage3D.y = _stageHeightHarf - _stage3DSize/2;
// バッファサイズの初期化
_context3D.configureBackBuffer(_stage3DSize, _stage3DSize, 16, true);
/*if (_screenShot.bitmapData != null) _screenShot.bitmapData.dispose();
_screenShot.bitmapData = new BitmapData(_stage3DSize,_stage3DSize, false, 0);
_screenShot.x = _stage3D.x;
_screenShot.y = _stage3D.y;*/
}
// シェーディングの種類を変更
private function _changeShadingType(e:MouseEvent):void
{
if (++_agalType > 4) _agalType -= 5;
_setAGAL(_agalType);
_changeText();
}
// ----------------------------------------------------------------------------------------------------
// Stage3D - 球面データ
//
// 球面の初期化
private function _initSphere():void
{
var i:uint, j:uint, rotX:Number, rotY:Number, segmentPlus:uint, v:Vector3D, m:Matrix3D,
vertexBuffer:VertexBuffer3D, vertexNum:uint;
_vertexList = new Vector.<Number>;
_indexList = new Vector.<uint>;
segmentPlus = _SEGMENT + 1;
m = new Matrix3D();
// 頂点リストにデータを追加
for (j=0;j<segmentPlus;j++) {
rotY = 180 * (j / _SEGMENT - 0.5);
for (i=0;i<segmentPlus;i++) {
rotX = 360 * (i / _SEGMENT - 0.5);
// 真正面(x=0, y=0, z=-1)を基準点として球面の頂点を生成
v = new Vector3D(0, 0, -1, 0);
m.identity();
m.appendRotation(-rotY, Vector3D.X_AXIS);
m.appendRotation(-rotX, Vector3D.Y_AXIS);
v = m.deltaTransformVector(v);
// 色は後で着色
_vertexList.push(v.x, v.y, v.z, 1, 1, 1, 1, 1);
}
}
// 頂点バッファを生成 & アップロード
vertexNum = _vertexList.length/8;
vertexBuffer = _context3D.createVertexBuffer(vertexNum, 8);
vertexBuffer.uploadFromVector(_vertexList, 0, vertexNum);
_context3D.setVertexBufferAt(0, vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_4);
_context3D.setVertexBufferAt(1, vertexBuffer, 4, Context3DVertexBufferFormat.FLOAT_4);
// インデックスリストにデータを追加
for (j=0;j<_SEGMENT;j++) {
for (i=0;i<_SEGMENT;i++) {
_indexList.push(j*segmentPlus + i, (j+1)*segmentPlus + i+1, (j+1)*segmentPlus + i);
_indexList.push(j*segmentPlus + i, j*segmentPlus + i+1, (j+1)*segmentPlus + i+1);
}
}
// インデックスバッファを生成
_indexNum = _indexList.length/3;
_indexBuffer = _context3D.createIndexBuffer(_indexList.length);
_indexBuffer.uploadFromVector(_indexList, 0, _indexList.length);
}
// ----------------------------------------------------------------------------------------------------
// Stage3D - 定数
//
// Matrix3D から定数のアップロード
private function _uploadVertexConstFromMatrix(i:uint, data:Matrix3D):void
{
_context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, i, data, true);
}
// Vector から定数のアップロード
private function _uploadVertexConstFromVector(i:uint, data:Vector.<Number>):void
{
_context3D.setProgramConstantsFromVector(Context3DProgramType.VERTEX, i, data, data.length/4);
}
// ----------------------------------------------------------------------------------------------------
// Stage3D - AGAL
//
/*
* AGALの生成
*
* vc0-3 : 透視投影変換用の Matrix3D データ
* vc4-7 : 球体回転用の Matrix3D データ
* vc8.x : 定数 0
* vc8.y : 定数 0.5
* vc8.z : 定数 1
* vc8.w : 定数 3
* vc9 : 着色用の RGBA データ
* vc10.xyz : 光源用のベクトルデータ(0, 0, -1)
* vc10.w : 球体の画面奥への平行移動用の定数
*
* vt0 : 座標データを回転したもの
* vt1 : さらに画面奥に平行移動したもの
* vt2 : 色データの格納用
* vt3.x : 頂点と光源の内積(-1 ~ 1)
* vt3.y : (1-内積)/3
* vt3.z : (1+内積)
* vt3.w : (-0.5*内積)
* vt4-6 : 彩度やコントラスト用の3×3行列
*/
private function _initAgal():void
{
// 頂点シェーダー 共通
_agalVertexCommon = "m44 vt0, va0, vc4 \n"; // 球体を回転
_agalVertexCommon += "mov vt1, vt0 \n"; // 座標をいったんコピー
_agalVertexCommon += "add vt1.z, vt1.z, vc10.w \n"; // Z座標のみ画面奥へ平行移動
_agalVertexCommon += "m44 op, vt1, vc0 \n"; // 透視投影変換して出力
_agalVertexCommon += "mov vt2, va1 \n"; // 元の色の取りだし(結局、使ってないけどエラー防止)
_agalVertexCommon += "mov vt2, vc9 \n"; // 元の色を使わず着色用の定数をコピー
_agalVertexCommon += "dp3 vt3.x, va0, vc10 \n"; // 元の座標と光源から内積を計算
// 何もなし
_agalVertexNone = "mov v0, vt2 \n"; // 色を出力
// アルファ
_agalVertexAlpha = "add vt3.x, vt3.x, vc8.z \n"; // (内積+1)
_agalVertexAlpha += "mul vt3.x, vt3.x, vc8.y \n"; // (内積+1)*0.5
_agalVertexAlpha += "mov vt2.w, vt3.x \n"; // アルファに(内積+1)*0.5を適用
_agalVertexAlpha += "mov v0, vt2"; // 色を出力
// 明度
_agalVertexBrightness = "add vt2.xyz, vt2.xyz, vt3.x \n"; // 各色に内積を足す
_agalVertexBrightness += "mov v0, vt2"; // 色を出力
// 彩度
_agalVertexSaturation = "sub vt3.y, vc8.z, vt3.x \n"; // (1-内積)
_agalVertexSaturation += "div vt3.y, vt3.y, vc8.w \n"; // (1-内積)/3
_agalVertexSaturation += "mov vt4.xyz, vt3.y \n"; // xyzに(1-内積)/3をコピー
_agalVertexSaturation += "mov vt5.xyz, vt3.y \n"; // xyzに(1-内積)/3をコピー
_agalVertexSaturation += "mov vt6.xyz, vt3.y \n"; // xyzに(1-内積)/3をコピー
_agalVertexSaturation += "mov vt4.w, vc8.x \n"; // wに0をコピー(エラー防止)
_agalVertexSaturation += "mov vt5.w, vc8.x \n"; // wに0をコピー(エラー防止)
_agalVertexSaturation += "mov vt6.w, vc8.x \n"; // wに0をコピー(エラー防止)
_agalVertexSaturation += "add vt4.x, vt4.x, vt3.x \n"; // xに内積を足す
_agalVertexSaturation += "add vt5.y, vt5.y, vt3.x \n"; // yに内積を足す
_agalVertexSaturation += "add vt6.z, vt6.z, vt3.x \n"; // zに内積を足す
_agalVertexSaturation += "m33 vt2.xyz, vt2.xyz, vt4 \n";// vt2 と vt4-6の 行列掛け算
_agalVertexSaturation += "mov v0, vt2"; // 色を出力
// コントラスト
_agalVertexContrast = "add vt3.z, vc8.z, vt3.x \n"; // (1+内積)
_agalVertexContrast += "mul vt3.w, vc8.y, vt3.x \n"; // (0.5*内積)
_agalVertexContrast += "neg vt3.w, vt3.w \n"; // (-0.5*内積)
_agalVertexContrast += "mov vt4, vc8.x \n"; // 全部に0をコピー
_agalVertexContrast += "mov vt5, vc8.x \n"; // 全部に0をコピー
_agalVertexContrast += "mov vt6, vc8.x \n"; // 全部に0をコピー
_agalVertexContrast += "mov vt4.x, vt3.z \n"; // xに(1+内積)をコピー
_agalVertexContrast += "mov vt5.y, vt3.z \n"; // yに(1+内積)をコピー
_agalVertexContrast += "mov vt6.z, vt3.z \n"; // zに(1+内積)をコピー
_agalVertexContrast += "m33 vt2.xyz, vt2.xyz, vt4 \n"; // vt2 と vt4-6の 行列掛け算
_agalVertexContrast += "add vt2.xyz, vt2.xyz, vt3.w \n";// 各色に(-0.5*内積)を足す
_agalVertexContrast += "mov v0, vt2"; // 色を出力
// 断片シェーダー
_agalFragment = "mov oc, v0";
}
// AGALをアップロード
// 0=NONE, 1=ALPHA, 2=BRIGHTNESS, 3=SATURATION, 4=CONTRAST
public function _setAGAL(type:uint=0):void
{
var agalVertex:String, vertexAssembler:AGALMiniAssembler, fragmentAssembler:AGALMiniAssembler, program3D:Program3D;
switch (type)
{
case 0:
agalVertex = _agalVertexCommon + _agalVertexNone;
break;
case 1:
agalVertex = _agalVertexCommon + _agalVertexAlpha;
break;
case 2:
agalVertex = _agalVertexCommon + _agalVertexBrightness;
break;
case 3:
agalVertex = _agalVertexCommon + _agalVertexSaturation;
break;
case 4:
agalVertex = _agalVertexCommon + _agalVertexContrast;
break;
default:
}
vertexAssembler = new AGALMiniAssembler();
vertexAssembler.assemble(Context3DProgramType.VERTEX, agalVertex);
fragmentAssembler = new AGALMiniAssembler();
fragmentAssembler.assemble(Context3DProgramType.FRAGMENT, _agalFragment);
program3D = _context3D.createProgram();
program3D.upload(vertexAssembler.agalcode, fragmentAssembler.agalcode);
_context3D.setProgram(program3D);
}
// ----------------------------------------------------------------------------------------------------
// テキスト
//
private function _initText():void
{
var _textFormat:TextFormat;
_textField = new TextField()
_textFormat = _textField.defaultTextFormat;
_textFormat.color = 0xffffff;
_textFormat.font = "_sans";
_textField.defaultTextFormat = _textFormat;
_textField.text = TEXT_LIST[_agalType];
_textField.selectable = false;
_textField.autoSize = TextFieldAutoSize.CENTER;
addChild(_textField);
_resizeText();
}
private function _changeText():void
{
_textField.text = TEXT_LIST[_agalType];
}
private function _resizeText():void
{
_textField.x = _stageWidthHarf - (_textField.width/2);
_textField.y = _stage3DSize*0.85;
}
// ----------------------------------------------------------------------------------------------------
// カラーサンプル
//
private function _initColor():void
{
_bmpColor = new Bitmap()
_bmpColor.bitmapData = new BitmapData(COLOR_SIZE, COLOR_SIZE, false, 0);
addChild(_bmpColor);
_resizeColor();
}
private function _changeColor(color:uint):void
{
_bmpColor.bitmapData.fillRect(COLOR_RECT, color);
}
private function _resizeColor():void
{
_bmpColor.x = _stageWidthHarf - COLOR_SIZE/2;
_bmpColor.y = _stage3DSize*0.9;
}
// ----------------------------------------------------------------------------------------------------
// カラーユーティリティ
//
private static function _uintRgb(r:Number, g:Number, b:Number):uint
{
return ((r * 0xFF + 0.5) << 16) + ((g * 0xFF + 0.5) << 8) + ((b * 0xFF + 0.5) << 0);
}
// HSBからRGBへの変換
private static function _hsbToRGB(hue:Number, saturation:Number, brightness:Number):Vector.<Number>
{
var r:Number, g:Number, b:Number, h:Number, f:Number, p:Number, q:Number, t:Number;
r = g = b = 0;
if (saturation == 0) {
r = g = b = brightness;
} else {
h = (hue - int(hue)) * 6;
f = h - int(h);
p = brightness * (1 - saturation);
q = brightness * (1 - saturation * f);
t = brightness * (1 - (saturation * (1 - f)));
switch (int(h)) {
case 0:
r = brightness;
g = t;
b = p;
break;
case 1:
r = q;
g = brightness;
b = p;
break;
case 2:
r = p;
g = brightness;
b = t;
break;
case 3:
r = p;
g = q;
b = brightness;
break;
case 4:
r = t;
g = p;
b = brightness;
break;
case 5:
r = brightness;
g = p;
b = q;
break;
}
}
return Vector.<Number>([r, g, b]);
}
}
}