2012-08-04 1 views
4

J'écris un jeu dans Visual Studio 2010, en utilisant le framework XNA 4.0. J'ai un modèle de terrain 3D généré à partir d'une carte de hauteur. Ce que j'essaie d'accomplir, c'est de teinter ce modèle dans un rayon donné autour d'un certain point, le but final étant d'afficher au joueur le rayon dans lequel une unité peut se déplacer dans un tour donné. La méthode que je utilise pour dessiner le modèle au moment est le suivant:Teinter un modèle de terrain dans un rayon autour d'un point donné dans XNA 4.0

void DrawModel(Model model, Matrix worldMatrix) 
    { 
     Matrix[] boneTransforms = new Matrix[model.Bones.Count]; 
     model.CopyAbsoluteBoneTransformsTo(boneTransforms); 

     foreach (ModelMesh mesh in model.Meshes) 
     { 
      foreach (BasicEffect effect in mesh.Effects) 
      { 
       effect.World = boneTransforms[mesh.ParentBone.Index] * worldMatrix; 
       effect.View = camera.viewMatrix; 
       effect.Projection = camera.projectionMatrix; 


       effect.EnableDefaultLighting(); 
       effect.EmissiveColor = Color.Green.ToVector3(); 
       effect.PreferPerPixelLighting = true; 

       // Set the fog to match the black background color 
       effect.FogEnabled = true; 
       effect.FogColor = Color.CornflowerBlue.ToVector3(); 
       effect.FogStart = 1000; 
       effect.FogEnd = 3200; 
      } 

      mesh.Draw(); 
     } 
    } 

En outre, dans le cas où il est pertinent, je suivais ce tutoriel http://create.msdn.com/en-US/education/catalog/sample/collision_3d_heightmap pour créer mon heightmap et du terrain.

Merci d'avance pour toute aide!

Répondre

2

Vous pouvez utiliser un shader pour y parvenir ...

vous ne devrez passer comme argument la position mondiale du centre et le rayon, et laisser le pixel shader recevoir le monde de pixel position interpolée à partir du vertex shader comme une coordonnée de texture ... alors il suffit de vérifier la distance de la position du pixel au centre et la teinter avec une couleur si la position du pixel est dans la gamme ...

+0

Je n'ai pas travaillé avec les shaders auparavant. Pourriez-vous expliquer ce que vous voulez dire en passant un pixel shader? Le shader est-il un objet? Merci – Dangerbunny

+0

Un shader est le code exécuté dans le GPU et associé à l'effet, vous pouvez coder votre propre effet ... bien que vous deviez apprendre les bases du shader auparavant, ici vous pouvez trouver quelques infos sur un shader pour faire ce que vous voulez par moi-même http://stackoverflow.com/questions/8000164/how-to-draw-a-circle-on-3d-terrain-in-xna – Blau

+0

Cela fonctionne parfaitement, merci! Aussi je ne peux pas croire que je n'ai pas vu l'autre poste, c'est à peu près exactement la même question que j'avais X) – Dangerbunny

0

La technique que vous recherchez s'appelle la décalcomanie.

Vous devez extraire la partie du terrain où le cercle sera dessiné, appliquer une texture appropriée à cette partie et la dessiner en la mélangeant avec le terrain.

Pour le cas d'un terrain basé sur une grille uniforme, cela ressemblera à ceci:

Vous avez la position centrale de l'autocollant et son rayon. Vous pouvez ensuite déterminer min/max row/col dans la grille, afin que les cellules incluent chaque région dessinée. Créez un nouveau tampon de vertex à partir de ces sommets. Les positions peuvent être lues à partir de la heightmap. Vous devez modifier les coordonnées de la texture, de sorte que la texture sera placée à la bonne position. Supposons que la position centrale ait la coordonnée (0.5, 0.5), la position centrale + (rayon, rayon) a la coordonnée (1, 1) et ainsi de suite. Avec cela, vous devriez être capable de trouver une équation pour les coordonnées de texture pour chaque sommet.

Extracted grid

Dans l'exemple ci-dessus, le sommet rouge en haut à gauche a les coordonnées de texture d'environ (-0.12, -0.05)

Ensuite, vous avez la sous-grille du terrain. Appliquez la texture du décalque. Définissez un biais de profondeur approprié (vous devez essayer certaines valeurs). Dans la plupart des cas, un SlopeScaleDepthBias négatif fonctionnera. Désactivez l'habillage des coordonnées de texture dans l'échantillonneur. Dessinez le sous-réseau.

Voici quelques code VB SlimDX je l'ai écrit à cet effet:

Public Sub Init() 
    Verts = (Math.Ceiling(2 * Radius/TriAngleWidth) + 2)^2 
    Tris = (Math.Ceiling(2 * Radius/TriAngleWidth) + 1)^2 * 2 

    Dim Indices(Tris * 3 - 1) As Integer 
    Dim curN As Integer 
    Dim w As Integer 
    w = (Math.Ceiling(2 * Radius/TriAngleWidth) + 2) 
    For y As Integer = 0 To w - 2 
     For x As Integer = 0 To w - 2 
      Indices(curN) = x + y * w : curN += 1 
      Indices(curN) = x + (y + 1) * w : curN += 1 
      Indices(curN) = (x + 1) + (y) * w : curN += 1 
      Indices(curN) = x + (y + 1) * w : curN += 1 
      Indices(curN) = (x + 1) + (y + 1) * w : curN += 1 
      Indices(curN) = (x + 1) + y * w : curN += 1 
     Next 
    Next 

    VB = New Buffer(D3DDevice, New BufferDescription(Verts * VertexPosTexColor.Struct.SizeOfBytes, ResourceUsage.Dynamic, BindFlags.VertexBuffer, CpuAccessFlags.Write, ResourceOptionFlags.None, VertexPosTexColor.Struct.SizeOfBytes)) 
    IB = New Buffer(D3DDevice, New DataStream(Indices, False, False), New BufferDescription(4 * Tris * 3, ResourceUsage.Default, BindFlags.IndexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 4)) 
End Sub 

Public Sub Update() 
    Dim Vertex(Verts - 1) As VertexPosTexColor.Struct 
    Dim curN As Integer 
    Dim rad As Single 'The decal radius 
    Dim height As Single 
    Dim p As Vector2 
    Dim yx, yz As Integer 
    Dim t As Vector2 'texture coordinates 
    Dim center As Vector2 'decal center 
    For y As Integer = Math.Floor((center.Y - rad)/TriAngleWidth) To Math.Floor((center.Y - rad)/TriAngleWidth) + Math.Ceiling(2 * rad/TriAngleWidth) + 1 
     For x As Integer = Math.Floor((center.X - rad)/TriAngleWidth) To Math.Floor((center.X - rad)/TriAngleWidth) + Math.Ceiling(2 * rad/TriAngleWidth) + 1 
      p.X = x * TriAngleWidth 
      p.Y = y * TriAngleWidth 

      yx = x : yz = y 
      If yx < 0 Then yx = 0 
      If yx > HeightMap.GetUpperBound(0) Then yx = HeightMap.GetUpperBound(0) 
      If yz < 0 Then yz = 0 
      If yz > HeightMap.GetUpperBound(1) Then yz = HeightMap.GetUpperBound(1) 
      height = HeightMap(yx, yz) 
      t.X = (p.X - center.X)/(2 * rad) + 0.5 
      t.Y = (p.Y - center.Y)/(2 * rad) + 0.5 
      Vertex(curN) = New VertexPosTexColor.Struct With {.Position = New Vector3(p.X, hoehe, p.Y), .TexCoord = t, .Color = New Color4(1, 1, 1, 1)} : curN += 1 
     Next 
    Next 
    Dim data = D3DContext.MapSubresource(VB, MapMode.WriteDiscard, MapFlags.None) 
    data.Data.WriteRange(Vertex) 
    D3DContext.UnmapSubresource(VB, 0) 
End Sub 

Et voici le suivant code C#.

public void Init() 
{ 
    Verts = Math.Pow(Math.Ceiling(2 * Radius/TriAngleWidth) + 2, 2); 
    Tris = Math.Pow(Math.Ceiling(2 * Radius/TriAngleWidth) + 1, 2) * 2; 

    int[] Indices = new int[Tris * 3]; 
    int curN; 
    int w; 
    w = (Math.Ceiling(2 * Radius/TriAngleWidth) + 2); 
    for(int y = 0; y <= w - 2; ++y) 
    { 
     for(int x = 0; x <= w - 2; ++x) 
     { 
      Indices[curN] = x + y * w ; curN += 1; 
      Indices[curN] = x + (y + 1) * w ; curN += 1; 
      Indices[curN] = (x + 1) + (y) * w ; curN += 1; 
      Indices[curN] = x + (y + 1) * w ; curN += 1; 
      Indices[curN] = (x + 1) + (y + 1) * w ; curN += 1; 
      Indices[curN] = (x + 1) + y * w ; curN += 1; 
     } 
    } 

    VB = new Buffer(D3DDevice, new BufferDescription(Verts * VertexPosTexColor.Struct.SizeOfBytes, ResourceUsage.Dynamic, BindFlags.VertexBuffer, CpuAccessFlags.Write, ResourceOptionFlags.None, VertexPosTexColor.Struct.SizeOfBytes)); 
    IB = new Buffer(D3DDevice, new DataStream(Indices, False, False), new BufferDescription(4 * Tris * 3, ResourceUsage.Default, BindFlags.IndexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 4)); 
} 

public void Update() 
{ 
    VertexPosTexColor.Struct[] Vertex = new VertexPosTexColor.Struct[Verts] ; 
    int curN; 
    float rad; //The decal radius 
    float height; 
    Vector2 p; 
    int yx, yz; 
    Vector2 t; //texture coordinates 
    Vector2 center; //decal center 
    for(int y = Math.Floor((center.Y - rad)/TriAngleWidth); y <= Math.Floor((center.Y - rad)/TriAngleWidth) + Math.Ceiling(2 * rad/TriAngleWidth) + 1; ++y) 
     for(int x = Math.Floor((center.X - rad)/TriAngleWidth); x <= Math.Floor((center.X - rad)/TriAngleWidth) + Math.Ceiling(2 * rad/TriAngleWidth) + 1; ++x) 
     { 
      p.X = x * TriAngleWidth; 
      p.Y = y * TriAngleWidth; 

      yx = x ; yz = y; 
      if(yx < 0) 
       yx = 0; 
      if (yx > HeightMap.GetUpperBound(0)) 
       yx = HeightMap.GetUpperBound(0); 
      if (yz < 0) 
       yz = 0; 
      if (yz > HeightMap.GetUpperBound(1)) 
       yz = HeightMap.GetUpperBound(1); 
      height = HeightMap[yx, yz]; 
      t.X = (p.X - center.X)/(2 * rad) + 0.5; 
      t.Y = (p.Y - center.Y)/(2 * rad) + 0.5; 
      Vertex[curN] = new VertexPosTexColor.Struct() {Position = new Vector3(p.X, hoehe, p.Y), TexCoord = t, Color = New Color4(1, 1, 1, 1)}; curN += 1; 
     } 
    } 
    var data = D3DContext.MapSubresource(VB, MapMode.WriteDiscard, MapFlags.None); 
    data.Data.WriteRange(Vertex); 
    D3DContext.UnmapSubresource(VB, 0); 
} 
+0

Okay, I J'ai cherché comment réaliser une telle technique, mais je n'ai pas trouvé de bonne ressource. Pourriez-vous suggérer un moyen de mettre en œuvre ceci ou une ressource à cette fin? Merci – Dangerbunny

+0

Il est vraiment difficile de trouver des informations à ce sujet. J'ai ajouté des explications pour le cas où vous utilisez une grille régulière. J'espère que cela aide. –

+0

Je suis avec vous aussi loin que la création d'un VertexBuffer, que je suppose est rempli avec le Vector3 de chaque sommet sur toute la hauteur rightmap?Après que tu m'as perdu X) Comment accéder aux coordonnées de texture du modèle? Je suis désolé, je suis un débutant dans le domaine de la 3D. Aussi je ne connais pas VB, je travaille avec C# atm. Merci pour votre patience, vous avez déjà été d'une grande aide – Dangerbunny

Questions connexes