[DirectX] Ombre ZFail

Le côté programmation du développement d'un jeu vidéo.

Ombre ZFail

Messagepar ShadowTzu » 03 Nov 2005, 11:44

coucou,
Alors j'ai un bug de zbuffer, je pense, en passant ma gestion des ombres de zpass en zfail.

le bon rendu, zpass ou zfail avec le zbuffer en Greater mais il y a le bug quand la camera est dans l'ombre:
Image

zfail buggué, pas de bug quand la camera est dans l'ombre, mais toujours ce probleme de zbuffer:
Image

coté code:
Code: Tout sélectionner
        Device.RenderState.StencilEnable = True
        Device.RenderState.AlphaTestEnable = False
        Device.RenderState.ZBufferWriteEnable = False
        Device.RenderState.ShadeMode = ShadeMode.Flat
        Device.RenderState.StencilFunction = Compare.Always
        Device.RenderState.StencilFail = StencilOperation.Keep
        Device.RenderState.StencilPass = StencilOperation.Keep

        Device.RenderState.ReferenceStencil = 0
        Device.RenderState.StencilMask = -1
        Device.RenderState.StencilWriteMask = -1

        Device.RenderState.AlphaBlendEnable = True
        Device.RenderState.SourceBlend = Blend.Zero
        Device.RenderState.DestinationBlend = Blend.One

''''
 ConstructionOmbre
'''''

        Device.RenderState.CullMode = Cull.Clockwise
        Device.RenderState.StencilZBufferFail = StencilOperation.Increment
        Device.DrawUserPrimitives(PrimitiveType.TriangleList, Tab_Ombre(Index).Nbr_VertDiv3, Tab_Ombre(Index).vertices)

        Device.RenderState.CullMode = Cull.CounterClockwise
        Device.RenderState.StencilZBufferFail = StencilOperation.Decrement
        Device.DrawUserPrimitives(PrimitiveType.TriangleList, Tab_Ombre(Index).Nbr_VertDiv3, Tab_Ombre(Index).vertices)

'[...] RenderState remis par défaut


Rendu de l'ombre:
Code: Tout sélectionner
        Device.RenderState.ZBufferEnable = False
        Device.RenderState.StencilEnable = True
        Device.RenderState.FogEnable = False

        Device.RenderState.AlphaBlendEnable = True
        Device.RenderState.SourceBlend = Blend.SourceAlpha
        Device.RenderState.DestinationBlend = Blend.InvSourceAlpha

        Device.RenderState.CullMode = Cull.CounterClockwise
        Device.RenderState.ReferenceStencil = 0
        Device.RenderState.StencilFunction = Compare.Less
        Device.RenderState.StencilZBufferFail = StencilOperation.Keep
        Device.DrawUserPrimitives(PrimitiveType.TriangleStrip, 2, v)


Voila si quelqu'un a une idée de ce qui ne va pas?
Avatar de l’utilisateur
ShadowTzu
Hello World, I'm new !
 
Messages: 144
Inscription: 12 Juil 2005, 20:40
Localisation: 70

Messagepar Ruffi » 03 Nov 2005, 17:15

J'ai une idée, mais que theorique : j'ai eu un cours parlant des shadow volume il y a 2 semaine mais je n'ai pas eu le temps de le mettre en place.

Mais il parlais de ton probleme, quand la camera est dans une ombre, car elle n'update pas le stencil buffer.

Mais, John Carmack (notre Dieu à tous, loué soit ton nom) a trouvé une solution. Je te file la partie de mon cours en parlant, sans pouvoir t'aider plus tant que je ne l'ai pas implementé moi meme :


The Depth-Fail Shadow Volume Algorithm

John Carmack introduced a reverse of the depth-pass algorithm. An 'first-cut' sequence is:

1) Render back faces. Increment stencil value for both depth-pass and depth-fail.
2) Render front faces of shadow volumes. Decrement stencil value for both depth-pass and depth-fail.
3) Render back faces of shadow volumes. Decrement stencil value for depth-pass. Do nothing for depth-fail.
4) Render front faces of shadow volumes. Increment stencil value for depth-pass. Do nothing for depth-fail.

The first two steps ensure positive values are left in the stencil buffer when the eye is inside a view volume. Otherwise the increment and decrement cancel each other out.


Final Depth-Fail Algorithm

1) Render back faces of shadow volumes. Increment stencil values for depth-fail. Do nothing for depth-pass. Disable updating of frame buffer and Z-buffer.
2) Render front faces of shadow volumes. Decrement stencil value for depth-fail. Do nothing for depth-pass. Disable updating of frame buffer and Z-buffer.


Shadow Volume Capping

If a shadow volume intersects the near plane of the view frustum, new polygons must be created to cap the shadow volume.

Capping polygons can be created by projecting the casters back faces onto the near plane and use these to close the shadow volume.
Ordering of vertices must be reversed to ensure outward facing normals.

Likewise front capping polygons can be extruded onto the back plane of the frustum with vertex order reversal to produce back capping polygons.


J'espere t'avoir été utile :D
Avatar de l’utilisateur
Ruffi
Hello World, I'm new !
 
Messages: 114
Inscription: 10 Avr 2005, 12:56
Localisation: Loin...

Messagepar ShadowTzu » 03 Nov 2005, 17:21

oui c'est cette technique que j'utilise, appelé Carmack's reverse, ou Zfail
Mon problème peu venir du Shadow Volume Capping, mais je ne sais pas du tout comment le résoudre

Edit: Problème résolut :D
Cela venait bien de la construction de l'ombre, il fallait rajouter des "cap" (couvercle) aux 2 coté du volume d'ombre.
Avatar de l’utilisateur
ShadowTzu
Hello World, I'm new !
 
Messages: 144
Inscription: 12 Juil 2005, 20:40
Localisation: 70

Messagepar Loulou » 03 Nov 2005, 19:37

A noter que si tu projètes à l'infini, tu peux te passer du back-cap.

Il existe plein plein d'optimisations pour les shadows volumes, si ça t'interesse je peux développer un peu.
Loulou
Hello World, I'm new !
 
Messages: 702
Inscription: 10 Avr 2005, 12:00

Messagepar kratolp » 04 Nov 2005, 13:40

eh eh, oui le ZFail ne marche que sur des volumes fermes.
Une autre technique pour les shadow volumes c'est le ZP+
http://artis.inrialpes.fr/Publications/2005/HHLH05/
kratolp
Hello World, I'm new !
 
Messages: 146
Inscription: 14 Juil 2005, 23:57

Messagepar Mickael » 04 Nov 2005, 15:17

Tu peux developper loulou ? :)
Mickael
Avatar de l’utilisateur
Mickael
Hello World, I'm new !
 
Messages: 140
Inscription: 17 Avr 2005, 11:51

Messagepar ShadowTzu » 04 Nov 2005, 16:32

ne construire l'ombre que quand la source de lumière ou l'objet bouge, ou tout les X frames, par exemple. :)
Avatar de l’utilisateur
ShadowTzu
Hello World, I'm new !
 
Messages: 144
Inscription: 12 Juil 2005, 20:40
Localisation: 70

Messagepar Loulou » 04 Nov 2005, 16:45

Ca fait déjà pas mal de temps, mais pêle-mêle :

- On peut n'utiliser le Z-Fail (qui je le rappelle est plus gourmand) que lorsque la caméra se trouve dans un shadow volume ; pour tout le reste on peut switcher en Z-Pass. Le test est lui plutôt simple, une intersection ray / box sur chaque objet devrait faire l'affaire.

- En Z-Fail, il vaut mieux utiliser une matrice de projection infinie. Ca permet de ne pas avoir de problème avec le far plane, de se passer du back cap, et d'extruder des triangles plutôt que des quads.

- Il est assez facile de faire un culling des shadow volumes. Par exemple pour une lumière omnidirectionnelle, on peut limiter l'extrusion à la portée de la source lumineuse. Pour une lumière directionnelle, on peut également virer pas mal de calculs inutiles avec de simples tests ray / box. Pour une lumière omni encore, on peut facilement savoir si le shadow volume d'un objet donné sera à l'écran ou non.

- Ne pas oublier les vertex shaders pour l'extrusion. Plus coûteux en mémoire mais mais nettement plus avantageux, surtout avec les capacités du hardware actuel.

- Les external triangles, qui permettent de gérer les lumières omni et directionnelles de la même manière. Un external triangle est en fait un triangle dont au moins l'un des sommets a une composante w négative : ça permet de modéliser "l'inverse" de ce triangle, à savoir un quad. C'est un peu compliqué à piger au début, mais c'est vraiment l'optimisation que je préfère.

- Une technique vue sur gamedev.net il me semble : le silouhete tracking, pour accélerer la détection de la silouhete. Le principe est simple : au lieu de faire une détection complète à chaque frame, on effectue une détection incrémentale basée sur la silouhete précédente et le déplacement de la lumière ou de l'objet.

- On peut utiliser les TRIANGLE_FAN lorsqu'il n'y a pas de back cap.

- On peut gratter un peu en utilisant le two-sided stencil buffer sur les cartes qui le supportent.

- Technique qui permet d'économiser pas mal aussi : utiliser des versions low poly des modèles pour fabriquer le shadow volume. Attention cependant, si on ne respecte pas certaines conditions ça peut mener à des résultats incorrects.

J'ai pas encore eu l'occasion d'étudier le Z-Pass+, mais ça avait l'air pas mal aussi.


Je pense synthétiser tout ça dans un bon gros tutoriel, quand je serai arrivé aux shadow volumes dans ma série de tutos sur la création d'un moteur 3D.
Loulou
Hello World, I'm new !
 
Messages: 702
Inscription: 10 Avr 2005, 12:00

Messagepar Ruffi » 12 Nov 2005, 17:30

Je commence à reflechir à comment je vais gerer les shadow volumes pour mon moteur :

J'ai fait une version simplifié pour tester le zpass et le zfail en créant le shadow volume à partir d'un simple triangle.
Pour le coup, j'ai compris le probleme du capping qu'avait ShadowTzu.

Maintenant je reflechit à comment creer les shadow volume de plus gros models.
J'ai lu dans "ShaderX2: Introductions & Tutorials with DirectX 9"qu'il y a 2 façon de gerer ça :
- avec le CPU : on calcule la silouette de l'objet par raport a la lumiere, et on l'extrude.
- avec le GPU : on créé un créé un nouveau mesh basé sur l'original, et on lui ajoute des polygones à chaque bord. Le shader extrudera ces polygone afin de creer le shadow volume. C'est la methode employé dans les sample du SDK de DirectX ( http://msdn.microsoft.com/library/default.asp?url=/library/en-us/directx9_c/directx/graphics/TutorialsAndSamples/Samples/ShadowVolume.asp )

Je pense que la version GPU est plus indiqué, surtout si le mesh est skinné par shaders. D'ailleur, ce dernier cas risque d'etre assez trivial à implementer. :)

Du coup, je me demande quel solution tu a utilisé pour Tzu3D.
Avatar de l’utilisateur
Ruffi
Hello World, I'm new !
 
Messages: 114
Inscription: 10 Avr 2005, 12:56
Localisation: Loin...

Messagepar ShadowTzu » 13 Nov 2005, 18:50

celle du sdk directx :)
(sans shader)
Avatar de l’utilisateur
ShadowTzu
Hello World, I'm new !
 
Messages: 144
Inscription: 12 Juil 2005, 20:40
Localisation: 70

Messagepar Ruffi » 02 Déc 2005, 02:07

Je me met (enfin) à coder mes ShadowVolume, par shader.

Sur tout les tutoriaux, que ce soit en zpass ou zfaill, par shader ou CPU, ils ne parlent jamais de comment rendre l'ombre un fois le stencil remplit.

Avant, j'affichait un quad noir avec de la transparence. Mais il y avait un probleme : le caping de devant fait que l'objet à l'origine de l'ombre est entierement ombré.

En regardant comment faisait le sdk de DirectX, j'ai vu qu'il faisait 2 rendu de la scene :
le premier juste avec la lumiere ambiante
( rendu des shadow volume dans le stencil)
la deusieme avec les lumieres, mais sans l'ambiante, en utilisant le stencil.

Bon voila quoi... rendre 2 fois la scene... :00000023:
Bon, je peut chercher à optimiser en n'affichant pas les objet entierement dans l'ombre durant le deusieme rendu, mais vu leur faible nombre ça serai pas grand chose.
Ou alors rendre la scene une seule fois mais en 2 passes dans le shader, en les affichant dans 2 endroits differents (backbuffer et dans une texture par exemple) qu'on fusionera selon le stencil une fois qu'il aura été calculé. (idée que j'ai eu en tapant ce post :D , mais qui a surement deja été tésté)

Comment gerrez vous ça ?
Avatar de l’utilisateur
Ruffi
Hello World, I'm new !
 
Messages: 114
Inscription: 10 Avr 2005, 12:56
Localisation: Loin...

Messagepar Loulou » 02 Déc 2005, 12:57

Il y a deux manières de rendre les ombres :

1- La manière très facile mais très incorrecte ; sera vite limitée si tu veux gérer plus d'une lumière, ou des lumières de différentes couleurs. C'est l'histoire du quad que tu rends sur l'écran pour foncer les zones d'ombre (il y a une feinte toute bête pour résoudre ton problème, mais je ne m'en rappelle plus).

2- La manière très peu performante, mais physiquement correcte : le rendu multi-passes (utilisé dans Doom 3 par exemple). En gros au lieu de foncer les zones d'ombre, tu vas partir d'une scène noire et illuminer les zones éclairées. Très peu performant car une passe par lumière. Par contre le résultat est très chouette.
Loulou
Hello World, I'm new !
 
Messages: 702
Inscription: 10 Avr 2005, 12:00

Messagepar janta » 02 Déc 2005, 15:43

Loulou a écrit:2- La manière très peu performante, mais physiquement correcte : le rendu multi-passes (utilisé dans Doom 3 par exemple). En gros au lieu de foncer les zones d'ombre, tu vas partir d'une scène noire et illuminer les zones éclairées. Très peu performant car une passe par lumière. Par contre le résultat est très chouette.

Et puis comme ca apres tu n'as pas besoin d'allumer les radiateurs chez toi en hiver :) ;) :D
janta
Hello World, I'm new !
 
Messages: 16
Inscription: 25 Nov 2005, 18:11
Localisation: Québec

Messagepar Ruffi » 02 Déc 2005, 18:36

La technique du SDK de directX est proche de la deusieme technique, mise a part qu'elle n'est pas multi-passe, mais plutot "mutli-rendu".

J'ai lu, je ne sais plus trop où, que faire plusieures passes dans un shader etait peu gourmant car les cartes graphiques etaient optimisé pour.
Une pass ambiante suivi d'une passe par lumiere pour chaque objet serait donc mieux que de reaficher tout la scene a chaque fois.

Par contre, il faudrai un buffer pour la version sombre, et un autre par lumiere.
Il ne me semble pas que DirectX permette de creer plusieurs backbuffer
Avatar de l’utilisateur
Ruffi
Hello World, I'm new !
 
Messages: 114
Inscription: 10 Avr 2005, 12:56
Localisation: Loin...

Messagepar grob1212 » 02 Déc 2005, 18:44

[quote="janta"]Et puis comme ca apres tu n'as pas besoin d'allumer les radiateurs chez toi en hiver :) ]


Et si tu veux carrément la cheminée en direct, il te reste touujours le photon mapping sur GPU ! :D
grob1212
Hello World, I'm new !
 
Messages: 277
Inscription: 13 Avr 2005, 15:12

Messagepar Loulou » 02 Déc 2005, 20:52

Par contre, il faudrai un buffer pour la version sombre, et un autre par lumiere.
Il ne me semble pas que DirectX permette de creer plusieurs backbuffer

Si bien sûr. Plus précisément, il te faut une texture de rendu.

Je ne me souviens plus du lien, mais à l'époque j'avais trouvé un mini-article très simple décrivant en détail chaque passe de l'algorithme.

En gros, il faut rendre la scène texturée dans ton color buffer, puis additionner les contributions de chaque lumière dans une texture de rendu separée, que tu vas finalement moduler (multiplier) avec ton color buffer pour avoir le résultat final.
Loulou
Hello World, I'm new !
 
Messages: 702
Inscription: 10 Avr 2005, 12:00

Messagepar yoyonel » 03 Déc 2005, 01:11

Juste un ptit mot sur le sample de DirectX.
Je vous conseille de regarder leur option (tres intéressante d'ailleurs) d'évaluation du FillRates des volumes d'ombres (du moins leurs enveloppes).

Avec "leur méthode", ils ont tres rapidement un nombre tres élévé de recouvrement (leur échelle va jusqu'à plus de 71 recouvrements !).
Cela vient directement du fait qu'ils extrudent l'ensemble des triangles (ou aretes de triangles) qui ne percoivent pas la lumiere. Ca simplifie certe les calculs (absence de calcul de silhouettes de contours) mais ca augmente rapidement le nombre de polygone et également (et ca c'est plus critique) le fillrate de ses volumes d'ombres.
Je pense qu'il faut prendre en compte assez rapidement ce facteur de FillRate, car ... c'est vraiment tres violent :-D.

Par contre je suis encore en train de lire le tuto. et le source du sample, ca se trouve je suis a coté de la plaque (pour l'extrusion de l'ensemble des triangles unlight) mais je suis quasi sure de ce que je dis (en concordant avec l'affichage du FillRate de leur application).

Juste pour terminer :-), leur méthode est loin d'etre bete, c'est une solution intéressante dans le cas d'une intégration complète sur GPU.
De plus, en ce moment je m'interesse aux modeles qui utilisent du vertex skinning pour l'animation, et gérer le shadow volume sur ces trucs ... c'est vraiment tres tendu (sans s'amuser a refaire le skinning sur CPU).
D'ailleurs j'ai toujours pas trouvé de solutions :-| ... Si quelqu'1 a une idée je suis preneur :p).
L'avantage de tout faire sur GPU, c'est justement d'utiliser toute les fonctionnalités hardware (en l'occurrence le skinning) ... mais bon les couts en terme de polygones et surtout en fillrate font vraiment trop mal pour que ca soit ma solution définitive :-|
Avatar de l’utilisateur
yoyonel
Hello World, I'm new !
 
Messages: 105
Inscription: 12 Avr 2005, 21:25
Localisation: Lyon

Messagepar Ruffi » 27 Déc 2005, 00:49

Loulou, aurais-tu mis la main sur la feinte toute bête dont tu parlais plus tot ? :D

Sinon, je voudrai afficher un quad à l'ecran (pour les rendu dans les texture par exemple), en imaginant que les coordonées de l'ecran aillent de 0 à 1 en X et 0 à 1 en Y en rendu ortho.
Du coup, je pensait passer par un simple shader, mais je ne sais pas comment calculer calculer la matrice de projection.:00000017:
Avatar de l’utilisateur
Ruffi
Hello World, I'm new !
 
Messages: 114
Inscription: 10 Avr 2005, 12:56
Localisation: Loin...

Messagepar Ruffi » 28 Déc 2005, 15:47

C'est bon, j'ai trouvé comment calculer une matrice de projection.
Pour ceux que ça pourait interesser :
http://graphics.ucsd.edu/courses/cse167_f05/CSE167_04.ppt
Avatar de l’utilisateur
Ruffi
Hello World, I'm new !
 
Messages: 114
Inscription: 10 Avr 2005, 12:56
Localisation: Loin...

Messagepar Ruffi » 04 Jan 2006, 21:19

Voila, je me suis enfin mis au shadows volumes.
Mais ça marche pas terrible pour le moment :(
Si le rendu en affichant le shadow volume semble correct :

Image
http://img250.imageshack.us/img250/8705/showvolumes2bb.jpg

Des que je veux afficher le quad noir, il presente de serieux problemes d'affichage :
Image
http://img257.imageshack.us/img257/2237/bug11vt.jpg
Image
http://img435.imageshack.us/img435/3085/bug26hm.jpg


J'ai suivi l'exemple du SDK : mon code est quasiment similaire concernant la creation du shadow volume ainsi que son shader :

Code: Tout sélectionner
bool cStaticShadowVolume::create( cD3DGraphics* graphics,
                                  std::string filename )
{
    m_graphics = graphics;

    LPD3DXBUFFER materialsBuffer = NULL;
    DWORD         numMaterial = 0;
    LPD3DXMESH     meshTMP = NULL;   
    LPD3DXMESH     mesh = NULL;   
    HRESULT         result;


    //--------------------------------
    // First, we create the mesh, by loading it and modifying it to the good vertex declaration
    // We don't use the original mesh (the shadow caster) because maybe we will use a low polygoned mesh

    // Loading the mesh
    result = D3DXLoadMeshFromX( filename.data(), D3DXMESH_SYSTEMMEM, m_graphics->getDevice(),
                                NULL, &materialsBuffer, NULL, &numMaterial, &meshTMP ) ;
    if( FAILED( result ) )
    {
        Log->sendFatalError( "cStaticShadowVolume::create() -> Cannot open mesh : " + filename );
        return false;
    }

    // Converting the mesh
    result = meshTMP->CloneMesh( D3DXMESH_32BIT, s_declaration, m_graphics->getDevice(), &mesh );
    if( FAILED( result ) )
    {
        Log->sendFatalError( "cStaticShadowVolume::create() -> Cannot clone mesh : " + filename );
        SafeRelease( meshTMP );
        return false;
    }
    SafeRelease( meshTMP );



    //--------------------------------
    // Now, we need some information about the mesh, more exactly the adjency betweens faces

    DWORD *adjency = new DWORD[ 3*mesh->GetNumFaces() ];
    DWORD *pointRep = new DWORD[ mesh->GetNumVertices() ];

    // Generate adjacency information
    result = mesh->GenerateAdjacency( ADJACENCY_EPSILON, adjency );
    if( FAILED( result ) )
    {
        SafeDeleteArray( adjency ); SafeDeleteArray( pointRep );
        SafeRelease( mesh );
        Log->sendFatalError( "cStaticShadowVolume::create() -> Cannot generate adjency : " + filename );
        return false;
    }

    // Convert the adjency
    result = mesh->ConvertAdjacencyToPointReps( adjency, pointRep );
    if( FAILED( result ) )
    {
        SafeDeleteArray( adjency ); SafeDeleteArray( pointRep );
        SafeRelease( mesh );
        Log->sendFatalError( "cStaticShadowVolume::create() -> Cannot convert adjency to point reps : " + filename );
        return false;
    }
    delete[] adjency;



    //--------------------------------
    // Create data needed to begin the shadow mesh creation

    // Get the Vertex buffer and the index buffer
    sShadowVertex*    VBData = NULL;
    DWORD*            IBData = NULL;

    mesh->LockVertexBuffer( 0, (LPVOID*)&VBData );
    mesh->LockIndexBuffer(  0, (LPVOID*)&IBData );

    if( !VBData || !IBData )
    {
        SafeDeleteArray( pointRep );
        SafeRelease( mesh );
        Log->sendFatalError( "cStaticShadowVolume::create() -> Cannot lock buffers : " + filename );
        return false;
    }

    // Create the edge mapping array
    DWORD numEdges = mesh->GetNumFaces() * 3;
    cEdgeMapping *mapping = new cEdgeMapping[ numEdges ];
    int numMaps = 0;  // Number of entries that exist in mapping

    // Create a new mesh
    ID3DXMesh *newMesh;
    result = D3DXCreateMesh( mesh->GetNumFaces()+numEdges*2, mesh->GetNumFaces()*3,
                             D3DXMESH_32BIT, s_declaration,    m_graphics->getDevice(), &newMesh );
    if( FAILED( result ) )
    {
        SafeDeleteArray( pointRep ); SafeDeleteArray( mapping );
        mesh->UnlockVertexBuffer(); mesh->UnlockIndexBuffer();
        SafeRelease( mesh );
        Log->sendFatalError( "cStaticShadowVolume::create() -> Cannot create new mesh" );
        return false;
    }

    // Get the Vertex buffer and the index buffer of this new mesh
    sShadowVertex*    newVBData = NULL;
    DWORD*            newIBData = NULL;
    sShadowVertex*    nextVertex = NULL;    // will store the location in the VB to write the next vertex to
    int                nextIndex = 0;        // will store the index in the IB where the next vertex index will be stored

    newMesh->LockVertexBuffer( 0, (LPVOID*)&newVBData );
    newMesh->LockIndexBuffer(  0, (LPVOID*)&newIBData );

    if( !newVBData || !newIBData )
    {
        SafeDeleteArray( pointRep ); SafeDeleteArray( mapping );
        mesh->UnlockVertexBuffer(); mesh->UnlockIndexBuffer();
        SafeRelease( mesh ); SafeRelease( newMesh );
        Log->sendFatalError( "cStaticShadowVolume::create() -> Cannot lock buffers : " + filename );
        return false;
    }

    // set these buffer to zero
    ZeroMemory( newVBData, newMesh->GetNumVertices()*newMesh->GetNumBytesPerVertex() );
    ZeroMemory( newIBData, sizeof(DWORD)*newMesh->GetNumFaces()*3 );
    nextVertex = newVBData;



    //--------------------------------
    // Create the shadow volume ( without patching non-shared egdes)

    // iterate through the faces
    for( uint face=0; face<mesh->GetNumFaces(); ++face )
    {

        //--------------------------------
        // Set the 3 vertices in the news VB and IB

        // Copy the vertex data for all 3 vertices
        CopyMemory( nextVertex,   VBData+IBData[face*3]  , sizeof(sShadowVertex) );
        CopyMemory( nextVertex+1, VBData+IBData[face*3+1], sizeof(sShadowVertex) );
        CopyMemory( nextVertex+2, VBData+IBData[face*3+2], sizeof(sShadowVertex) );

        // Write out the face
        newIBData[ nextIndex++ ] = face*3;
        newIBData[ nextIndex++ ] = face*3+1;
        newIBData[ nextIndex++ ] = face*3+2;

        // Compute the face normal and assign it to the normals of the vertices.
        D3DXVECTOR3 v1, v2;  // v1 and v2 are the edge vectors of the face
        D3DXVECTOR3 vNormal;
        v1 = *(D3DXVECTOR3*)(nextVertex + 1) - *(D3DXVECTOR3*)nextVertex;
        v2 = *(D3DXVECTOR3*)(nextVertex + 2) - *(D3DXVECTOR3*)(nextVertex + 1);
        D3DXVec3Cross( &vNormal, &v1, &v2 );
        D3DXVec3Normalize( &vNormal, &vNormal );

        nextVertex->normal     = vNormal;
        (nextVertex+1)->normal = vNormal;
        (nextVertex+2)->normal = vNormal;

        nextVertex += 3;

        //--------------------------------
        // Add the face's edges to the edge mapping table


        //**** Edge 1 ****//

        int index;
        int vertIndex[3] = { pointRep[ IBData[ face*3   ] ],
                             pointRep[ IBData[ face*3+1 ] ],
                             pointRep[ IBData[ face*3+2 ] ] };
        index = findEdgeInMappingTable( vertIndex[0], vertIndex[1], mapping, numEdges );

        if( index == -1 )
        {
            SafeDeleteArray( pointRep ); SafeDeleteArray( mapping );
            mesh->UnlockVertexBuffer(); mesh->UnlockIndexBuffer(); newMesh->UnlockVertexBuffer(); newMesh->UnlockIndexBuffer();
            SafeRelease( mesh ); SafeRelease( newMesh );
            Log->sendFatalError( "cStaticShadowVolume::create() -> finding edge in mapping table failed : " + filename );
            return false;
        }

        if( mapping[ index ].m_oldEdge[0] == -1 && mapping[ index ].m_oldEdge[1] == -1 )
        {
            // No entry for this edge yet.  Initialize one.
            mapping[ index ].m_oldEdge[0] = vertIndex[0];
            mapping[ index ].m_oldEdge[1] = vertIndex[1];
            mapping[ index ].m_newEdge[0][0] = face*3;
            mapping[ index ].m_newEdge[0][1] = face*3+1;

            ++numMaps;
        }
        else
        {
            mapping[ index ].m_newEdge[1][0] = face*3;      // For clarity
            mapping[ index ].m_newEdge[1][1] = face*3+1;

            // First triangle
            newIBData[ nextIndex++ ] = mapping[ index ].m_newEdge[0][1];
            newIBData[ nextIndex++ ] = mapping[ index ].m_newEdge[0][0];
            newIBData[ nextIndex++ ] = mapping[ index ].m_newEdge[1][0];

            // Second triangle
            newIBData[ nextIndex++ ] = mapping[ index ].m_newEdge[1][1];
            newIBData[ nextIndex++ ] = mapping[ index ].m_newEdge[1][0];
            newIBData[ nextIndex++ ] = mapping[ index ].m_newEdge[0][0];

            // mapping[ index ] is no longer needed.
            // Copy the last map entry over and decrement the map count.
            mapping[ index ] = mapping[ numMaps-1 ];
            FillMemory( &mapping[ numMaps-1 ], sizeof( mapping[ numMaps-1 ] ), 0xFF );
            --numMaps;
        }



        //**** Edge 2 ****//

        index = findEdgeInMappingTable( vertIndex[1], vertIndex[2], mapping, numEdges );

        if( index == -1 )
        {
            SafeDeleteArray( pointRep ); SafeDeleteArray( mapping );
            mesh->UnlockVertexBuffer(); mesh->UnlockIndexBuffer(); newMesh->UnlockVertexBuffer(); newMesh->UnlockIndexBuffer();
            SafeRelease( mesh ); SafeRelease( newMesh );
            Log->sendFatalError( "cStaticShadowVolume::create() -> finding edge in mapping table failed : " + filename );
            return false;
        }

        if( mapping[ index ].m_oldEdge[0] == -1 && mapping[ index ].m_oldEdge[1] == -1 )
        {
            // No entry for this edge yet.  Initialize one.
            mapping[ index ].m_oldEdge[0] = vertIndex[1];
            mapping[ index ].m_oldEdge[1] = vertIndex[2];
            mapping[ index ].m_newEdge[0][0] = face*3+1;
            mapping[ index ].m_newEdge[0][1] = face*3+2;

            ++numMaps;
        }
        else
        {
            mapping[ index ].m_newEdge[1][0] = face*3+1;      // For clarity
            mapping[ index ].m_newEdge[1][1] = face*3+2;

            // First triangle
            newIBData[ nextIndex++ ] = mapping[ index ].m_newEdge[0][1];
            newIBData[ nextIndex++ ] = mapping[ index ].m_newEdge[0][0];
            newIBData[ nextIndex++ ] = mapping[ index ].m_newEdge[1][0];

            // Second triangle
            newIBData[ nextIndex++ ] = mapping[ index ].m_newEdge[1][1];
            newIBData[ nextIndex++ ] = mapping[ index ].m_newEdge[1][0];
            newIBData[ nextIndex++ ] = mapping[ index ].m_newEdge[0][0];

            // mapping[ index ] is no longer needed.
            // Copy the last map entry over and decrement the map count.
            mapping[ index ] = mapping[ numMaps-1 ];
            FillMemory( &mapping[ numMaps-1 ], sizeof( mapping[ numMaps-1 ] ), 0xFF );
            --numMaps;
        }



        //**** Edge 3 ****//

        index = findEdgeInMappingTable( vertIndex[2], vertIndex[0], mapping, numEdges );

        if( index == -1 )
        {
            SafeDeleteArray( pointRep ); SafeDeleteArray( mapping );
            mesh->UnlockVertexBuffer(); mesh->UnlockIndexBuffer(); newMesh->UnlockVertexBuffer(); newMesh->UnlockIndexBuffer();
            SafeRelease( mesh ); SafeRelease( newMesh );
            Log->sendFatalError( "cStaticShadowVolume::create() -> finding edge in mapping table failed : " + filename );
            return false;
        }

        if( mapping[ index ].m_oldEdge[0] == -1 && mapping[ index ].m_oldEdge[1] == -1 )
        {
            // No entry for this edge yet.  Initialize one.
            mapping[ index ].m_oldEdge[0] = vertIndex[2];
            mapping[ index ].m_oldEdge[1] = vertIndex[0];
            mapping[ index ].m_newEdge[0][0] = face*3+2;
            mapping[ index ].m_newEdge[0][1] = face*3;

            ++numMaps;
        }
        else
        {
            mapping[ index ].m_newEdge[1][0] = face*3+2;      // For clarity
            mapping[ index ].m_newEdge[1][1] = face*3;

            // First triangle
            newIBData[ nextIndex++ ] = mapping[ index ].m_newEdge[0][1];
            newIBData[ nextIndex++ ] = mapping[ index ].m_newEdge[0][0];
            newIBData[ nextIndex++ ] = mapping[ index ].m_newEdge[1][0];

            // Second triangle
            newIBData[ nextIndex++ ] = mapping[ index ].m_newEdge[1][1];
            newIBData[ nextIndex++ ] = mapping[ index ].m_newEdge[1][0];
            newIBData[ nextIndex++ ] = mapping[ index ].m_newEdge[0][0];

            // mapping[ index ] is no longer needed.
            // Copy the last map entry over and decrement the map count.
            mapping[ index ] = mapping[ numMaps-1 ];
            FillMemory( &mapping[ numMaps-1 ], sizeof( mapping[ numMaps-1 ] ), 0xFF );
            --numMaps;
        }
    }

   
    //--------------------------------
    // Now, the entries in the edge mapping table represent non-shared edges.
    // So the shadow mesh has some holes that we need to patch.

   
    //--------------------------------
    // First, we create a new mesh large enought for all news vertex and index datas

    sShadowVertex*    patchVBData = NULL;
    DWORD*            patchIBData = NULL;
    ID3DXMesh*        patchMesh    = NULL;

    result = D3DXCreateMesh( nextIndex/3 + numMaps*7,    // the maximum number of faces
                             ( mesh->GetNumFaces()+numMaps )*3,    // the maximum number of indices
                             D3DXMESH_32BIT, s_declaration, m_graphics->getDevice(), &patchMesh );

    if( FAILED( result ) )
    {
            SafeDeleteArray( pointRep ); SafeDeleteArray( mapping );
            mesh->UnlockVertexBuffer(); mesh->UnlockIndexBuffer(); newMesh->UnlockVertexBuffer(); newMesh->UnlockIndexBuffer();
            SafeRelease( mesh ); SafeRelease( newMesh );
            Log->sendFatalError( "cStaticShadowVolume::create() -> cannot create new mesh " );
            return false;
    }

   
    //--------------------------------
    // Get the Vertex buffer and the index buffer of this patch mesh

    patchMesh->LockVertexBuffer( 0, (LPVOID*)&patchVBData );
    patchMesh->LockIndexBuffer(  0, (LPVOID*)&patchIBData );

    if( !patchVBData || !patchIBData )
    {
        SafeDeleteArray( pointRep ); SafeDeleteArray( mapping );
        mesh->UnlockVertexBuffer(); mesh->UnlockIndexBuffer(); newMesh->UnlockVertexBuffer(); newMesh->UnlockIndexBuffer();
        SafeRelease( mesh ); SafeRelease( newMesh ); SafeRelease( patchMesh );
        Log->sendFatalError( "cStaticShadowVolume::create() -> Cannot lock buffers : " + filename );
        return false;
    }

   
    //--------------------------------
    // Set vertex buffer and index buffer initials values

    ZeroMemory( patchVBData, sizeof(sShadowVertex) * ( mesh->GetNumFaces() + numMaps ) * 3 );
    ZeroMemory( patchIBData, sizeof(DWORD) * ( nextIndex + 3 * numMaps * 7 ) );

    // Copy the data from one mesh to the other
    CopyMemory( patchVBData, newVBData, sizeof(sShadowVertex)*mesh->GetNumFaces()*3 );
    CopyMemory( patchIBData, newIBData, sizeof(DWORD)*nextIndex );

    // release the new mesh, and set it as the patch mesh
    newMesh->UnlockVertexBuffer();
    newMesh->UnlockIndexBuffer();
    newVBData = patchVBData;
    newIBData = patchIBData;
    SafeRelease( newMesh );
    newMesh = patchMesh;
    patchMesh = NULL;


       
    //--------------------------------
    // Now, we will patch the mesh by iterating through the mapping table
    // - for each shared edge, we will generate a quad
    // - for each non-shared edge, we patch the opening with new faces

    int indiceNextVertex = mesh->GetNumFaces() * 3 ;

    for( int i = 0; i < numMaps; ++i )
    {

        // If the 2nd new edge indexes is -1, this edge is a non-shared one.
        if( mapping[i].m_oldEdge[0] != -1 &&
            mapping[i].m_oldEdge[1] != -1 )
        {
            // We patch the opening by creating new faces.

            if( mapping[i].m_newEdge[1][0] == -1 ||  // must have only one new edge
                mapping[i].m_newEdge[1][1] == -1 )
            {
                // Find another non-shared edge that shares a vertex with the current edge.
                for( int i2 = i+1; i2<numMaps; ++i2 )
                {
                    if( mapping[i2].m_oldEdge[0] != -1 &&       // must have a valid old edge
                        mapping[i2].m_oldEdge[1] != -1 &&
                        ( mapping[i2].m_newEdge[1][0] == -1 ||  // must have only one new edge
                          mapping[i2].m_newEdge[1][1] == -1 ) )
                    {
                        int vertShared = 0;
                        if( mapping[i2].m_oldEdge[0] == mapping[i].m_oldEdge[1] )
                            vertShared++;
                        if( mapping[i2].m_oldEdge[1] == mapping[i].m_oldEdge[0] )
                            vertShared++;

                        if( vertShared == 2 )
                        {
                            // These are the last two edges of this particular opening.
                            // Mark this edge as shared so that a degenerate quad can be created for it.

                            mapping[i2].m_newEdge[1][0] = mapping[i].m_newEdge[0][0];
                            mapping[i2].m_newEdge[1][1] = mapping[i].m_newEdge[0][1];
                            break;
                        }
                        else if( vertShared == 1 )
                        {
                            // "before" and "after" tell us which edge comes before the other.
                            int before, after;
                            if( mapping[i2].m_oldEdge[0] == mapping[i].m_oldEdge[1] )
                            {
                                before = i;
                                after = i2;
                            }
                            else
                            {
                                before = i2;
                                after = i;
                            }

                            // Found such an edge. Now create a face along with two degenerate quads from these two edges.
                            newVBData[ indiceNextVertex   ] = newVBData[ mapping[ after  ].m_newEdge[0][1] ];
                            newVBData[ indiceNextVertex+1 ] = newVBData[ mapping[ before ].m_newEdge[0][1] ];
                            newVBData[ indiceNextVertex+2 ] = newVBData[ mapping[ before ].m_newEdge[0][0] ];

                            // Recompute the normal
                            D3DXVECTOR3 v1 = newVBData[ indiceNextVertex+1 ].position - newVBData[ indiceNextVertex   ].position;
                            D3DXVECTOR3 v2 = newVBData[ indiceNextVertex+2 ].position - newVBData[ indiceNextVertex+1 ].position;
                            D3DXVec3Normalize( &v1, &v1 );
                            D3DXVec3Normalize( &v2, &v2 );
                            D3DXVec3Cross( &newVBData[ indiceNextVertex ].normal, &v1, &v2 );
                            newVBData[ indiceNextVertex+1 ].normal = newVBData[ indiceNextVertex+2 ].normal = newVBData[ indiceNextVertex ].normal;

                            newIBData[ nextIndex   ] = indiceNextVertex;
                            newIBData[ nextIndex+1 ] = indiceNextVertex+1;
                            newIBData[ nextIndex+2 ] = indiceNextVertex+2;

                            // 1st quad
                            newIBData[ nextIndex+3 ] = mapping[ before ].m_newEdge[0][1];
                            newIBData[ nextIndex+4 ] = mapping[ before ].m_newEdge[0][0];
                            newIBData[ nextIndex+5 ] = indiceNextVertex + 1;

                            newIBData[ nextIndex+6 ] = indiceNextVertex + 2;
                            newIBData[ nextIndex+7 ] = indiceNextVertex + 1;
                            newIBData[ nextIndex+8 ] = mapping[ before ].m_newEdge[0][0];

                            // 2nd quad
                            newIBData[ nextIndex+9  ] = mapping[ after ].m_newEdge[0][1];
                            newIBData[ nextIndex+10 ] = mapping[ after ].m_newEdge[0][0];
                            newIBData[ nextIndex+11 ] = indiceNextVertex;

                            newIBData[ nextIndex+12 ] = indiceNextVertex+1;
                            newIBData[ nextIndex+13 ] = indiceNextVertex;
                            newIBData[ nextIndex+14 ] = mapping[ after ].m_newEdge[0][0];

                            // Modify mapping entry i2 to reflect the third edge of the newly added face.

                            if( mapping[i2].m_oldEdge[0] == mapping[i].m_oldEdge[1] )
                            {
                                mapping[i2].m_oldEdge[0] = mapping[i].m_oldEdge[0];
                            }
                            else
                            {
                                mapping[i2].m_oldEdge[1] = mapping[i].m_oldEdge[1];
                            }
                            mapping[i2].m_newEdge[0][0] = indiceNextVertex+2;
                            mapping[i2].m_newEdge[0][1] = indiceNextVertex;

                            // Update next vertex/index positions

                            indiceNextVertex += 3;
                            nextIndex += 15;
                            break;
                        } //  else if( vertShared == 1 )
                    } // if( mapping[i2].m_oldEdge[0] != -1 ...
                }    // for( int i2 = i+1; i2<numMaps; ++i2 )
            }    // if( mapping[i].m_newEdge[1][0] == -1 || ...
            else
            {
                // This is a shared edge.  Create the degenerate quad.

                // First triangle
                newIBData[ nextIndex++ ] = mapping[i].m_newEdge[0][1];
                newIBData[ nextIndex++ ] = mapping[i].m_newEdge[0][0];
                newIBData[ nextIndex++ ] = mapping[i].m_newEdge[1][0];

                // Second triangle
                newIBData[ nextIndex++ ] = mapping[i].m_newEdge[1][1];
                newIBData[ nextIndex++ ] = mapping[i].m_newEdge[1][0];
                newIBData[ nextIndex++ ] = mapping[i].m_newEdge[0][0];
            }                       
        }    // if( mapping[i].m_oldEdge[0] != -1 && ...
    }    // for( int i = 0; i < numMaps; ++i )


    //--------------------------------
    // The output mesh may have an index buffer bigger than what is actually needed, so we create yet
    // another mesh with the exact IB size that we need and output it. 
    // This mesh also uses 16-bit index if 32-bit is not necessary.

    bool need32Bit = ( mesh->GetNumFaces() + numMaps )*3 > 65535;
    result = D3DXCreateMesh( nextIndex/3, ( mesh->GetNumFaces()+numMaps) * 3,
                         D3DXMESH_WRITEONLY | ( need32Bit ? D3DXMESH_32BIT : 0 ),
                         s_declaration, m_graphics->getDevice(), &m_shadowMesh );
    if( FAILED( result ) )
    {
        SafeDeleteArray( pointRep ); SafeDeleteArray( mapping );
        mesh->UnlockVertexBuffer(); mesh->UnlockIndexBuffer(); newMesh->UnlockVertexBuffer(); newMesh->UnlockIndexBuffer();
        SafeRelease( mesh ); SafeRelease( newMesh );
        Log->sendFatalError( "cStaticShadowVolume::create() -> Failed to create final shadow mesh : " + filename );
        return false;
    }

    //--------------------------------
    // Lock the VB and IB of the final mesh

    sShadowVertex*    finalVBData = NULL;
    WORD*            finalIBData = NULL;
    m_shadowMesh->LockVertexBuffer( 0, (LPVOID*)&finalVBData );
    m_shadowMesh->LockIndexBuffer(  0, (LPVOID*)&finalIBData );

    if( !finalVBData || !finalIBData )
    {
        SafeDeleteArray( pointRep ); SafeDeleteArray( mapping );
        mesh->UnlockVertexBuffer(); mesh->UnlockIndexBuffer(); newMesh->UnlockVertexBuffer(); newMesh->UnlockIndexBuffer();
        SafeRelease( mesh ); SafeRelease( newMesh ); SafeRelease( m_shadowMesh );
        Log->sendFatalError( "cStaticShadowVolume::create() -> Failed to lock final shadow mesh : " + filename );
        return false;
    }
   
    //--------------------------------
    // Fill the final mesh

    CopyMemory( finalVBData, newVBData, sizeof(sShadowVertex) * ( mesh->GetNumFaces()+numMaps ) * 3 );
    if( need32Bit )
        CopyMemory( finalIBData, newIBData, sizeof(DWORD) * nextIndex );
    else
    {
        for( int i = 0; i < nextIndex; ++i )
            finalIBData[i] = (WORD)newIBData[i];
    }

   

    //--------------------------------
    // clean up

    newMesh->UnlockVertexBuffer();
    newMesh->UnlockIndexBuffer();
    m_shadowMesh->UnlockVertexBuffer();
    m_shadowMesh->UnlockIndexBuffer();
    mesh->UnlockVertexBuffer();
    mesh->UnlockIndexBuffer();
    SafeRelease( newMesh );
    SafeRelease( mesh );
    SafeDeleteArray( mapping );


    return true;

} // function create()
Avatar de l’utilisateur
Ruffi
Hello World, I'm new !
 
Messages: 114
Inscription: 10 Avr 2005, 12:56
Localisation: Loin...

Messagepar Loulou » 04 Jan 2006, 21:31

Je n'ai pas regardé ton code, mais d'après les captures d'écran il y a de fortes chances pour que le problème vienne du calcul des arêtes et / ou des triangles à extruder. En l'occurence ils ne seraient pas tous dans le bon sens (parfois suffit d'inverser l'ordre de deux sommets pour que ça fonctionne).

Ou alors le modèle est mal défini à la base, mais étant donné qu'il s'agit d'un modèle du SDK je pense qu'on peut exclure cette hypothèse.
Loulou
Hello World, I'm new !
 
Messages: 702
Inscription: 10 Avr 2005, 12:00

Messagepar Ruffi » 04 Jan 2006, 22:04

Effectivement, c'etait bien ça.
Maintenant ça marche nickel, reste plus qu'a optimiser le shader (enlever les if que ma carte graphique semble ne pas trop aimer )

(je vais coriger le code au cas où quelqu'un en aurait besoin)
Avatar de l’utilisateur
Ruffi
Hello World, I'm new !
 
Messages: 114
Inscription: 10 Avr 2005, 12:56
Localisation: Loin...


Retourner vers Programmation

Qui est en ligne

Utilisateurs parcourant ce forum: Yahoo [Bot] et 23 invités

cron