100Fermer102
bearbecueLe 01/05/2012 à 01:23
Zeph (./90) :
Disclaimer: je n'ai pas commencé à tenter le light pre-pass rendering, j'ai un bug foireux à corriger avant ^^

En deferred shading classique donc, pour la passe où j'applique les lumières une à une, j'étais initialement parti pour utiliser des sphères de même rayon que celui des lumières à appliquer. Sauf que pour permettre à un pixel d'être affecté par plusieurs lumières, il faut désactiver le depth test pour que les sphères se superposent, mais ça provoque un effet indésirable : quand on est à l'extérieur de la sphère l'affichage de la face avant et de la face arrière fait que chaque pixel est dessiné deux fois, donc la lumière est appliquée deux fois également, donc ça foire, donc c'est nul.

Du coup, j'utilise à la place un disque que j'oriente toujours face à la caméra, comme ça je suis sûr que les pixels sont dessinés une seule fois par lumière. Sauf que du coup, si je* dépasse la lumière (i.e. si je me place de façon à être entre l'objet éclairé et la lumière), alors ce disque est dessiné derrière moi, donc n'apparaît pas du tout à l'écran, donc la lumière n'est pas appliquée, donc c'est tout noir, donc c'est nul aussi.

Il y a plusieurs façons de résoudre le problème, mais aucune ne me semble "bonne" :

- Je pourrais redessiner tout l'écran pour chaque lampe, comme ça au moins il n'y a aucun problème quelles que soient leurs positions tritop
- Je pourrais, quand une lampe est derrière moi* mais que ma distance à elle est quand même inférieure à son rayon, redessiner tout l'écran
- Je pourrais, quand une lampe est derrière moi* mais que ma distance à elle est quand même inférieure à son rayon, ou ramener mon disque juste devant moi* (même si ça risque de ne m'économiser qu'une poignée de pixels par rapport à la méthode précédente)
- Je pourrais réactiver le depth test et remettre le depth buffer à zéro avant l'affichage de chaque lumière
- Je pourrais utiliser le stencil buffer pour y coller l'index de la lumière en cours d'affichage, et éviter de dessiner deux fois un même pixel pour une même lampe

Mais bof quand même, il n'y a pas une solution plus... mieux ? grin

*je/moi = la caméra


bon, reponse rapide: tu peux utiliser le stencil-buffer.

- pour chaque lumiere
{
1- clear du stencil (valeur = 0) (degeulasse point de vue perfs, mais c'est l'implementation naive)
avec le depth-test ACTIF:
{
2- tu affiche les faces avant du volume de la lumiere, avec le stencil set en SET (valeur = 1)
3- tu affiche les faces arrieres du volume de la lumiere (t'inverse le culling quoi), avec le stencil set en DECREMENT (-1), ou en SET (valeur = 0)
}
avec le depth-test DESACTIVE, et le test de stencil a EQUAL (valeur = 1)
{
4- tu affiche un fullscreen quad (implementation naive aussi, mais probablement quand meme plus performante que la version qui affiche tout le volume) et tu calcule le lighting dedans.
}
}

en gros, le resultat, c'est que seul les pixels qui intersectent le _volume_ de ta light passeront dans ton shader de lighting et seront calcules. (les autres sont aussi "calcules" mais se font rejeter dans une etape precedente du pipeline, et le plus couteux la, c'est le pixel shader et les stages suivants (notamment le blending))

1- le stencil buffer contient la valeur 0 pour chaque pixel
2- pour toute la surface avant du volume de la light qui n'est pas derriere de la geometrie, le stencil passe a 1
3- c'est la que c'est tricky. ce sont les faces arrieres. 3 cas de figure:
3.1- si il y a de la geometrie entre la face avant et la face arriere, le depth-test va faire que le pixel ne sera pas processe. le stencil va rester a 1.
3.2- si il n'y a pas de geometrie, et que la face arriere est visible, ca veut dire que la light n'eclaire rien le long de ce pixel. si il y a de la geometrie, elle est derriere le volume de la light, et pas affectee. le pixel est processe, le stencil est decremente, et repasse a 0 (ou est set direct a 0 si t'as choisi le mode SET au lieu de DECREMENT, peu importe)
3.3- si le depth test des faces avant avait fail (une geometrie est entre le volume de la light et la camera), le stencil est deja a 0, et le depth test va fail de nouveau, le pixel ne sera pas process, et restera a 0.

a ce moment la, quand l'etape 3 est finie, dans ton stencil buffer, tous les pixels dont la valeur de stencil == 1 sont a l'interieur du volume de ta light smile

4- avec le mode de stencil test a EQUAL et la valeur a 1, le stencil buffer va etre utilise comme un masque binaire. tous les pixels dont le stencil != 1 seront rejetes avant l'execution du pixel shader. vu que t'affiche un fullscreen quad, t'as pas d'overdraw. vu que t'utilise le stencil, tu paye seulement le prix des pixels vraiment lightes. (ca peut aussi etre un rectangle en screenspace qui englobe le volume de la light, evidemment)


un truc chiant c'est le clear complet de stencil avant de process chaque light. c'est tres mauvais. surtout quand tu clear pas la depth avec, vu que le stencil et la depth sont packes dans les memes 32 bits.

mais t'as un moyen simple de remedier a ca:

le stencil buffer, c'est une valeur aribitraire 8 bits.
-> tu peux le clear toutes les 254 lights \o/

a la premiere light, tu set les valeurs de set/test a '1'. pour la 2eme, tu les set a '2', la 3eme, a '3', et quand t'arrive a 255, tu clear le stencil oui

ceci dit t'as 3 draw-calls par light au lieu d'un seul. (4 draw calls si tu combine ca avec le double rendu YUV du post d'avant)
mais c'est pas forcement tres genant, normalement ca reste quand meme avantageux par rapport a tout afficher comme un bourrin... les gains dependent de ton shader de lighting.. bref.. a tester ^^ (si t'as le temps cheeky)

sinon, plus simple:

- tu flip le mode de culling, et t'affiche les faces arrieres de la sphere de ta light, sans depth-test. trigni (merci d'avoir lu tout le pave d'au dessus grin (ou pas cheeky))