• Benvenuto in Making Videogames!
  • Dai sfogo alla tua fantasia!
  • Crea il tuo Videogioco!
Benvenuto ospite! Login Registrati




Valutazione discussione:
  • 1 voto(i) - 5 media
  • 1
  • 2
  • 3
  • 4
  • 5
Tutorial 8 - SpecialFX
#1
Tutorial 8: SpecialFX

[Immagine: 008shot.jpg]

Questo tutorial descrive come creare effetti speciali. Vedremo l'uso delle ombre tramite lo stencil buffer, i sistemi particellari, le billboards, le luci dinamiche e il nodo di scena per creare le superfici d'acqua.
Cominciamo come già fatto in altri tutorials. Notiamo che stavolta imposteremo a true il flag 'shadows' nella chiamata a createDevice() questo perché vorremo che il modello stavolta proietti un'ombra dinamica. Se l'esempio dovesse girare troppo lentamente impostate a false il flag (con le schede video odierni non dovrebbero esserci problemi). L'Irrlicht Engine comunque controlla sempre che il vostro hardware supporti lo stencil buffer e disabiliterà le ombre dinamiche automaticamente.
Codice PHP:
#include <irrlicht.h>
#include <iostream>
#include "driverChoice.h"

using namespace irr;

#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif

int main()
{
    
// ask if user would like shadows
    
char i;
    
printf("Please press 'y' if you want to use realtime shadows.\n");

    
std::cin >> i;

    const 
bool shadows = (== 'y');

    
// ask user for driver
    
video::E_DRIVER_TYPE driverType=driverChoiceConsole();
    if (
driverType==video::EDT_COUNT)
        return 
1
Creiamo il nostro device e usciamo in caso di fallimento. Rendiamo la scelta dello stencil shadow opzionale in modo da girare comunque l'esempio con hardware più datati senza ombre.
Codice PHP:
IrrlichtDevice *device =
        
createDevice(driverTypecore::dimension2d<u32>(640480),
        
16falseshadows);

    if (
device == 0)
        return 
1// could not create selected driver.

    
video::IVideoDriverdriver device->getVideoDriver();
    
scene::ISceneManagersmgr device->getSceneManager(); 
Per il nostro ambiente carichiamo un file .3ds. E' una piccola stanza modellata con Anim8or ed esportata nel formato 3ds perché il formato .an8 non è riconosciuto dal Irrlicht Engine. Sono un pessimo grafico 3d e il texture mapping non è granché. Fortunatamente sono migliore come programmatore che come artista e Irrlicht Engine può creare dei texture mapping molto belli per noi: utilizziamo un mesh manipulator e creiamo un mapping texture planare per la mesh della stanza. Se volete vedere il mio mapping fatto con Anim8or, decommentate la linea e lo vedrete. Non so nemmeno come si impostano i materiali correttamente con Anim8or, c'è una luce speculare che proprio non mi piace. La toglieremo via codice.
Codice PHP:
scene::IAnimatedMeshmesh smgr->getMesh("../../media/room.3ds");

    
smgr->getMeshManipulator()->makePlanarTextureMapping(mesh->getMesh(0), 0.004f);

    
scene::ISceneNodenode 0;

    
node smgr->addAnimatedMeshSceneNode(mesh);
    
node->setMaterialTexture(0driver->getTexture("../../media/wall.jpg"));
    
node->getMaterial(0).SpecularColor.set(0,0,0,0); 
Ora il primo effetto speciale: l'animazione dell'acqua. Funziona così: Il WaterSurfaceSceneNode prende come input una mesh e la fa oscillare come la superficie dell'acqua. Se poi le applichiamo un bel materiale come EMT_REFLECTION_2_LAYER, viene fuori un bel effetto. Questo lo facciamo con le righe di codice che seguono. Come mesh di input creiamo una mesh piana, senza alcuna incurvatura all'interno. Potevamo usare qualsiasi altra mesh, volendo potremmo usare anche la stessa stanza room.3ds (il che darebbe a tutta la scena un aspetto veramente strano).
Codice PHP:
mesh smgr->addHillPlaneMesh"myHill",
        
core::dimension2d<f32>(20,20),
        
core::dimension2d<u32>(40,40), 00,
        
core::dimension2d<f32>(0,0),
        
core::dimension2d<f32>(10,10));

    
node smgr->addWaterSurfaceSceneNode(mesh->getMesh(0), 3.0f300.0f30.0f);
    
node->setPosition(core::vector3df(0,7,0));

    
node->setMaterialTexture(0driver->getTexture("../../media/stones.jpg"));
    
node->setMaterialTexture(1driver->getTexture("../../media/water.jpg"));

    
node->setMaterialType(video::EMT_REFLECTION_2_LAYER); 
Il secondo effetto speciale è molto semplice, scommetto che l'avete già visto in qualche demo del Irrlicht Engine: una billboard (una mesh quadrata, texturizzata e che punta sempre la camera) trasparente combinata con una luce dinamica. Semplicemente creiamo un nodo di scena di tipo luce, lo facciamo volare in tondo e per farlo sembrare più bello gli attacchiamo un nodo di tipo billboard.
Codice PHP:
// create light

    
node smgr->addLightSceneNode(0core::vector3df(0,0,0),
        
video::SColorf(1.0f0.6f0.7f1.0f), 800.0f);
    
scene::ISceneNodeAnimatoranim 0;
    
anim smgr->createFlyCircleAnimator (core::vector3df(0,150,0),250.0f);
    
node->addAnimator(anim);
    
anim->drop();

    
// attach billboard to light

    
node smgr->addBillboardSceneNode(nodecore::dimension2d<f32>(5050));
    
node->setMaterialFlag(video::EMF_LIGHTINGfalse);
    
node->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
    
node->setMaterialTexture(0driver->getTexture("../../media/particlewhite.bmp")); 
Il prossimo effetto speciale è più interessante: un sistema particellare. Il sistema particellare in Irrlicht è abbastanza modulare ed estensibile ma anche molto facile da usare. Esiste un nodo di tipo sistema particellare nel quale si può piazzare un emettitore di particelle che farà partire particelle oppure no. Questi emettitori sono piuttosto flessibili e tipicamente hanno un sacco di parametri come direzione, intensità e colore della particella creata.
Ci sono diversi tipi di emettitori, per esempio c'è l'emettitore puntiforme che emette le particelle da un solo punto fisso. Se i tipi di emettitori nell'engine non fossero comunque abbastanza per voi, potete sempre crearne di vostri semplicemente derivando una vostra classe dalla classe interfaccia IParticleEmitter agganciandola poi al sistema particellare tramite setEmitter(). In questo esempio creeremo un emettitore a cubo (box), dove le particelle nasceranno casualmente da dentro la superficie del box. I parametri definiscono il box, la direzione delle particelle, il minimo e il massimo numero di particelle generate al secondo, il colore, il minimo e il massimo tempo di durata della particella.
Poiché con solo gli emettitori il sistema particellare sarebbe un po' noioso, possiamo utilizzare i modificatori che variano i parametri delle particelle come il loro movimento durante il loro volo. Questi modificatori possono essere aggiunti per simulare la gravità oppure il vento. Il modificatore di particelle che utilizzeremo, ad esempio, va a modificare il colore delle particelle: le farà scomparire lentamente con un “fade out”. Come per gli emettitori anche i modificatori possono essere creati e personalizzati da voi, basta semplicemente derivare la classe dall'interfaccia IParticleAffector ed aggiungerli con addAffector().
Dopo aver applicato un materiale adatto al sistema particellare avremo un bel fuoco da campo nella scena. Aggiustando il materiale, la texture o intervenendo sui parametri degli emettitori e dei modificatori si possono creare facilmente diversi effetti come fumo, pioggia, esplosioni, neve e così via.
Codice PHP:
// create a particle system

    
scene::IParticleSystemSceneNodeps =
        
smgr->addParticleSystemSceneNode(false);

    
scene::IParticleEmitterem ps->createBoxEmitter(
        
core::aabbox3d<f32>(-7,0,-7,7,1,7), // emitter size
        
core::vector3df(0.0f,0.06f,0.0f),   // initial direction
        
80,100,                             // emit rate
        
video::SColor(0,255,255,255),       // darkest color
        
video::SColor(0,255,255,255),       // brightest color
        
800,2000,0,                         // min and max age, angle
        
core::dimension2df(10.f,10.f),         // min size
        
core::dimension2df(20.f,20.f));        // max size

    
ps->setEmitter(em); // this grabs the emitter
    
em->drop(); // so we can drop it here without deleting it

    
scene::IParticleAffectorpaf ps->createFadeOutParticleAffector();

    
ps->addAffector(paf); // same goes for the affector
    
paf->drop();

    
ps->setPosition(core::vector3df(-70,60,40));
    
ps->setScale(core::vector3df(2,2,2));
    
ps->setMaterialFlag(video::EMF_LIGHTINGfalse);
    
ps->setMaterialFlag(video::EMF_ZWRITE_ENABLEfalse);
    
ps->setMaterialTexture(0driver->getTexture("../../media/fire.bmp"));
    
ps->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR); 
Ora aggiungiamo un nodo per la luce volumetrica, che aggiunge una finta area luminescente nella scena. Come per le billboard e i sistemi particellari assegnamo una texture per l'effetto desiderato, stavolta utilizzeremo un texture animator per ricreare l'illusione di una area magicamente luminescente.
Codice PHP:
scene::IVolumeLightSceneNode smgr->addVolumeLightSceneNode(0, -1,
                
32,                              // Subdivisions on U axis
                
32,                              // Subdivisions on V axis
                
video::SColor(0255255255), // foot color
                
video::SColor(0000));      // tail color

    
if (n)
    {
        
n->setScale(core::vector3df(56.0f56.0f56.0f));
        
n->setPosition(core::vector3df(-120,50,40));

        
// load textures for animation
        
core::array<video::ITexture*> textures;
        for (
s32 g=70; --g)
        {
            
core::stringc tmp;
            
tmp "../../media/portal";
            
tmp += g;
            
tmp += ".bmp";
            
video::ITexturedriver->getTexturetmp.c_str() );
            
textures.push_back(t);
        }

        
// create texture animator
        
scene::ISceneNodeAnimatorglow smgr->createTextureAnimator(textures150);

        
// add the animator
        
n->addAnimator(glow);

        
// drop the animator because it was created with a create() function
        
glow->drop();
    } 
Come ultimo effetto speciale vogliamo un'ombra dinamica proiettata dal personaggio animato. Per farlo andiamo a caricare un modello animato da un file DirectX .x e lo piazziamo nella stanza. Per creare l'ombra semplicemente chiamiamo addShadowVolumeSceneNode(). Il colore delle ombre può essere impostato solo a livello globale e varrà per tutte le ombre presenti, chiamando il metodo ISceneManager:ConfusedetShadowColor(). Voila, ecco la nostra ombra dinamica.
Siccome il modello è un po' troppo piccolo per la scena lo andiamo a scalare ingrandendolo con setScale(). Poiché è anche illuminato da una luce dinamica dobbiamo normalizzare le sue normali per rendere l'illuminazione corretta. Questo si rende sempre necessario ogni qual volta la scala di un modello illuminato dinamicamente non è più (1,1,1). Altrimenti sembrerà troppo scuro o troppo chiaro a causa della scalatura del modello e di conseguenza delle normali che servono per ll'illuminazione.
Codice PHP:
// add animated character

    
mesh smgr->getMesh("../../media/dwarf.x");
    
scene::IAnimatedMeshSceneNodeanode 0;

    
anode smgr->addAnimatedMeshSceneNode(mesh);
    
anode->setPosition(core::vector3df(-50,20,-60));
    
anode->setAnimationSpeed(15);

    
// add shadow
    
anode->addShadowVolumeSceneNode();
    
smgr->setShadowColor(video::SColor(150,0,0,0));

    
// make the model a little bit bigger and normalize its normals
    // because of the scaling, for correct lighting
    
anode->setScale(core::vector3df(2,2,2));
    
anode->setMaterialFlag(video::EMF_NORMALIZE_NORMALStrue); 

Finalmente possiamo andare a disegnare ogni cosa e questo è tutto.
Codice PHP:
scene::ICameraSceneNodecamera smgr->addCameraSceneNodeFPS();
    
camera->setPosition(core::vector3df(-50,50,-150));
    
camera->setFarValue(10000.0f); // this increase a shadow visible range.

    // disable mouse cursor
    
device->getCursorControl()->setVisible(false);

    
s32 lastFPS = -1;

    while(
device->run())
    if (
device->isWindowActive())
    {
        
driver->beginScene(truetrue0);

        
smgr->drawAll();
        
driver->endScene();
        const 
s32 fps driver->getFPS();

        if (
lastFPS != fps)
        {
            
core::stringw str L"Irrlicht Engine - SpecialFX example [";
            
str += driver->getName();
            
str += "] FPS:";
            
str += fps;

            
device->setWindowCaption(str.c_str());
            
lastFPS fps;
        }
    }

    
device->drop();
    return 
0;


Versione in pdf scaricabile da QUI
 
Rispondi
#2
Approfondiamo.. lo Stencil Buffer.

Nella fase di rendering le librerie grafiche fanno uso di molte strutture ausiliarie tra cui i buffer. Si tratta di zone di memoria NxM dove si vanno a memorizzare determinate informazioni che serviranno per ottimizzare il disegno (per esempio non disegnando ciò che è stato già coperto visivamente da altri oggetti ecc..) oppure per creare certi effetti (sovrapposizioni di più colori, trasparenze, ecc..).

I frame buffer (o render buffer) sono alcuni di questi.
. Color Buffer: che tiene per ogni punto il colore dell'immagine da renderizzare.
. Depth/Z Buffer: tiene la profondità Z per sapere cosa sta prima e dopo rispetto alla camera (in Unity quando si sviluppa in 2d con il 3d è facile imbattercisi);
. Stencil Buffer: serve a memorizzare la visibilità degli oggetti e in genere a fare da maschera/filtro sui punti poco prima che vengano disegnati.
. Accumulation Buffer : serve per ottenere effetti dati proprio da sovrapporsi delle immagini renderizzate
. Auxiliary Buffer : serve come ausilio per lavorare con i colori dei singoli punti

Lo Stencil Buffer si usa come una specie di filtro passa/non_passa, per capire il concetto, durante il rendering 3d-2d si memorizza l'ipotetica posizione dove il punto proiettato verrà disegnato ma possiamo "spengerlo" impostando a "0" quella coordinata, oppure "1" se vogliamo che passi.
[Immagine: c5_stencil.png]

Nell'esempio si ottiene quasi una finestra da cui "sembra" di vedere attraverso, eventuali valori intermedi tra 0 e 1 potrebbero servire per fare trasparenza, tipicamenteo lo stencil buffer ha una profondità di 8bit, dove ogni punto assume valori tra 0..255.

Ora per decidere come il punto 3d passa o non passa una volta diventato 2d possiamo usare vari algoritmi.. ad esempio proiettare una silhouette partendo da una sorgente puntiforme (un sole) attraverso un oggetto (il caster) su una superficie ricevente (un terreno):
immagine1
Intersecando la proiezione con la superficie che riceve, ci resta l'ombra proiettata:
immagine2

Le immagini presenti in questo link spiegano più in dettaglio l'algoritmo mentre assegna i valori ed effettua le operazioni tipiche sui singoli punti (somma, sottrai, confronto, mantieni, inverti da fondo, ecc..).

http://docs.unity3d.com/Manual/SL-Stencil.html (notare in Unity la similitudine con le operazioni GL)
https://en.wikibooks.org/wiki/OpenGL_Pro...cil_buffer
https://open.gl/depthstencils
https://www.opengl.org/wiki/Stencil_Test
http://www.gamasutra.com/view/feature/13...uffers.php
https://msdn.microsoft.com/en-us/library...s.85).aspx
 
Rispondi
#3
Grazie 1000 Big Grin
 
Rispondi
#4
E' comunque solo per cultura generale. Mettersi a smanettare con lo stencil buffer magari programmando uno shader è tutt'altra cosa.. Sad ma almeno uno sa di cosa parla Tongue

Comunque, finite le traduzioni, vedrò di fare un esempio più "pratico" e completo che metta insieme un pò di queste cose su Irrlicht.
 
Rispondi
  


Vai al forum:


Browsing: 2 Ospite(i)