Подтвердить что ты не робот

При преобразовании текстур (нарисованных как плоские 3D-объекты) для имитации глубины черные линии появляются случайным образом

Мы разрабатываем RPG сверху вниз, используя XNA. Недавно мы столкнулись с неудачей при написании кода для отображения наших карт. При рисовании карты сверху вниз с нормальной матрицей преобразования все кажется прекрасным. При использовании матрицы без плоской трансформации, такой как сжимая верхнюю или нижнюю сторону до имитирующей глубины, появляются черные линии (строки, когда сверху или снизу, столбец, когда слева или справа сжимаются), которые перемещаются, когда камера меняет положение. Движение и размещение кажутся случайными. (Изображение представлено ниже.)

Фоновая информация

Карты состоят из плиток. У исходной текстуры есть плитки, состоящие из 32x32 пикселей. Мы рисуем плитки, создавая 2 треугольника и отображая часть исходной текстуры на этих треугольниках. Шейдер делает это для нас. Существует три слоя треугольников. Сначала мы рисуем все непрозрачные плитки и все непрозрачные пиксели всех полупрозрачных и частично прозрачных плит, затем все полупрозрачные и частично прозрачные плитки и пиксели. Это прекрасно работает (но когда мы увеличиваем коэффициент с плавающей запятой, иногда цветные линии находятся между рядами строк и/или столбцами).

Renderstates

Мы используем один и тот же элемент растеризатора для всех фрагментов, и мы переключаемся между двумя при рисовании сплошных или полупрозрачных плит.

_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;

В тени мы устанавливаем SpriteBlendMode следующим образом:

Первый сплошной слой 1 использует

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

Все остальные сплошные и прозрачные слои (рисуются позже) используют

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

Другие шейдеры также используют это. Используемый SpriteBatch для SpriteFonts использует настройку по умолчанию.

Сгенерированная текстура

Некоторые фрагменты создаются "на лету" и сохраняются в файле. Файл загружается при загрузке карты. Это делается с помощью RenderTarget, созданного следующим образом:

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

При сгенерировании файл сохраняется и загружается (поэтому мы не теряем его, когда устройство сбрасывается, поскольку оно больше не будет на RenderTarget). Я попытался использовать mipmapping, но это спрайт. Нет информации о том, где размещены плитки, поэтому mipmapping бесполезен и не решает проблему.

Вершины

Мы перебираем каждую позицию. Здесь нет плавающих точек, но позиция - это 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

Чтобы расположить плитки, используется следующий код:

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

Как вы знаете, поплавки 32 бит, с точностью до 24 бит. Максимальное значение бита z составляет 8 бит (5 = 00000101). Максимальные значения для X и Y равны 16 бит соответственно. 24 бит. Я предположил, что ничто не может пойти не так, как с точки зрения плавающих точек.

this.Position = tilePosition;

Когда вершины установлены, он делает это следующим образом (так что все они имеют одну и ту же позицию плитки)

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

Шейдер может рисовать анимированные плитки и статические плитки. Оба используют следующее состояние сэмплера:

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

Шейдер не устанавливает никаких разных состояний сэмплера, мы также не видим в нашем коде.

Каждый проход, мы зажимаем альфа-значение (поэтому мы не получаем черные пиксели), используя следующую строку

clip(color.a - alpha)

Альфа 1 для сплошного слоя 1 и почти 0 для любого другого слоя. Это означает, что если есть доля альфы, она будет нарисована, если только на нижнем слое (потому что мы не знали, что с ним делать).

Камера

Мы используем камеру для имитации поиска сверху вниз на плитки, заставляя их казаться плоскими, используя значение z, чтобы наложить их на внешние слоистые данные (3 слоя не всегда в правильном порядке). Это также отлично работает. Камера обновляет матрицу преобразования. Если вам интересно, почему у этого есть какая-то странная структура, такая как this.AddChange - код Double Buffered (это также работает). Матрица преобразования формируется следующим образом:

// 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; }); 

Рассуждение и поток

Есть 3 слоя данных плитки. Каждая плитка определяется IsSemiTransparent. Когда плитка IsSemiTransparent, ее нужно нарисовать после чего-то не IsSemiTransparent. Данные плитки складываются при загрузке в экземпляр SplattedTile. Таким образом, даже если слой один из данных плитки пуст, первый слой из SplattedTile будет иметь данные о плитки в первом слое (при условии, что по крайней мере один слой имеет данные плитки). Причина в том, что Z-buffer не знает, с чем смешаться, если они нарисованы по порядку, так как за ним не должно быть твердых пикселей.

У слоев нет значения z, данные отдельной плитки. Когда это плитка земли, она имеет Priority = 0. Итак, плитки с тем же Priority заказываем на слое (порядок рисования) и непрозрачность (полупрозрачный, после непрозрачности). Плитки с различным приоритетом будут нарисованы в соответствии с их приоритетом.

Первый сплошной слой не имеет целевых пикселей, поэтому я устанавливаю его на DestinationBlend.Zero. Ему также не нужно AlphaBlending, так как нет ничего, чтобы alphablend с. Остальные слои (5, 2 сплошных, 3 прозрачных) могут быть нарисованы, когда уже есть данные о цвете, и их необходимо соответствующим образом смешать.

Перед повторением через 6 проходов устанавливается projection matrix. При использовании конуса это работает. При использовании конуса это не так.

Проблема

Мы хотим подражать некоторой глубине, применяя конусность, используя некоторую матрицу. Мы попробовали несколько значений, но это пример:

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

Экран (все с высотой 0, все плоские вещи) будут сжаты. Чем ниже y (выше на экране), тем больше он сжимается. Это действительно работает, но теперь случайные черные линии появляются почти везде. Кажется, это исключает несколько фрагментов, но я не вижу, что такое корреляция. Мы думаем, что это могло бы иметь какое-то отношение к интерполяции или мипмапсам.

И вот образ, чтобы показать вам, о чем я говорю: Screenshot with lines.

Плитки, не затронутые, по-видимому, являются статическими плитами НЕ на нижнем слое. Однако прозрачные плитки поверх них показывают другие графические артефакты. Они пропускают строки (так что строки просто удаляются). Я отметил этот текст, потому что я думаю, что это намек на то, что происходит. Строки вертикальные появляются, если я помещаю mip mag and minfilter в Linear.

Вот изображение, увеличенное (в увеличении игры), показывающее артефакт на плитки на слое 2 или 3 Screenshot with lines, zoomed in

Мы уже пробовали

  • mipfilter on Point или Linear
  • Настройка GenerateMipMaps на исходные текстуры
  • Настройка GenerateMipMaps для сгенерированных текстур (конструктор истинного флага RenderTarget)
  • Включение mipmapping (только уменьшилось количество артефактов, поскольку я был mipmapping спрайт.
  • Не рисовать слой 2 и 3 (это фактически делает ВСЕ плитки черными линиями)
  • DepthBufferEnable = false
  • Установка всех твердых слоев на SrcBlend = One; DestBlend = Zero;
  • Установка всех твердых слоев на ScrBlend = SrcAlpha; DestBlend = InvSrcAlpha;
  • Не рисовать прозрачный слой (линии все еще существуют).
  • Удаление clip(opacity) в shader. Это удаляет только некоторые строки. Мы изучаем это дальше.
  • Поиск той же проблемы в msdn, stackoverflow и использовании google (без везения).

Кто-нибудь знает эту проблему? Наконец, мы вызываем SpriteBatch ПОСЛЕ рисования плит и используем другой shader для аватаров (не показывайте проблем, потому что они имеют высоту > 0). Это отменит наш sampler state? Или...?

4b9b3361

Ответ 1

Проблема связана с числовыми типами в HLSL.

Сначала очистите этот шейдер. Я сделаю это, потому что именно так мы нашли реальную проблему. Ниже представлен унифицированный diff для SplattedTileShader.fx до того, как было добавлено 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);
    }
 }

`

Как вы видите, есть новая переменная ввода, называемая layer (type = uint). И теперь есть одна функция PixelLayer вместо трех.

Далее приведен унифицированный diff для 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)
         );

Да, мы изменили типы!

И теперь проблемы раскрываются. Похоже, что из-за способа обработки ввода поплавки никогда не будут точно такими же, как целочисленное значение. Обоснование этого выходит за рамки этой темы, но, возможно, я должен создать на ней сообщество wiki.

Итак, что произошло?

Итак, мы использовали, чтобы отбросить значения, которые не были на слое (if input.TextureInfo[0] < layer -> discard). Внутри input.TextInfo [layer] есть поплавок. Теперь мы сравниваем этот float с нашим значением уровня uint. И здесь происходит волшебство. Некоторые пиксели будут точно соответствовать (или, может быть, чуть выше этого значения уровня), и это было бы хорошо, по коду, если бы тип был (u) int, но это не так.

Итак, как это исправить? Хорошо, с этим на полпути, вероятно, есть правило. Скользящий код отображает пиксель на плитке, если он находится на полпути. Мы делаем то же самое со слоями.

Вот исправление (унифицированный diff) для 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);

Теперь все типы верны. Код работает так, как должен, и проблема решена. Мне нечего было делать с кодом discard(...), на который я на самом деле указал.

Спасибо всем, кто участвовал в нашем решении.

Ответ 2

Посмотрите на скалу дна этого последнего изображения - через нее проходят песчаные линии. Предположительно, вы сначала рисуете песок, затем скалу сверху.

Это говорит мне, что это не "черные линии, которые рисуются" через текстуры, а части текстуры не рисуются. Поскольку это происходит, когда вы растягиваетесь вертикально, это почти наверняка означает, что вы создаете сопоставление от старых пикселей к новым пикселям без интерполяции значений между ними в новой текстуре.

Например, используя отображение (x,y) --> (x, 2y), точки будут отображаться как (0,0) --> (0,0), (0,1) --> (0,2) и (0,2) --> (0, 4). Обратите внимание: никакие точки в исходной текстуре не привязаны к (0,1) или (0,3). Это приведет к просачиванию фона. Держу пари, если вы измените его, чтобы растянуться горизонтально, вы увидите вертикальные линии.

Что вам нужно сделать, так это сопоставить другой путь: если каждый пиксель в целевой текстуре, найдите его значение в исходном изображении, используя обратное преобразование. Вероятно, вы получите дробные значения пиксельных координат, поэтому вам нужно будет интерполировать значения.

Я вообще не знаком с XNA, но, вероятно, есть более удобный способ сделать это, чем вручную.

Ответ 3

Я не могу понять, почему появляются черные линии, но я могу дать вам другой способ визуализации ландшафта, который может привести к тому, что он будет выглядеть корректно (и, надеюсь, даст вам немного ускорения скорости).

Спрайты

Для этого вам понадобится текстурный атлас (лист спрайтов a.k.a.). Вы можете разделить свои алты на несколько атласов и использовать мультитекстурирование.

Буфер вершин

Что бы я сделал, это поцарапать SpriteBatch, вы всегда знаете, где будут ваши спрайты - создайте VertexBuffer при запуске (возможно, один на каждый слой) и используйте его для рисования слоев. Что-то вроде этого (это 2D-буфер, он просто "смотрит" на 3d как ваш):

Vertex Buffer Sample

Определение вершины, вероятно, будет состоять из:

  • Позиция (Vector2)
  • Координата текстуры (Vector2)
  • Цвет (Vector4/Color)

Каждый раз, когда пейзаж должен быть "циклическим" (подробнее об этом позже), вы пройдете через карту под камерой и обновите координаты текстуры и/или цвет в VertexBuffer. Не обновляйте буфер каждый кадр. Я бы не послал координаты текстуры в графический процессор в диапазоне [0, 1], а [0, Number of Sprites] - вычислил [0, 1] в вашем вершинном шейдере.

Важно: не разделяйте вершины (т.е. используйте IndexBuffer), потому что вершины, разделяемые более чем двумя гранатами, должны оставаться различными (они имеют разные координаты текстур) - создайте буфер, как будто IndexBuffer не существовало. Использование IndexBuffer является расточительным в этом сценарии, поэтому просто придерживайтесь только VertexBuffer.

Rendering

Используемая вами матрица мира отобразит [0, 1] в размер экрана плюс размер плитки (т.е. простой масштаб x = Viewport.Width + 32 и y = Viewport.Height + 32). Ваша матрица проекции будет тождественной матрицей.

Матрица представления сложна. Представьте себе, что ваша карта просматривает текущий фрагмент фрагментов (который есть) в {0,0}, вам нужно выяснить смещение (в пикселях) от того, на котором смотрит ваша камера. По существу, это будет смещенная матрица с x = Camera.X - LeftTile.X * (Viewport.Width / NumTiles.X) и аналогичная для y.

Матрицы являются единственным сложным битом, после того как вы создали их простым вызовом DrawUserPrimitives(), и все готово.

Обратите внимание, что это касается только вашего ландшафта, рисуйте другие спрайты, как сегодня.

Пейзаж Велоспорт

Когда положение вашей камеры меняется, вам в основном нужно определить, смотрит ли он на новый блок плиток и соответствующим образом обновляет VertexBuffer (координаты и цвет текстуры - оставляйте позицию в покое, нет необходимости пересчитывать ее).

В качестве альтернативы

Другой вариант - сделать каждый слой RenderTarget2D и использовать ваше текущее преобразование один раз для всего слоя. Это либо решит вашу проблему, либо сделает реальную причину очень показательной.

Боковое примечание: я бы предоставил образец кода, если бы здесь не было 00h40, этот вопрос заслуживает этого. Я посмотрю, сколько времени у меня будет завтра.

Ответ 4

С тем, что вы нам дали, я был бы чрезвычайно подозрительным к вашему слою кода. Это действительно выглядит как нижний слой иногда, пробивающий слои, которые должны быть сверху, и скрывает их, в зависимости от округления с плавающей запятой. Полосы, перпендикулярные углу обзора, являются очень распространенным эффектом, когда у вас есть два треугольника, которые должны быть точно копланарными, но по какой-либо причине не имеют точно таких же координат вершин (например, один больше другого). Что произойдет, если вы нарисуете разные слои очень маленьким количеством отдельно друг от друга? Например, нарисуйте нижний сплошной слой на -0.00002, а следующий на -0.00001, а верхний слой на 0 точно (при условии, что все три будут нарисованы в 0 сейчас).

Я не знаю конкретно о XNA, но проблема расслоения всегда является фундаментальной проблемой использования плавающей точки для представления геометрии, и я был бы удивлен, если XNA "волшебным образом" избежит ее для вас. Не знаете, почему некоторые из плиток хороши, но большинство из них завинчиваются. Вероятно, эти плитки просто повезло или что-то в этом роде. Проблемы, вызванные ошибкой с плавающей запятой, часто очень странны.

Если разделение слоев немного не помогает, то вы в значительной степени сводятся к стандартной отладке на основе комментариев; попробуйте его без спрайтов, без анимированных плит, без прозрачного слоя, и c. Когда это прекратится, все, что вы только что прокомментировали, нарушает его: P

Ответ 5

Проблема заключается в вашем шейдере SplattedTileShader.fx.

Черные строки появляются, потому что отброшенные пиксели.

сложно отслеживать ваш шейдерный код без дополнительной информации, но проблема там.

Я думаю, что то, как вы делаете мультитекстурию, слишком сложно.

возможно, будет проще сделать шейдер, поддерживающий мультитекстуры за один проход.

в вашей вершине вы можете передать четыре значения веса для каждой текстуры и сделать микс самостоятельно в шейдере.

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