10Fermer12
bearbecueLe 13/02/2012 à 21:19
3)
La c'est la plus interessante des 3.
alors, oui, tu peux effectivement faire ca, 1 shader qui gere jusqu'a 5 lights, et accumuler en additif les memes batch tant que toutes les lights ont pas ete processees.
mais ca va etre tres lent... que dis-je.. atrocement lent.. IGNOBLEMENT lent...
des que t'aura plus de 10 objets et 3 lights, et une resol decente embarrassed
bref, des que t'aura une vraie scene.

Theoriquement, le "rayon d'action" des lights est infini, vu que l'intensite percue varie en 1/(d^2), avec d la distance du pixel a la light.
Une optim courante est de tricher sur la fonction d'attenuation des lights, et pas utiliser 1/(d^2), mais une fonction qui intersecte y=0 au bout d'une certaine distance, tout en donnant l'impression d'une courbe assez smooth, qui pourrait ressembler a la courbe d'attenuation normale. du coup tes lights ont une distance d'influence max.
tu peux ensuite utiliser la bounding sphere de chaque light pour determiner quel batch est affecte par quelle light.
ca t'apporte rien si t'as une seule piece avec 200 lights dedans qui eclairent chacune toute la piece, mais dans des scenes "normales", ca aide deja un peu.

que tu peux aussi calculer le rayon d'influence au dela duquel l'influence de la light n'affecte plus l'image de facon perceptible (hint: couleurs 8-bits affichees, intensite < 1/255 sert a rien, toussa).
ceci dit, si tu utilise un buffer floating-point pour faire ton rendu avant de le blitter a l'ecran, tu pourra avoir des differences en accumulant beaucoup de lights de faible intensite.
ca pete aussi le HDR. apres tu peux toujours te demmerder pour augmenter dynamiquement le rayon d'influence de toutes tes lights en fonction de l'expose de ta camera, mais bon.. t'augmente l'expose, et ca se met a ramer tritop


en pratique, ca ca va bien quand t'as que 2/3 lights par objet.
ca s'appelle le forward-rendering.

foreach(light)
{
  color += draw(object, light);
}


mais en l'etat, sans optims tres rusees ou concessions chiantes, ca pue un peu du zgeg. complexite a la louche: O(G*L), avec G le nombre de batch geometriques a rendre, et L le nombre de lights moyennes par objet



Apres, t'as le deffered-rendering.

l'idee c'est de decoupler lumieres et geometrie, et de descendre a O(G + L)
evidemment les notations de complexite algorithmiques sont pas forcement tres pertinentes, vu que t'as plein d'autres facteurs qui peuvent fausser l'estimation, genre l'overdraw, l'overhead d'avoir de multiples render targets, les plusieurs passes geometriques eventuelles, la distribution des lights, etc.. mais en pratique c'est quand meme bon.

du coup, l'idee du deffered rendering, c'est de dire que tous tes materiaux, pour etre rendus, ont besoin des memes infos, typiquement, pour chaque pixel:

- albedo (la couleur diffuse de l'objet)
- normale
- coeffs speculaires divers, suivant le modele d'eclairage choisi.
- eventuellement depth accessible depuis le shader
- potentiellement d'autres trucs, genre: couleur d'emission, ou trucs du genre

apres, tu te demmerde pour packer tout ca dans un framebuffer avec le nombre le plus faible de rendertargets possibles.
le dit framebuffer s'appelle un G-buffer. (comme Geometry-buffer)

par exemple, tu peux avoir:

rendertarget1 (RGBA) : { diffuse.r, diffuse.g, diffuse.b, specular.power }
rendertarget2 (RGBA) : { normal.x, normal.y, normal.z, specular.intensity }

donc, dans une premiere passe, t'as un shader tout simple qui ecrit dans ces deux render targets (oui, tu peux outputter plusieurs colors dans des render targets differentes dans le meme pixel shader)
il sample la diffuse, eventuellement la normalmap si yen a une, eventuellement une specmap, bref.

a la fin de cette passe, t'as deux rendertargets qui contiennent toutes les normales, diffuse colors, spec coeffs, de ta scene, que tu peux rebinder a un autre shader en tant que textures.

ensuite, dans une deuxieme passe, tu prends toutes les lights de ta scene, et tu les affiche avec un shader qui prend les deux rendertargets d'au dessus en tant que textures, la depth map, et calcule l'eclairage.
si ta light couvre toute la scene, tu la rend avec un fullscreen quad, sinon, t'affiche une primitive basique qui recouvre les zones d'influence de la light a l'ecran (une sphere, ou un carre/rectangle en 2D a l'ecran).

et au final t'as tous les objets de ta scene rendus eclaires.
du coup le shader de lighting est vachement plus simple (une seule light a gerer), le shader des objets aussi, et c'est cool grin

Evidemment, t'es oblige d'utiliser quand meme du forward rendering pour tes objets transparents eclaires :/
avec les derniers shader models, tu peux faker en utilisant un G-buffer avec multisampling. tu le multisample 4x, et t'as 4 layers de geometrie eclairee par pixel possibles (donc 3 layers transparents), ou utiliser un G-buffer supplementaire pour la geometrie transparente, mais dans ce cas t'as une seule couche eclairable.


apres, le deffered rendering, c'est bien, mais c'est un peu relou quand meme, parceque c'est vachement rigide niveau materiaux.

tu as donc une AUTRE option \o/



le light-prepass rendering, qui est une forme de deffered rendering, mais legerement differente.

ca t'oblige a faire deux passes de geometrie, mais:

- c'est plus flexible niveau materiau
- c'est plus leger niveau memoire / framebuffers
- ca peut gerer une putain de chiee de lights, c'est ce qu'on utilise dans hh, et on affiche des lights qui sont en fait des particules, on peut en avoir 50 000 par frame sans probleme... et sur des machines pas si terribles. (du moment que l'overdraw est raisonnable tongue)
- ca marche en DX9 (mais bon c'est pas forcement un argument grin)

le G-buffer ressemble a ca:

- normales
- parametres de spec

une fois le G-buffer rendu, comme au dessus, tu fais une passe sur toutes les lights.
mais c'est pas le materiau final qu'elles vont rendre.
c'est l'eclairage recu par chaque pixel de la scene : le light-buffer
(avec la composante speculaire optionellement dans l'alpha. apres, si tu veux un rendu exact, il te faut du RGB pour le spec aussi, donc il te faut un light-buffer et un spec-buffer separe.)

une fois que t'as le light buffer, tu le file au vrai shader de rendu de tes objets.
pour chaque pixel, il a l'intensite recue, et son lobe de spec, donc il a plus besoin des normales, des specmaps, et autres conneries, sauf pour certains types de materiaux, mais peut quand meme faire des effets tordus si il a envie, rajouter une couleur d'emission si ya besoin, combiner la diffuse comme il veut avec ce qu'il veut, attenuer la spec comme il le sent, absorber et diffuser la lumiere recue comme il veut, etc, etc..

meme combat que le deffered-rendering classique pour les objets transparents.

tiens jvais te faire des screens du G-Buffer de hh smile