[Flash] Starlingの学習 DynamicLighting
環境:
- Starling 2.1
- Starling Extension - Dynamic Lighting 2.0
- Adobe AIR SDK 22
- FlashDevelop 5.2
- Windows 7 64bit
Dynamic Lightingについて学ぶぞ。
http://wiki.starling-framework.org/extensions/dynamic_lighting で例示されてる機能だ。
そこで気になってるのが、Normap Mapなんだよ。色がついているだろ。
この色ってなんの意味があるの?
そのページで紹介されてるツールSpriteIlluminatorのページ見ると、色はAngle(角度)を表す、と書いてある。2Dグラフでいう接線の傾き、みたいなものかな。
ふーん。
でも、こういうのはHeight(高さ)情報だけあれば、ある程度は表現できるはずなんだよな。Height情報は色のないgray scaleで表せるので、色をつけるこのツール使わなくてもいけるのだが‥‥。
このSpriteIlluminatorというツールを使いたくないの?
このツール、WindowsXPで動作しないことを確認した。正直XPで動作しないツールは買いたくない‥‥。と言っても、ただの画像なので画像編集ソフトでも作れる。https://www.google.co.jp/search?q=normal+mapで検索すると Photoshopで作ってたりOnlineで作れたりするようだぞ。
というか それ以前に、キャラクターのアニメーション動作パターン1つ1つに色つきNormal Map作る作業は大変だ。 色つけず、gray scaleでいいのなら、まだやりようがある。
というわけで、色を外したNormal Mapでどう表示されるか確認した。
Colorless Normal Map = Height Map ?
まず http://wiki.starling-framework.org/extensions/dynamic_lighting のdownloadで、Githubに提供されてるサンプルを取得する。
PhotoshopCS6でNormalMapを [Image - Mode - GrayScale] に変換する。これでHeight map(ハイトマップ)として使えるデータになった。
- 色つきのNormal Map(法線マップ)は高さ(Height)と角度(法線ベクトル、Angle)の情報を持つ。
- 色なしのHeight Map(ハイトマップ)は高さ(Height)情報のみ持つ。
これを同じ条件でDynamic Lightingしてみよう。
compare Normal Map with Height Map
Main.as は、これまでの違いは横幅を650pxにしている箇所だけ。このアニメーションキャラが横幅325pxだったので、重ねずに2つ並べると650pxになる。
package
{
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import starling.core.Starling;
/**
* ...
* @author foo
*/
[SWF(width="650",height="480",frameRate="60",backgroundColor="#002143")]
public class Main extends Sprite
{
private var stln:Starling;
public static var frameRate:uint = 0;
public function Main()
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
stage.stage3Ds[0].addEventListener(Event.CONTEXT3D_CREATE, onContext3dCreated);
stln = new Starling(Game, stage);
stln.showStats = true;
stln.antiAliasing = 0;
stln.start();
}
private function onContext3dCreated(e:Event):void
{
stage.stage3Ds[0].removeEventListener(Event.CONTEXT3D_CREATE, onContext3dCreated);
if (Starling.context.driverInfo.toLowerCase().indexOf("software") != -1) {
// GPU disable
Starling.current.nativeStage.frameRate = 30;
} else {
// GPU enable
}
frameRate = Starling.current.nativeStage.frameRate;
}
}
}
Game.as は、Githubに提供されてるサンプルを参考にした。
package
{
import flash.display.Bitmap;
import starling.animation.Juggler;
import starling.core.Starling;
import starling.display.MovieClip;
import starling.display.Quad;
import starling.display.Sprite;
import starling.events.Event;
import starling.events.Touch;
import starling.events.TouchEvent;
import starling.events.TouchPhase;
import starling.text.TextField;
import starling.textures.Texture;
import starling.textures.TextureAtlas;
import starling.extensions.lighting.LightSource;
import starling.extensions.lighting.LightStyle;
/**
* ...
* @author foo
*/
public class Game extends Sprite
{
[Embed(source="../assets/character.png")]
private static const CharacterTexture:Class;
[Embed(source="../assets/character_n.png")]
private static const CharacterNormalTexture:Class;
[Embed(source="../assets/character_h.png")]
private static const CharacterHeightTexture:Class;
[Embed(source="../assets/character.xml", mimeType="application/octet-stream")]
private static const CharacterXml:Class;
private var stgWidth:int;
private var stgHeight:int;
public function Game()
{
if (stage) init()
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
addEventListener(Event.ENTER_FRAME, onGetGpuStyle);
}
private function onGetGpuStyle(e:Event):void
{
if (Main.frameRate <= 0) return;
removeEventListener(Event.ENTER_FRAME, onGetGpuStyle);
addItem();
}
private function addItem():void
{
stgWidth = stage.stageWidth;
stgHeight = stage.stageHeight;
addCharcters();
addLights();
}
private function addLights():void
{
var ambl:LightSource = LightSource.createAmbientLight();
ambl.x = stgWidth * 0.5;
ambl.y = stgHeight * 0.5;
ambl.z = -50;
//ambl.showLightBulb = true;
var pla:LightSource = LightSource.createPointLight(0xffff00);
pla.x = stgWidth * 0.2;
pla.y = stgHeight * 0.3;
pla.z = -50;
pla.showLightBulb = true;
var plb:LightSource = LightSource.createPointLight(0xffff00);
plb.x = stgWidth * 0.8;
plb.y = stgHeight * 0.3;
plb.z = -50;
plb.showLightBulb = true;
var dirl:LightSource = LightSource.createDirectionalLight();
dirl.x = stgWidth * 0.5;
dirl.y = stgHeight * 0.3;
dirl.z = -150;
dirl.rotationY = -1.0;
dirl.showLightBulb = true;
addChild(ambl);
addChild(pla);
addChild(plb);
//addChild(dirl);
}
private function addCharcters():void
{
var chrTx:Texture = Texture.fromEmbeddedAsset(CharacterTexture);
var chrNormalTx:Texture = Texture.fromEmbeddedAsset(CharacterNormalTexture);
var chrHeightTx:Texture = Texture.fromEmbeddedAsset(CharacterHeightTexture);
var chrXML:XML = XML(new CharacterXml());
var txAt:TextureAtlas = new TextureAtlas(chrTx, chrXML);
var normalTxAt:TextureAtlas = new TextureAtlas(chrNormalTx, chrXML);
var heightTxAt:TextureAtlas = new TextureAtlas(chrHeightTx, chrXML);
var tx:Vector.<Texture> = txAt.getTextures();
var ntx:Vector.<Texture> = normalTxAt.getTextures();
var htx:Vector.<Texture> = heightTxAt.getTextures();
var mc1:MovieClip = addCharacter(tx, ntx);
mc1.scaleX = -1;
mc1.x = mc1.width + 100;
mc1.y = 150;
//var qd:Quad = new Quad(mc1.width, mc1.height, 0xcccccc);
//qd.x = mc1.x; qd.y = mc1.y;
//addChild(qd);
addChild(mc1);
Starling.juggler.add(mc1);
var msg:TextField = new TextField(120, 24, 'NormalMap');
msg.format.color = 0xFFFFFF;
msg.x = stgWidth / 2 - 120;;
msg.y = mc1.y - 48;
addChild(msg);
var mc2:MovieClip = addCharacter(tx, htx);
mc2.x = mc2.width - 100;
mc2.y = 150;
addChild(mc2);
Starling.juggler.add(mc2);
msg = new TextField(120, 24, 'HeightMap');
msg.format.color = 0xFFFFFF;
msg.x = stgWidth / 2;
msg.y = mc2.y - 48;
addChild(msg);
}
private function addCharacter(tx:Vector.<Texture>, ntx:Vector.<Texture>):MovieClip
{
var mc:MovieClip = new MovieClip(tx, tx.length);
var styl:LightStyle = new LightStyle(ntx[0]);
styl.ambientRatio = 0.2;
styl.diffuseRatio = 0.8;
styl.specularRatio = 0.2;
styl.shininess = 16;
mc.style = styl;
for (var i:int = 0; i < mc.numFrames; i++) {
mc.setFrameAction(i, updateStyle);
}
return mc;
function updateStyle(mc:MovieClip, frameId:int):void
{
styl.normalTexture = ntx[frameId];
}
}
}
}
実Flash。操作可。ポイントライトをマウスドラッグで移動できる。ダブルクリックで消灯・点灯可能。
違いがあるね。Normal Mapのほうがやはり見栄えがよいよね。
Normal Mapでいくべきだろうなー、これは。
この照明は光を強くするとかできるの?
光量を変えることはできないよ。その代わりPointLightのZ位置をShift+マウスドラッグで変更できるぞ。Z位置を変えることで光源を近く/遠くにすれば光量をコントロールできる。
なるほど。‥‥うまく位置変更できないなー。
Light側は、ソースコードレベルでもX/Y/Z位置と色の指定だけ可能で、光量は指定できない。Lightには3種類ある。
- ambientLight (位置・角度に関係なくすべての物体を照らす。Global Illuminationでの物体同士の相互反射による間接光表現を簡易的に再現する)
- pointLight (位置が近いものだけ照らす。ろうそくの光のような照明)
- directionalLight (位置には関係なく、特定の角度から照らす。日光のような照明)
光が当たる物体側では以下の4要素を指定できる。
- ambient(位置や角度に関わらず、ambientLightを表面で反射した光)
- diffuse(角度によって表面の反射の仕方が異なる。散乱光)
- specular(表面に当たった光源の反射。鏡面反射)
- shininess(specularが大きくぼんやりしてるか、小さくくっきりしてるか)
0 件のコメント:
コメントを投稿