mardi 27 décembre 2011

Autre blog...

Le blog d'un autre grand malade... 
http://johnwhigham.blogspot.com/
pas mis à jour très souvent, mais plein d'articles très intéressants...

jeudi 22 décembre 2011

Changement d'atmosphère !

Je n'était pas du tout satisfait du système de rendu d'atmosphère de mon générateur de planète; ce dernier était composé de deux éléments, cohabitant avec plus ou moins de bonheur:

- un meshe toujours orienté dans le plan de la caméra, représentant le "halo" atmosphérique vu de l'espace
- une skybox invisible de l'espace, mais se coloriant en bleu lorsque la camera se trouve sur la surface, coté jour

Lorsque la camera vient de l'espace pour pénetrer dans l'atmosphère, le halo s'efface progressivement, tandis que la skybox se colorie progressivement de la couleur du ciel ...

Par ailleurs, une fois sur la surface, lorsque le soleil se couche, l'effet n'est pas très réaliste : le bleu vire progressivement au noir; j'aimerais avoir les couleurs flamboyantes des vrais couchers de soleils... (j'ai tenté d'ajouter une touche de rouge violet, l'effet était sympa, mais sans plus...).

Sans parler du fait que d'avoir ses deux élements à synchroniser complexifie le code.

Bref ça marche, mais c'est pas terrible... Je me suis donc mis à la recherche d'un meilleur algo, en fouillant sur mon site favori :

http://vterrain.org

J'y ait trouvé la doc suivante, dans laquelle T.Nishita propose un algo se basant sur l'équation de Rayleigh:

http://nis-lab.is.s.u-tokyo.ac.jp/~nis/cdrom/sig93_nis.pdf

et un exemple d'implémentation de cet algo est proposé par S.O'Neil; les sources sont dispos ici :

http://sponeil.net/

Et une explication de son implémentation ici :

http://www.gamedev.net/page/resources/_/technical/graphics-programming-and-theory/real-time-atmospheric-scattering-r2093

L'idée est de rendre l'atmosphere via un modele 3D de sphère, rendue coté INTERIEUR (normales tournées vers le centre de la sphère), en déterminant la couleur pour chaque vertice de la sphère.


Ci dessous, le modèle de sphère que je vais utiliser pour le rendu:





I. Coté CPU:
J'ai repris l'implémentation de S.O'Neil, en l'adaptant à mon API 3D, sous forme d'une classe C++ : 

class cOpticalLengthLookupTable
{

public:

cOpticalLengthLookupTable( IMReal p_InnerRadius, IMReal p_OuterRadius );
~cOpticalLengthLookupTable( void );

void Create( IMReal p_RayleighScaleHeight, IMReal p_MieScaleHeight );
void SetVertexScatteringColor( const cIMVector& p_vertexpos, const cIMVector& p_viewpos, const cIMVector& p_lightdir, cIMVector& p_outcolor );
};


La méthode Create() consiste à précalculer la table des chemins optiques; OuterRadius represente l'altitude Max de la couche atmosphérique de la planète; cela correspond donc au rayon du meshe 'sphere' utilise pour le rendu de l'atmosphere. Le paramètre p_InnerRadius correspond au rayon de la planète.

La méthode SetVertexScatteringColor() calcule pour chaque vertice sa couleur propre, en fonction de la position camera et de la direction du vecteur lumineux.
Dans le cas de mon moteur 3D, le scenegraph peut comporter jusqu'à 3 sources lumineuses; On effectue donc pour chaque vertice au max 3 appels à cOpticalLengthLookupTable::SetVertexScatteringColor(), puis on somme les 3 couleurs résultantes, en prenant soin de clamper le résultat entre 0.0 et 1.0 :

if( islight1 )
{
m_scattering->SetVertexScatteringColor( vertpos, m_viewer_relative_pos, lightdir1, outcol1 );
}


if( islight2 )
{
m_scattering->SetVertexScatteringColor( vertpos, m_viewer_relative_pos, lightdir2, outcol2 );
}


if( islight3 )
{
m_scattering->SetVertexScatteringColor( vertpos, m_viewer_relative_pos, lightdir3, outcol3 );
}


outcolsum[0] = cIMMaths::Clamp( 0.0, 1.0, outcol1[0] + outcol2[0] + outcol3[0] );
outcolsum[1] = cIMMaths::Clamp( 0.0, 1.0, outcol1[1] + outcol2[1] + outcol3[1] );
outcolsum[2] = cIMMaths::Clamp( 0.0, 1.0, outcol1[2] + outcol2[2] + outcol3[2] );


La méthode cOpticalLengthLookupTable::SetVertexScatteringColor() est relativement couteuse en temps CPU; aussi, pour ne pas mettre le FPS à genou, j'ai adopté la méthode suivante : l'ensemble des vertex de la sphere n'est pas traité sur une frame, mais étalé sur plusieurs d'entres elles; le nombre max de vertices traités sur une frame est stocké dans la constante ATMOSCATTERINGVERTEXPERFRAME:
for( long j = 0; j < ATMOSCATTERINGVERTEXPERFRAME; j++ )
{

       // traiter le vertice d'index 'm_atmovertexindex' ...

m_atmovertexindex++;

if( m_atmovertexindex == meshe->m_vertices_final_size )
{
m_atmovertexindex = 0;
}
}

II. Coté GPU:

Il suffit de se munir de shaders capables de restituer pour chaque pixel la couleur interpolée en fonction des couleurs calculées coté CPU pour chaque Vertices : difficile de faire plus simple !! 

Le code HLSL de mes shaders:

float4x4 matViewProjection: register(c0);

struct VS_INPUT 
{
  float4 Position : POSITION0;
  float4 Color    : TEXCOORD0;
};

struct VS_OUTPUT 
{
  float4 Position : POSITION0;
  float4 Color    : TEXCOORD0;
};

VS_OUTPUT vs_main( VS_INPUT Input )
{
  VS_OUTPUT Output;

  Output.Position = mul( Input.Position, matViewProjection );
  Output.Color = Input.Color;
     
  return( Output );   
}

NB : Noter que coté vertex shader je stocke la couleur calculée du vertice dans les coords textures...

 struct PS_INTPUT 
{
  float4 Position : POSITION0;
  float4 Color    : TEXCOORD0;
};


float4 ps_main( PS_INTPUT input ) : COLOR0
{   
  return input.Color;
}

Quelques screenshots:








On aperçoit ici et là quelques artefacts "triangles" : c'est dû à la tesselation du meshe "sphère", peut être un peu faible, mais augmenter le nombre de vertex de la sphère revient à consommer un peu plus de temps CPU; je pense avoir trouvé ici un compromis satisfaisant entre vitesse et rendu :)





Une voie d'amélioration possible serait de "shaderiser" le code de cOpticalLengthLookupTable::SetVertexScatteringColor(), en fournissant au shader la table précalculée des chemins optiques sous la forme d'une texture... 

dimanche 11 décembre 2011

Virtual terrain project

Un site qui compile des documents et des liens vers des sites très intéressant...

vterrain.org

C'est vraiment LA référence pour qui s'intéresse aux techniques et algos de rendus de terrains. A noter que beaucoup des documents qui y sont référencés sont très pointus du point de vue théorie.

mercredi 7 décembre 2011

De la trouadé, pour commencer..

Mon objectif : développer un prototype de moteur de rendu planétaire, à l'instar de ceux-çi : 

http://inovaestudios.com/technology.htm
http://www.outerra.com/



Bien évidemment je suis très loin d'atteindre ce niveau de rendu, néanmoins mon proto, nommé "SuperSphere", est déja relativement avancé (j'ai écris les premières lignes de codes début Janvier 2011).


L'idée est de représenter les planètes en tailles "réelles" (diamètre d'une dizaine de milliers de kilomètres) et de pouvoir s'en approcher, pénétrer dans l’atmosphère jusqu’à pouvoir se poser au niveau du sol, et ce sans aucun écran de chargement ni de transitions;  les planètes présentent des reliefs variés (plaines, chaînes de montagnes, lac, océans... générés de façon procédurale (relief généré "on the fly" par une fonction mathématique, typiquement une fractale...). Au fur et à mesure des déplacements de la caméra le LOD (Level Of Details) est géré par le système pour faire en sorte que les zones proches de la caméra soit détaillées le plus possible, et les zones plus éloignées voient leurs nombres de triangles dégradés, afin de réduire le plus possible la quantité de triangles poussés dans le GPU.... Voilà pour le principe, dans les grandes lignes du moins !


Pour le rendu 3D je m'appuie sur mon moteur 3D générique "I-Motion", qui tourne actuellement sous Windows et s'appuie sur Direct3D (bien que l'ouverture à d'autres middlewares de rendu soit possible grâce à un système de plugins. J'aurais l'occasion de revenir sur ce dernier lors de prochains articles...

En attendant la suite, quelques screenshots...