3D menu
7.4.2010· Autor: Ondřej Brichta·
Počet komentářů: 2
V dnešním článku si ukážeme základ pro 3d rotující menu s využitím Papervision enginu, zajímavostí pak bude použité stínování povrchu objektů, které zde využijeme.
3D menu
O flashových menu a nabídkách bychom mohli psát neustále dokola. Dnešní ukázka bude ale zajímavá tím, že v ní použijeme stínování bitmapové textury 3d objektů, které celé 3D prostředí dělá mnohem atraktivnějším.
Shadery
O stínování se starají tzv. Shadery, které zajišťují vykreslení efektu stínu, případně odrazu nebo deformace textury. Můžeme využívat „obyčejných" shaderů, které vytvoří jednobarevnou texturu, která je osvícená na základě definovaného objektu světla vzdálenosti od něj. Takový stínovaný materiál pak ve scéně může vypadat následovně:
var light:PointLight3D = new PointLight3D();
light.z=-200;
light.x=0;
light.y=0;
var flatShadeMater:FlatShadeMaterial = new FlatShadeMaterial(light);
flatShadeMater.doubleSided=true;
var item:Plane=new Plane(flatShadeMater,60,45,2);

Důležitý je objekt světla, třídy PointLight3D, protože podle jeho umístění se vypočítávají výsledné stíny/osvícení povrchů. Je třeba zdůraznit, že u řady shaderů funguje stínování poněkud odlišně a je potřeba hodně testovat, co nejlépe vyhovuje.
Ukázková scéna dnešní aplikace je válec, jehož povrch je tvořen čtverci. Výše uvedené nastavení pak může vytvořit následující podobu:
U každého shaderu lze pak volit barvu osvícení, stínu, množství efektu apod. Shader se pak použije při konstrukci objektu jako klasický materiál.
Podívejme se na lepší výsledky pomocí PhongShaderu:
var light:PointLight3D = new PointLight3D();
light.z=200;
light.x=0;
light.y=0;
var phongShadeMater:PhongMaterial = new PhongMaterial(light,0xff0000,0x000000,1);
phongShadeMater.doubleSided=true;
var item:Plane=new Plane(phongShadeMater,60,45,2);
Všimněte si, že jsme světlo umístili do opačné z-ové pozice:

Ovšem jednobarevné výplně nejsou jediné, které můžeme využívat. Co takové bitmapy? Jejich použití vyžaduje trochu více práce:
var light:PointLight3D = new PointLight3D();
light.z=200;
light.x=0;
light.y=0;
var phongShader:PhongShader=new PhongShader(light,0xffffff,0x000000,5);
var shadedMaterial:ShadedMaterial=new ShadedMaterial(bmpMater,phongShader);
shadedMaterial.doubleSided=true;
var item:Plane=new Plane(shadedMaterial,60,45,2);
Zde už pracujeme se zmiňovaným pojmem Shader nativně a to tak, že jej musíme zkombinovat pomocí objektu třídy ShadedMaterial společně s bitmapovým objektem textury. U bitmapové textury je třeba kvůli správné funkčnosti zajistit už „hotová" bitmapová data, nelze tedy používat poměrně jednoduchou třídu BitmapFileMaterial.

U všech nativních shaderů trochu postrádáme změnu osvitu podle z-ové souřadnice. To řeší třída DepthShader (originál zde: http://blog.zupko.info/?p=111), jejíž použití je analogické k výše uvedeným ukázkám:
var light:PointLight3D = new PointLight3D();
light.z=-200;
light.x=0;
light.y=0;
var depthShader:DepthShader = new DepthShader(light, 0xffffff, 0x000000, 300, 400);
var shadedMaterial:ShadedMaterial=new ShadedMaterial(bmpMater,depthShader);
shadedMaterial.doubleSided=true;
var item:Plane=new Plane(shadedMaterial,60,45,2);

Opět si všimněte změny polohy světla, která ve výsledku vede k podobnému osvícení, nicméně změna byla důležitá s ohledem na jinou třídy shaderu. Jak to vše zkombinovat s více různými texturami, o tom si povíme v některém z dalších článků. Pro dnešek si ukážeme celou testovací aplikaci, kde kromě samotné rotace „menu" máme aplikované zmíněné shadery:
import caurina.transitions.Tweener;
import org.papervision3d.objects.DisplayObject3D;
import org.papervision3d.events.InteractiveScene3DEvent;
import org.papervision3d.materials.utils.MaterialsList;
import org.papervision3d.objects.primitives.Cube;
import org.papervision3d.objects.primitives.Sphere;
import org.papervision3d.cameras.Camera3D;
import org.papervision3d.render.BasicRenderEngine;
import org.papervision3d.view.Viewport3D;
import org.papervision3d.scenes.Scene3D;
import org.papervision3d.objects.primitives.Plane;
import org.papervision3d.materials.BitmapMaterial;
import org.papervision3d.core.proto.MaterialObject3D;
import org.papervision3d.materials.shadematerials.*;
import org.papervision3d.materials.shaders.*;
import org.papervision3d.lights.PointLight3D;
//sestavení scény
var sceneP3D = new Scene3D();
var viewport:Viewport3D=new Viewport3D(600,600,false,true);
var renderer:BasicRenderEngine = new BasicRenderEngine();
var cam:Camera3D=new Camera3D(20);
this.addChild(viewport);
//hlavní 3d objekt
var model:DisplayObject3D = new DisplayObject3D();
sceneP3D.addChild(model);
//
var light:PointLight3D = new PointLight3D();
light.z=0;
light.x=0;
light.y=0;
//max počet objektů horizontálně
var h_num:uint=10;
//max počet objektů vertikálně
var v_num:uint=5;
//poloměr válce
var radius:Number=200;
for (var iy:uint = 0; iy<v_num; iy++) {
for (var ix:uint = 0; ix<h_num; ix++) {
//
var phongShader:PhongShader=new PhongShader(light,0xffffff,0x000000,5);
//var depthShader:DepthShader = new DepthShader(light, 0xffffff, 0x000000, 300, 400);
//var phongShadeMater:PhongMaterial = new PhongMaterial(light,0xff0000,0x000000,1);
//phongShadeMater.doubleSided=true;
var bmpData:Img = new Img(0,0) ;
var bmpMater:BitmapMaterial =new BitmapMaterial(bmpData);
bmpMater.smooth=true;
var shadedMaterial:ShadedMaterial=new ShadedMaterial(bmpMater,phongShader);
shadedMaterial.doubleSided=true;
shadedMaterial.interactive=true;
var item:Plane=new Plane(shadedMaterial,60,45,2);
item.name="p"+iy+ix;
model.addChild(item);
item.addEventListener(InteractiveScene3DEvent.OBJECT_DOUBLE_CLICK,objClick);
}
}
setFlat(false);
var isFlat:Boolean;
function setFlat(_flat:Boolean):void {
//nastavení stylu válec-placka
isFlat=_flat;
if (_flat) {
Tweener.addTween(model,{rotationY:0,time:1,transition:"easeInCubic"});
}
for (var iy:uint = 0; iy<v_num; iy++) {
var y_num:Number=-150+iy*70;
for (var ix:uint = 0; ix<h_num; ix++) {
var item:DisplayObject3D=model.getChildByName("p"+iy+ix);
var alph:Number=ix*360/h_num;
var rad:Number=alph*Math.PI/180;
//
if (_flat) {
Tweener.addTween(item,{x:-150+iy*70,time:1,transition:"easeInCubic"});
Tweener.addTween(item,{y:-220+ix*48,time:1,transition:"easeInCubic"});
Tweener.addTween(item,{z:100,time:1,transition:"easeInCubic"});
Tweener.addTween(item,{rotationY:0,time:1,transition:"easeInCubic"});
} else {
Tweener.addTween(item,{x:Math.sin(rad)*radius,time:1,transition:"easeInCubic"});
Tweener.addTween(item,{y:y_num,time:1,transition:"easeInCubic"});
Tweener.addTween(item,{z:Math.cos(rad)*radius,time:1,transition:"easeInCubic"});
Tweener.addTween(item,{rotationY:alph,time:1,transition:"easeInCubic"});
}
}
}
}
function flat(evt:Event):void {
setFlat(true);
}
function round(evt:Event):void {
setFlat(false);
}
//doplnění listenerů pro tlačítka stylu vzhledu
flat_btn.addEventListener(MouseEvent.CLICK,flat);
round_btn.addEventListener(MouseEvent.CLICK,round);
//akce po kliknutí na objekt
function objClick(evt:InteractiveScene3DEvent):void {
var obj:DisplayObject3D=evt.displayObject3D;
var rand:Number=Math.round(Math.random()*180);
Tweener.addTween(obj,{scaleX:1.5,time:0.5,transition:"easeOutCubic"});
Tweener.addTween(obj,{scaleY:1.5,time:0.5,transition:"easeOutCubic"});
Tweener.addTween(obj,{scaleX:1,delay:1,time:1,transition:"easeOutCubic"});
Tweener.addTween(obj,{scaleY:1,delay:1,time:1,transition:"easeOutCubic"});
}
this.addEventListener(Event.ENTER_FRAME,renderScene);
function renderScene(evt:Event):void {
renderer.renderScene(sceneP3D,cam,viewport);
}
var clickX:Number=0;
var moving:Boolean=false;
function mouseD(evt:Event):void {
clickX=mouseX;
this.stage.addEventListener(MouseEvent.MOUSE_MOVE,mouseM);
}
function mouseM(evt:Event):void {
if (! isFlat) {
var dx:Number = (mouseX-clickX)/3;
Tweener.addTween(model,{rotationY:model.rotationY+dx,time:1,transition:"easeOutCubic"});
}
}
function mouseU(evt:Event):void {
this.stage.removeEventListener(MouseEvent.MOUSE_MOVE,mouseM);
}
this.stage.addEventListener(MouseEvent.MOUSE_DOWN,mouseD);
this.stage.addEventListener(MouseEvent.MOUSE_UP,mouseU);
V uvedené ukázce máme možnost tažením myši rotovat válec s objekty, reakce na kliknutí je simulována událostí dvojkliku na objekt, který se pak animovaně zvětší:
Zdrojový soubor ukázky je zde:papervision_menu.zip
Ondřej Brichta Vývojář multimediálních aplikací, šéfredaktor Flash.cz, školitel produktů Flash, Flex, Flash Media Server
Web:
http://www.obria.cz
|
Motto: <°))))><