При преобразовании текстур (нарисованных как плоские трехмерные объекты) в имитирующую глубину черные линии появляются случайным образом - PullRequest
50 голосов
/ 10 мая 2011

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

Справочная информация

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

Renderstates

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

_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, но это спрайт-лист. Информация о том, где размещены плитки, отсутствует, поэтому мипмаппирование бесполезно и не решило проблему.

Вершина

Мы перебираем все позиции. Здесь пока нет плавающих точек, но позиция - это 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 слоя не всегда находятся в правильном порядке). Это тоже отлично работает. Камера обновляет матрицу преобразования. Если вам интересно, почему у него такая странная структура, как эта. AddChange - код с двойной буферизацией (это также работает). Матрица преобразования формируется следующим образом:

// 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, так как с ним ничего не поделать.Другие слои (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, все плоские вещи) будет сжат.Чем ниже у (выше на экране), тем больше он сжимается.Это на самом деле работает, но теперь случайные черные линии появляются почти везде.Кажется, это исключает несколько плиток, но я не вижу, в чем корреляция.Мы думаем, что это может иметь отношение к интерполяции или мип-картам.

А вот изображение, показывающее, о чем я говорю: Screenshot with lines.

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

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

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

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

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

Ответы [ 5 ]

6 голосов
/ 10 мая 2011

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

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

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

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

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

5 голосов
/ 31 октября 2011

Проблема связана с числовыми типами в 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)
         );

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

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

Итак, что происходило?

Хорошо, поэтому мы использовали для отбрасывания значений, которых не было на слое (if input.TextureInfo[0] < layer -> discard). Внутри input.TextInfo [layer] находится число с плавающей точкой. Теперь мы сравним это значение с нашим значением слоя 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(...), на который я изначально указывал.

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

3 голосов
/ 22 сентября 2011

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

Спрайты

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

Vertex Buffer

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

Vertex Buffer Sample

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

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

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

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

Рендеринг

Используемая вами матрица мира отобразит [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, этот вопрос заслуживает этого.Я посмотрю, сколько у меня будет завтрашнего вечера.

2 голосов
/ 10 мая 2011

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

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

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

0 голосов
/ 22 сентября 2011

Проблема в вашем шейдере SplattedTileShader.fx.

Черные линии появляются из-за сброшенных пикселей.

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

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

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

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

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

...