• 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 26 - OcclusionQuery
#1
Tutorial 26: OcclusionQuery

[Immagine: 026shot.jpg]

Questo tutorial mostra come velocizzare il rendering tramite l'uso della OcclusionQuery (OQ) (*). Tipicamente il rendering tenta di tagliare quei nodi della scena che non sono visibili perché fuori dal view frustum. Tuttavia questo metodo non considera quegli oggetti che, pur essendo nella linea visiva, vengono coperti visivamente da altri oggetti davanti e più grandi che stanno tra loro e la camera. Una OQ fa proprio questo. Questa ricerca sostanzialmente misura quanti pixel il rendering precedente ha lasciato sullo schermo. Dal momento che non possiamo riconoscere i pixel alla fine di ogni rendering, il conteggio viene fatto durante il rendering stesso. Per questo è necessario renderizzare l'occluder (l'oggetto che sta davanti) prima. L'oggetto necessita di scrivere nello z-buffer per diventare un vero occlusore. Quindi il nodo viene renderizzato e nel caso ci sia un passaggio sulla z, per es. il pixel viene scritto nel framebuffer, il pixel è conteggiato nella query. Alla fine il risultato nella query è il numero di pixel che sono stati attraversati. Ora possiamo giudicare, in base al numero di pixel se il nodo è visibile e quindi è da renderizzare o se va rimosso dal rendering. Notare che il numero dei pixel è una certezza rispetto rispetto ad un sistema generico e approssimativo. I pixels possono essere sovrascritti più tardi possibili, in modo che la GPU possa cercare di evitare inesattezze e falsi negativi nella query.
Avrete notato che dobbiamo renderizzare il nodo per ottenere il numero di pixel. Quindi dove è il beneficio? direte voi. Ci sono molti sistemi in cui una OQ può esserci di aiuto. Una buona idea sarebbe renderizzare la bbox (box ricoprente o rettangolo di selezione) nel nodo invece che tutta la mesh. E' molto veloce e abbastanza sicuro come approssimazione. Se servisse un rendering più esatto un'ulteriore idea potrebbe essere quella di usare solo i materiali di base per quegli oggetti, evitando così gli shaders completi e i cambi di stato dovuti alle textures. Quanto meno se non stiamo facendo il rendering per la scena attuale questi shaders non servono. C'è un terzo modo per ottimizzare la OQ. Controllare la query solo a certi frame, il 5o o il 10o frame o anche meno frequentemente. Questo ovviamente dipenderà dalla velocità della camera e degli oggetti nella scena.
Codice PHP:
#ifdef _MSC_VER
// We'll also define this to stop MSVC complaining about sprintf().
#define _CRT_SECURE_NO_WARNINGS
#pragma comment(lib, "Irrlicht.lib")
#endif

#include <irrlicht.h>
#include "driverChoice.h"

using namespace irr;
Serve l'input da tastiera per cambiare i parametri.
class MyEventReceiver : public IEventReceiver
{
public:
    // This is the one method that we have to implement
    virtual bool OnEvent(const SEvent& event)
    {
        // Remember whether each key is down or up
        if (event.EventType == irr::EET_KEY_INPUT_EVENT)
            KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;

        return false;
    }

    // This is used to check whether a key is being held down
    virtual bool IsKeyDown(EKEY_CODE keyCode) const
    {
        return KeyIsDown[keyCode];
    }
    
    MyEventReceiver()
    {
        for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
            KeyIsDown[i] = false;
    }

private:
    // We use this array to store the current state of each key
    bool KeyIsDown[KEY_KEY_CODES_COUNT];
}; 
Creiamo un irr::IrrlichtDevice e i nodi di scena. Uno sarà l'occludende, uno sarà quello occluso. Quest'ultimo sarà una sfera che ha molti triangoli.
Codice PHP:
int main()
{
    
// ask user for driver
    
video::E_DRIVER_TYPE driverType=driverChoiceConsole();
    if (
driverType==video::EDT_COUNT)
        return 
1;

    
// create device
    
MyEventReceiver receiver;

    
IrrlichtDevicedevice createDevice(driverType,
            
core::dimension2d<u32>(640480), 16falsefalsefalse, &receiver);

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

    
video::IVideoDriverdriver device->getVideoDriver();
    
scene::ISceneManagersmgr device->getSceneManager();

    
smgr->getGUIEnvironment()->addStaticText(L"Press Space to hide occluder."core::recti(10,10200,50)); 
Creo il nodo che deve essere occluso. Creiamo una sfera con un conteggio alto di poligoni.
Codice PHP:
scene::ISceneNode node smgr->addSphereSceneNode(1064);
    if (
node)
    {
        
node->setPosition(core::vector3df(0,0,60));
        
node->setMaterialTexture(0driver->getTexture("../../media/wall.bmp"));
        
node->setMaterialFlag(video::EMF_LIGHTINGfalse);
    } 
Ora creiamo l'altro nodo, l'occludente. Un semplice piano.
Codice PHP:
scene::ISceneNodeplane smgr->addMeshSceneNode(smgr->addHillPlaneMesh(
        
"plane"core::dimension2df(10,10), core::dimension2du(2,2)), 0, -1,
        
core::vector3df(0,0,20), core::vector3df(270,0,0));

    if (
plane)
    {
        
plane->setMaterialTexture(0driver->getTexture("../../media/t351sml.jpg"));
        
plane->setMaterialFlag(video::EMF_LIGHTINGfalse);
        
plane->setMaterialFlag(video::EMF_BACK_FACE_CULLINGtrue);
    } 
Qui creiamo la query di occlusione (OQ). Siccome non usiamo un nodo con una mesh piena (ESNT_MESH o ESNT_ANIMATED_MESH), passiamo la sua geometria di base. Altrimenti potremmo anche passare una mesh più semplice o il rettangolo di selezione (bbox). Utilizzeremo comunque un metodo basato sul tempo, dove la OQ renderizza sul frame buffer e in caso di successo (occlusione), la mesh non viene disegnata per un certo numero di frames.
Codice PHP:
driver->addOcclusionQuery(node, ((scene::IMeshSceneNode*)node)->getMesh()); 
Abbiamo fatto tutto, ora serve giusto la camera e disegniamo tutto. Scriviamo anche il numero di frame al secondo e il nome del driver nel titolo della finestra per verificare la velocità del rendering. Ci salviamo anche il tempo intercorso tra l'ultima OQ e l'informazione che ci dice se il nodo deve essere visibile nei prossimi frames.
Codice PHP:
smgr->addCameraSceneNode();
    
int lastFPS = -1;
    
u32 timeNow device->getTimer()->getTime();
    
bool nodeVisible=true;

    while(
device->run())
    {
        
plane->setVisible(!receiver.IsKeyDown(irr::KEY_SPACE));

        
driver->beginScene(truetruevideo::SColor(255,113,113,133)); 
Primo disegniamo la scena, possibilmente senza l'oggetto occluso. E' necessario perché ci serve che l'occluder sia disegnato prima. Possiamo anche usare più gestori di scene per tenere traccia di più occluder renderizzando una scena separatamente.
Codice PHP:
node->setVisible(nodeVisible);
        
smgr->drawAll();
        
smgr->getGUIEnvironment()->drawAll(); 
Ogni tanto, ogni 100ms, controlliamo la visibilità. Lanciamo la query, aggiorniamo il valore dei pixel e interroghiamo il risultato. Dal momento che abbiamo renderizzato il nodo, rendiamo la query invisibile. Volendo il risultato immediatamente faremo l'aggiornamento in modo bloccante. Se non ci serve il risultato immediatamente, per es. perché abbiamo altre cose da renderizzare, possiamo chiamare l'aggiornamento ma senza bloccare tutto. Questo darà alla GPU più tempo per passare i risultati senza ingolfare la pipeline di rendering. Se l'aggiornamento è chiamato senza bloccare, il risultato dalla getOcclusionQueryResult sarà o il valore precedente o il valore 0xffffffff se nessun valore è stato generato. Il risultato viene preso e passato immediatamente come flag di visibilità per il nodo.
Codice PHP:
if (device->getTimer()->getTime()-timeNow>100)
        {
            
driver->runAllOcclusionQueries(false);
            
driver->updateAllOcclusionQueries();
            
nodeVisible=driver->getOcclusionQueryResult(node)>0;
            
timeNow=device->getTimer()->getTime();
        }

        
driver->endScene();

        
int fps driver->getFPS();

        if (
lastFPS != fps)
        {
            
core::stringw tmp(L"OcclusionQuery Example [");
            
tmp += driver->getName();
            
tmp += L"] fps: ";
            
tmp += fps;

            
device->setWindowCaption(tmp.c_str());
            
lastFPS fps;
        }
    } 
Alla fine cancelliamo il device Irrlicht.
Codice PHP:
device->drop();
    
    return 
0;

E' tutto. Compiliamo e proviamo il programma.


(*) si veda GPU Gems, cap.29 http://http.developer.nvidia.com/GPUGems..._ch29.html
GPU Gems2, cap.6 http://http.developer.nvidia.com/GPUGems...ter06.html
OGL Query Object https://www.opengl.org/wiki/Query_Object


Versione pdf scaricabile da QUI
 
Rispondi
  


Vai al forum:


Browsing: 1 Ospite(i)