51 votes

Lors de la transformation de textures (dessinées comme des objets 3D plats) pour imiter la profondeur, des lignes noires apparaissent de manière aléatoire.

Nous développons un RPG de haut en bas en utilisant XNA. Récemment, nous avons rencontré un problème lors de l'écriture du code d'affichage de nos cartes. Lorsque l'on dessine la carte en vue descendante avec une matrice de transformation normale, tout semble aller bien. Lorsque l'on utilise une matrice de transformation non plate, par exemple en comprimant le haut ou le bas pour imiter la profondeur, des lignes noires (lignes lorsque le haut ou le bas, colonne lorsque la gauche ou la droite est comprimée) qui se déplacent lorsque la caméra change de position, apparaissent. Le mouvement et le placement semblent être aléatoires. (Image fournie plus bas.)

Informations générales

Les cartes sont constituées de tuiles. La texture originale comporte des tuiles de 32x32 pixels. Nous dessinons les tuiles en créant 2 triangles et en affichant une partie de la texture originale sur ces triangles. Un shader fait cela pour nous. Il y a trois couches de triangles. D'abord, nous dessinons toutes les tuiles opaques et tous les pixels opaques de toutes les tuiles semi-opaques et partiellement transparentes, puis toutes les tuiles et pixels semi-opaques et partiellement transparents. Cela fonctionne bien (mais lorsque nous zoomons par un facteur à virgule flottante, il arrive que des lignes de couleur se trouvent entre les rangées et/ou les colonnes de tuiles).

États de rendu

Nous utilisons le même rasterizerState pour toutes les tuiles et nous passons de l'un à l'autre lorsque nous dessinons des tuiles solides ou semi-transparentes.

_rasterizerState = new RasterizerState();
_rasterizerState.CullMode = CullMode.CullCounterClockwiseFace;

_solidDepthState = new DepthStencilState();
_solidDepthState.DepthBufferEnable = true;
_solidDepthState.DepthBufferWriteEnable = true;

_alphaDepthState = new DepthStencilState();
_alphaDepthState.DepthBufferEnable = true;
_alphaDepthState.DepthBufferWriteEnable = false;

Dans l'ombre, nous définissons le SpriteBlendMode comme suit :

La première couche solide 1 utilise

AlphaBlendEnable = False; 
SrcBlend = One; 
DestBlend = Zero; 

Tous les autres calques solides et transparents (dessinés plus tard) utilisent la fonction

AlphaBlendEnable = True; 
SrcBlend = SrcAlpha;
DestBlend = InvSrcAlpha; 

D'autres shaders l'utilisent également. Le site SpriteBatch pour le SpriteFonts utilisé, utilise le paramètre par défaut.

Texture générée

Certaines tuiles sont générées à la volée et enregistrées dans un fichier. Le fichier est chargé lorsque la carte est chargée. Cela se fait à l'aide d'un RenderTarget créé comme suit :

RenderTarget2D rt = new RenderTarget2D(sb.GraphicsDevice, 768, 1792, false, 
    SurfaceFormat.Color, DepthFormat.None);
    sb.GraphicsDevice.SetRenderTarget(rt);

Une fois généré, le fichier est sauvegardé et chargé (afin de ne pas le perdre lors de la réinitialisation de l'appareil, car il ne sera plus sur un support de type RenderTarget ). J'ai essayé d'utiliser le mipmapping, mais il s'agit d'un spritesheet. Il n'y a aucune information sur l'emplacement des tuiles, donc le mipmapping est inutile et n'a pas résolu le problème.

Sommets

Nous passons en revue toutes les positions. Pas encore de points flottants ici, mais la position est un Vector3 (Float3).

for (UInt16 x = 0; x < _width;  x++)
{
    for (UInt16 y = 0; y < _heigth; y++)
    {
        [...]
        position.z = priority; // this is a byte 0-5

Pour positionner les tuiles, le code suivant est utilisé :

tilePosition.X = position.X;
tilePosition.Y = position.Y + position.Z;
tilePosition.Z = position.Z;

Comme vous le savez, les flottants sont de 32 bits, avec 24 bits pour la précision. La valeur maximale de z est de 8 bits (5 = 00000101). Les valeurs maximales pour X et Y sont de 16 bits resp. 24 bits. J'ai supposé que rien ne pouvait se passer en termes de points flottants.

this.Position = tilePosition;

Lorsque les sommets sont définis, il le fait de la manière suivante (afin qu'ils partagent tous la même position de tuile)

Vector3[] offsets  = new Vector3[] { Vector3.Zero, Vector3.Right, 
    Vector3.Right + (this.IsVertical ? Vector3.Forward : Vector3.Up), 
    (this.IsVertical ? Vector3.Forward : Vector3.Up) };
Vector2[] texOffset = new Vector2[] { Vector2.Zero, Vector2.UnitX, 
    Vector2.One, Vector2.UnitY };

for (int i = 0; i < 4; i++)
{
    SetVertex(out arr[start + i]);
    arr[start + i].vertexPosition = Position + offsets[i];

    if (this.Tiles[0] != null)
        arr[start + i].texturePos1 += texOffset[i] * this.Tiles[0].TextureWidth;
    if (this.Tiles[1] != null)
        arr[start + i].texturePos2 += texOffset[i] * this.Tiles[1].TextureWidth;
    if (this.Tiles[2] != null)
        arr[start + i].texturePos3 += texOffset[i] * this.Tiles[2].TextureWidth;
}

Shader

Le shader peut dessiner des tuiles animées et des tuiles statiques. Les deux utilisent l'état d'échantillonnage suivant :

sampler2D staticTilesSampler = sampler_state { 
    texture = <staticTiles> ; magfilter = POINT; minfilter = POINT; 
    mipfilter = POINT; AddressU = clamp; AddressV = clamp;};

Le shader ne définit pas d'états d'échantillonnage différents, ce que nous ne faisons pas non plus dans notre code.

A chaque passage, nous coupons à la valeur alpha (pour ne pas avoir de pixels noirs) en utilisant la ligne suivante

clip(color.a - alpha)

Alpha est égal à 1 pour la couche solide 1, et presque 0 pour toute autre couche. Cela signifie que s'il y a une fraction d'alpha, elle sera dessinée, sauf sur la couche inférieure (car nous ne saurions pas quoi en faire).

Appareil photo

Nous utilisons une caméra pour simuler une observation de haut en bas des tuiles, les faisant apparaître plates, en utilisant la valeur z pour les superposer par des données de superposition externes (les 3 couches ne sont pas toujours dans le bon ordre). Cela fonctionne également très bien. La caméra met à jour la matrice de transformation. Si vous vous demandez pourquoi elle a une structure bizarre comme ceci.AddChange - le code est en double tampon (cela fonctionne aussi). La matrice de transformation est formée comme suit :

// First get the position we will be looking at. Zoom is normally 32
Single x = (Single)Math.Round((newPosition.X + newShakeOffset.X) * 
    this.Zoom) / this.Zoom;
Single y = (Single)Math.Round((newPosition.Y + newShakeOffset.Y) * 
    this.Zoom) / this.Zoom;

// Translation
Matrix translation = Matrix.CreateTranslation(-x, -y, 0);

// Projection
Matrix obliqueProjection = new Matrix(1, 0, 0, 0,
                                      0, 1, 1, 0,
                                      0, -1, 0, 0,
                                      0, 0, 0, 1);

Matrix taper = Matrix.Identity; 

// Base it of center screen
Matrix orthographic = Matrix.CreateOrthographicOffCenter(
    -_resolution.X / this.Zoom / 2, 
     _resolution.X / this.Zoom / 2, 
     _resolution.Y / this.Zoom / 2, 
    -_resolution.Y / this.Zoom / 2, 
    -10000, 10000);

// Shake rotation. This works fine       
Matrix shakeRotation = Matrix.CreateRotationZ(
    newShakeOffset.Z > 0.01 ? newShakeOffset.Z / 20 : 0);

// Projection is used in Draw/Render
this.AddChange(() => { 
    this.Projection = translation * obliqueProjection * 
    orthographic * taper * shakeRotation; }); 

Raisonnement et flux

Il existe 3 couches de données sur les tuiles. Chaque tuile est définie par IsSemiTransparent . Lorsqu'une tuile est IsSemiTransparent il doit être dessiné après quelque chose qui n'est pas IsSemiTransparent . Les données de la tuile sont empilées lorsqu'elles sont chargées sur une SplattedTile instance. Ainsi, même si la couche 1 des données de la tuile est vide, la couche 1 de la SplattedTile aura des données de tuiles dans la première couche, (étant donné qu'au moins une couche a des données de tuiles). La raison en est que le Z-buffer ne sait pas avec quoi se mélanger s'ils sont dessinés dans l'ordre, car il se peut qu'il n'y ait pas de pixels solides derrière lui.

Les couches n'ont PAS de valeur z, les données individuelles des tuiles en ont une. Lorsqu'il s'agit d'une tuile de sol, elle a une valeur z. Priority = 0 . Ainsi, les tuiles avec le même Priority nous sommes classés par couche (ordre de dessin) et par opacité (semi-transparent, après opaque). Les tuiles ayant une priorité différente seront dessinées en fonction de leur priorité.

Le premier calque solide n'a pas de pixels de destination, donc je l'ai réglé sur DestinationBlend.Zero . Il n'a pas non plus besoin AlphaBlending puisqu'il n'y a rien à mélanger. Les autres couches (5, 2 solides, 3 transparentes) peuvent être dessinées lorsqu'il y a déjà des données de couleur et qu'il faut les mélanger en conséquence.

Avant d'itérer à travers les 6 passages, la fonction projection matrix est réglé. Lorsque vous n'utilisez pas de cône, cela fonctionne. En utilisant un cône, ça ne fonctionne pas.

Le problème

Nous voulons imiter un peu plus de profondeur en appliquant le taper, en utilisant la matrice some. Nous avons essayé plusieurs valeurs mais ceci est un exemple :

new Matrix(1, 0, 0, 0,
           0, 1, 0, 0.1f,
           0, 0, 1, 0,
           0, 0, 0, 1);

L'écran (tout ce qui a une hauteur de 0, tout ce qui est plat) sera comprimé. Plus le y est bas (plus haut sur l'écran), plus il est écrasé. Cela fonctionne, mais maintenant des lignes noires aléatoires apparaissent presque partout. Cela semble exclure quelques tuiles, mais je ne vois pas quelle est la corrélation. Nous pensons que cela pourrait avoir quelque chose à voir avec l'interpolation ou les mipmaps.

Et voici une image pour vous montrer ce dont je parle : Screenshot with lines .

Les tuiles non affectées semblent être des tuiles statiques qui ne sont PAS sur la couche inférieure. Cependant, les tuiles transparentes qui se superposent à celles-ci présentent d'autres artefacts graphiques. Il leur manque des lignes (les rangées sont donc simplement supprimées). J'ai marqué ce texte parce que je pense qu'il est un indice de ce qui se passe. Le site vertical apparaissent si je mets le mip mag and minfilter a Linear .

Voici une image agrandie (zoom dans le jeu), montrant l'artefact sur les tuiles de la couche 2 ou 3. Screenshot with lines, zoomed in

Nous avons déjà essayé

  • mipfilter en Point o Linear
  • Réglage de GenerateMipMaps sur les textures originales
  • Réglage de GenerateMipMaps sur les textures générées (constructeur true flag de RenderTarget )
  • L'activation du mipmapping (qui n'a donné que plus d'artefacts lors du zoom arrière, parce que j'ai été mipmapping d'un spritesheet .
  • Ne pas dessiner les couches 2 et 3 (ce qui fait que TOUTES les tuiles ont des lignes noires)
  • DepthBufferEnable = false
  • Régler toutes les couches solides sur SrcBlend = One; DestBlend = Zero;
  • Régler toutes les couches solides sur ScrBlend = SrcAlpha; DestBlend = InvSrcAlpha;
  • Ne pas dessiner la couche transparente (les lignes sont toujours là).
  • Suppression de clip(opacity) dans le shader . Cela ne supprime que certaines lignes. Nous poursuivons nos recherches dans ce domaine.
  • Je recherche le même problème sur msdn, stackoverflow et en utilisant google (sans succès).

Quelqu'un reconnaît-il ce problème ? Pour finir, nous appelons le SpriteBatch APRÈS avoir dessiné les tuiles, et utiliser un autre Shader pour les avatars (ne posent aucun problème, car ils ont une hauteur > 0). Cela annule-t-il notre sampler state ? Ou... ?

6voto

Regardez la roche du bas de cette dernière image - elle est traversée par des lignes de couleur sable. On peut supposer que vous dessinez d'abord le sable, puis le rocher par-dessus.

Cela me dit que ce ne sont pas des "lignes noires qui sont dessinées" à travers les textures, mais que certaines parties des textures ne sont pas dessinées. Comme cela se produit lorsque vous étirez verticalement, cela signifie presque certainement que vous créez un mappage des anciens pixels vers les nouveaux, sans interpoler les valeurs entre les deux dans la nouvelle texture.

Par exemple, en utilisant le mappage (x,y) --> (x, 2y) les points seront cartographiés comme (0,0) --> (0,0) , (0,1) --> (0,2) y (0,2) --> (0, 4) . Remarquez qu'aucun point de la carte de texture source à (0,1) o (0,3) . Ainsi, l'arrière-plan s'infiltrerait. Je parie que si vous le changez pour qu'il s'étende à l'horizon, vous verrez des lignes verticales.

Ce qu'il faudrait faire, c'est une mise en correspondance dans l'autre sens : étant donné chaque pixel de la texture cible, trouvez sa valeur dans l'image source en utilisant l'inverse de la transformation ci-dessus. Vous obtiendrez probablement des valeurs fractionnaires dans les coordonnées des pixels, vous voudrez donc interpoler les valeurs.

Je ne suis pas du tout familier avec XNA, mais il existe probablement un moyen plus pratique de faire cela qu'à la main.

5voto

Derk-Jan Points 400

Le problème est lié aux types numériques en HLSL.

D'abord, nettoyons ce shader. Je vais le faire, car c'est ainsi que nous avons trouvé le véritable problème. Voici un diff unifié pour SplattedTileShader.fx avant la mise en place du taperfix :

@@ -37,76 +37,31 @@
    data.Position = mul(worldPosition, viewProjection);

-   
     return data;
 }

-float4 PixelLayer1(VertexShaderOutput input, uniform float alpha) : COLOR0
+float4 PixelLayer(VertexShaderOutput input, uniform uint layer, uniform float alpha) : COLOR0
 {
-   if(input.TextureInfo[0] < 1)
+   if(input.TextureInfo[0] < layer)
        discard;

-    float4 color;
+   float4 color;
+   float2 coord;
+   if(layer == 1)
+       coord = input.TexCoord1;
+   else if(layer == 2)
+       coord = input.TexCoord2;
+   else if(layer == 3)
+       coord = input.TexCoord3;

-   switch (input.TextureInfo[1])
+   switch (input.TextureInfo[layer])
    {
        case 0:
-           color = tex2D(staticTilesSampler, input.TexCoord1);
+           color = tex2D(staticTilesSampler, coord);
            break;
        case 1:
-           color = tex2D(autoTilesSampler, input.TexCoord1);
+           color = tex2D(autoTilesSampler, coord);
            break;
        case 2:
-           color = tex2D(autoTilesSampler, input.TexCoord1 + float2(frame, 0) * animOffset) * (1 - frameBlend) + tex2D(autoTilesSampler, input.TexCoord1 + float2(nextframe, 0) * animOffset) * frameBlend;
-           break;
-   }
-
-   clip(color.a - alpha);
-
-   return color;
-}
-
-float4 PixelLayer2(VertexShaderOutput input, uniform float alpha) : COLOR0
-{
-   if(input.TextureInfo[0] < 2)
-       discard;
-
-    float4 color;
-
-   switch (input.TextureInfo[2])
-   {
-       case 0:
-           color = tex2D(staticTilesSampler, input.TexCoord2);
-           break;
-       case 1:
-           color = tex2D(autoTilesSampler, input.TexCoord2);
-           break;
-       case 2: 
-           color = tex2D(autoTilesSampler, input.TexCoord2 + float2(frame, 0) * animOffset) * (1 - frameBlend) + tex2D(autoTilesSampler, input.TexCoord2 + float2(nextframe, 0) * animOffset) * frameBlend;
-           break;
-   }
-
-   clip(color.a - alpha);
-
-   return color;
-}
-
-float4 PixelLayer3(VertexShaderOutput input, uniform float alpha) : COLOR0
-{
-   if(input.TextureInfo[0] < 3)
-       discard;
-
-    float4 color;
-
-   switch (input.TextureInfo[3])
-   {
-       case 0:
-           color = tex2D(staticTilesSampler, input.TexCoord3);
-           break;
-       case 1:
-           color = tex2D(autoTilesSampler, input.TexCoord3);
-           //color = float4(0,1,0,1);
-           break;
-       case 2:
-           color = tex2D(autoTilesSampler, input.TexCoord3 + float2(frame, 0) * animOffset) * (1 - frameBlend) + tex2D(autoTilesSampler, input.TexCoord3 + float2(nextframe, 0) * animOffset) * frameBlend;
+           color = tex2D(autoTilesSampler, coord + float2(frame, 0) * animOffset) * (1 - frameBlend) + tex2D(autoTilesSampler, coord + float2(nextframe, 0) * animOffset) * frameBlend;
            break;
    }
@@ -125,5 +80,5 @@
        DestBlend = Zero;
         VertexShader = compile vs_3_0 VertexShaderFunction();
-        PixelShader = compile ps_3_0 PixelLayer1(1);
+        PixelShader = compile ps_3_0 PixelLayer(1,1);
     }

@@ -134,5 +89,5 @@
        DestBlend = InvSrcAlpha;
        VertexShader = compile vs_3_0 VertexShaderFunction();
-        PixelShader = compile ps_3_0 PixelLayer2(0.00001);
+        PixelShader = compile ps_3_0 PixelLayer(2,0.00001);
    }

@@ -143,5 +98,5 @@
        DestBlend = InvSrcAlpha;
        VertexShader = compile vs_3_0 VertexShaderFunction();
-        PixelShader = compile ps_3_0 PixelLayer3(0.00001);
+        PixelShader = compile ps_3_0 PixelLayer(3,0.00001);
    }
 }
@@ -155,5 +110,5 @@
        DestBlend = InvSrcAlpha;
         VertexShader = compile vs_3_0 VertexShaderFunction();
-        PixelShader = compile ps_3_0 PixelLayer1(0.000001);
+        PixelShader = compile ps_3_0 PixelLayer(1,0.000001);
     }

@@ -164,5 +119,5 @@
        DestBlend = InvSrcAlpha;
        VertexShader = compile vs_3_0 VertexShaderFunction();
-        PixelShader = compile ps_3_0 PixelLayer2(0.000001);
+        PixelShader = compile ps_3_0 PixelLayer(2,0.000001);
    }

@@ -173,5 +128,5 @@
        DestBlend = InvSrcAlpha;
        VertexShader = compile vs_3_0 VertexShaderFunction();
-        PixelShader = compile ps_3_0 PixelLayer3(0.00001);
+        PixelShader = compile ps_3_0 PixelLayer(3,0.00001);
    }
 }

`

Comme vous pouvez le voir, il y a une nouvelle variable d'entrée appelée couche (type = uint). Et il y a maintenant une fonction PixelLayer au lieu de trois.

Voici le diff unifié pour SplattedTileVertex.cs

@@ -11,5 +11,5 @@
     {
         internal Vector3 vertexPosition;
-        internal byte textures;
+        internal float textures;
         /// <summary>
         /// Texture 0 is static tiles
@@ -17,7 +17,7 @@
         /// Texture 2 is animated autotiles
         /// </summary>
-        internal byte texture1;
-        internal byte texture2;
-        internal byte texture3;
+        internal float texture1;
+        internal float texture2;
+        internal float texture3;
         internal Vector2 texturePos1;
         internal Vector2 texturePos2;
@@ -27,8 +27,8 @@
         (
             new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
-            new VertexElement(12, VertexElementFormat.Byte4, VertexElementUsage.PointSize, 0),
-            new VertexElement(16, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0),
-            new VertexElement(24, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 1),
-            new VertexElement(32, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 2)
+            new VertexElement(12, VertexElementFormat.Vector4, VertexElementUsage.PointSize, 0),
+            new VertexElement(28, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0),
+            new VertexElement(36, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 1),
+            new VertexElement(44, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 2)
         );

Oui, nous avons changé les types !

Et maintenant, les problèmes apparaissent au grand jour. Il semble qu'en raison de la manière dont l'entrée est traitée, les valeurs flottantes ne seront jamais exactement les mêmes que les valeurs entières. Le raisonnement derrière cela va au-delà de ce fil, mais peut-être que je devrais faire un wiki communautaire à ce sujet.

Alors, que se passait-il ?

Ok, donc nous avions l'habitude de rejeter les valeurs qui n'étaient pas sur la couche (if input.TextureInfo[0] < layer -> discard) . Dans input.TextInfo[layer] il y a un float. Maintenant, nous comparons ce flottant à notre valeur uint de la couche. Et c'est là que la magie opère. Certains pixels vont correspondre exactement (ou peut-être juste au-dessus de la valeur de la couche) et cela serait parfait, du point de vue du code, si le type était un (u)int, mais ce n'est pas le cas.

Alors comment le réparer ? Eh bien, suivez la règle de la moitié de ce qui existe probablement. Le code de déplacement rend un pixel sur une tuile, s'il est à mi-chemin. Nous faisons la même chose avec les couches.

Voici le correctif (diff unifié) pour SplattedTileShader.fx

@@ -42,28 +42,24 @@
 float4 PixelLayer(VertexShaderOutput input, uniform uint layer, uniform float alpha) : COLOR0
 {
-   if(input.TextureInfo[0] < layer)
+   if(input.TextureInfo[0] < (float)layer - 0.5)
        discard;

    float4 color;
    float2 coord;
-   if(layer == 1)
+   if(layer < 1.5)
        coord = input.TexCoord1;
-   else if(layer == 2)
+   else if(layer < 2.5)
        coord = input.TexCoord2;
-   else if(layer == 3)
+   else
        coord = input.TexCoord3;

-   switch (input.TextureInfo[layer])
-   {
-       case 0:
-           color = tex2D(staticTilesSampler, coord);
-           break;
-       case 1:
-           color = tex2D(autoTilesSampler, coord);
-           break;
-       case 2:
-           color = tex2D(autoTilesSampler, coord + float2(frame, 0) * animOffset) * (1 - frameBlend) + tex2D(autoTilesSampler, coord + float2(nextframe, 0) * animOffset) * frameBlend;
-           break;
-   }
+   float type = input.TextureInfo[layer];
+
+   if (type < 0.5)
+       color = tex2D(staticTilesSampler, coord);
+   else if (type < 1.5)
+       color = tex2D(autoTilesSampler, coord);
+   else
+       color = tex2D(autoTilesSampler, coord + float2(frame, 0) * animOffset) * (1 - frameBlend) + tex2D(autoTilesSampler, coord + float2(nextframe, 0) * animOffset) * frameBlend;

    clip(color.a - alpha);

Maintenant, tous les types sont corrects. Le code fonctionne comme il le devrait, et le problème est résolu. Il n'y avait rien à faire avec le discard(...) code, ce que j'avais initialement pointé du doigt.

Merci à tous ceux qui nous ont aidés à résoudre ce problème.

3voto

Jonathan Dickinson Points 4655

Je n'arrive pas à comprendre pourquoi les lignes noires apparaissent, mais je peux vous donner une autre façon de rendre le paysage qui pourrait lui donner un aspect correct (et, je l'espère, vous donner un petit gain de vitesse).

Sprites

Vous aurez besoin d'un atlas des textures (alias feuille de sprites) pour que cela fonctionne. Vous pourriez diviser vos altas en plusieurs atlas et utiliser le multi-texturage.

Tampon de sommets

Ce que je ferais, c'est de rayer le SpriteBatch, vous savez toujours où vos sprites vont être - créer un VertexBuffer au démarrage (éventuellement un par couche) et l'utiliser pour dessiner les couches. Quelque chose comme ceci ( Il s'agit d'un tampon en 2D, il a juste l'air d'être en 3D comme le vôtre. ):

Vertex Buffer Sample

La définition des sommets serait probablement constituée de :

  • Position ( Vector2 )
  • Coordonnées de la texture ( Vector2 )
  • Couleur ( Vector4/Color )

Chaque fois que le paysage doit être "cyclé" (nous y reviendrons plus tard), vous parcourez la carte sous la caméra et mettez à jour les coordonnées de la texture et/ou la couleur dans le fichier de configuration. VertexBuffer . Ne rafraîchissez pas le tampon à chaque image. Je n'enverrais pas les coordonnées de la texture au GPU dans le cadre d'un processus d'optimisation. [0, 1] plutôt [0, Number of Sprites] - calculer le [0, 1] dans votre vertex shader.

Important : Ne pas partager les sommets (c'est-à-dire utiliser un IndexBuffer ) parce que les sommets qui sont partagés par plus de deux faces doivent rester distincts (ils ont des coordonnées de texture distinctes) - construisez le tampon comme si IndexBuffer n'existaient pas. En utilisant un IndexBuffer est un gaspillage dans ce scénario, il suffit donc de s'en tenir à une VertexBuffer seul.

Rendu

La matrice mondiale que vous utilisez permettra de cartographier [0, 1] à la taille de l'écran plus la taille d'une tuile (c'est-à-dire une simple mise à l'échelle). x = Viewport.Width + 32 y y = Viewport.Height + 32 ). Votre matrice de projection serait une matrice d'identité.

La matrice de vue est délicate. Imaginez que votre carte regarde un bloc actuel de tuiles (ce qui est le cas) à l'endroit suivant {0,0} Pour cela, vous devez déterminer le décalage (en pixels) par rapport à l'endroit où se trouve votre caméra. Il s'agira donc essentiellement d'une matrice de décalage avec x = Camera.X - LeftTile.X * (Viewport.Width / NumTiles.X) et similaires pour y .

Les matrices sont le seul point délicat, une fois que vous les avez configurées, c'est un simple DrawUserPrimitives() appelez et vous avez terminé.

Notez que cela ne concerne que votre paysage, dessinez vos autres sprites comme vous le faites aujourd'hui.

Paysage cycliste

Lorsque la position de votre caméra change, vous devez déterminer si elle regarde un nouveau bloc de tuiles et mettre à jour l'indicateur de position de la caméra. VertexBuffer de manière appropriée (coordonnées de la texture et couleur - ne touchez pas à la position, il n'est pas nécessaire de la recalculer).

Ou bien

Une autre option consiste à rendre chaque couche à un fichier RenderTarget2D et utiliser votre transformation actuelle une fois sur l'ensemble de la couche. Cela résoudrait votre problème, ou rendrait la vraie raison très apparente.

Remarque : je fournirais des exemples de code s'il n'était pas 00h40 ici, cette question le mérite. Je verrai combien de temps j'ai demain soir.

2voto

Paul Z Points 594

Avec ce que vous nous avez donné, je serais extrêmement méfiant à l'égard de votre code de superposition. On dirait vraiment que la couche inférieure est parfois en passant à travers les couches qui sont censées être au-dessus et en les cachant, selon l'arrondi des points flottants. Les bandes perpendiculaires à l'angle de vue sont un effet très courant lorsque vous avez deux triangles qui sont censés être exactement coplanaires, mais qui, pour une raison quelconque, n'ont pas exactement les mêmes coordonnées de sommet (par exemple, l'un est plus grand que l'autre). Que se passe-t-il si vous dessinez les différentes couches à une distance très très faible les unes des autres ? Par exemple, dessinez la couche solide inférieure à -0.00002, la suivante à -0.00001, et la couche supérieure à 0 exactement (en supposant que les trois sont dessinées à 0 maintenant).

Je ne connais pas spécifiquement XNA, mais le problème de la superposition est toujours un problème fondamental de l'utilisation de la virgule flottante pour représenter la géométrie, et je serais surpris que XNA l'évite "magiquement" pour vous. Je ne sais pas pourquoi certaines tuiles sont correctes mais que la plupart sont ratées. Ces tuiles ont probablement eu de la chance, ou quelque chose comme ça. Les problèmes causés par une erreur de virgule flottante sont souvent très étranges.

Si le fait de séparer légèrement les couches n'aide pas, alors vous en êtes réduit à un débogage standard basé sur les commentaires ; essayez sans sprites, sans tuiles animées, sans couche transparente, &c. Lorsque cela ne se produit plus, c'est que ce que vous venez de commenter est en train de le casser :P

0voto

Blau Points 4601

Le problème se trouve dans votre shader SplattedTileShader.fx.

Les lignes noires apparaissent à cause des pixels rejetés.

Il est difficile de suivre le code de votre shader sans plus d'informations, mais le problème est là.

Je pense que la façon dont vous faites la multitexture est trop compliquée.

Il sera peut-être plus facile de faire un shader qui supporte les multitextures en une seule passe.

dans votre vertex, vous pouvez passer quatre valeurs de poids pour chaque texture et faire le mélange par vous-même dans le shader.

http://www.riemers.net/eng/Tutorials/XNA/Csharp/Series4/Multitexturing.php

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X