将纹理(绘制为平面3D对象)转换为模仿深度时,黑色线条会随机出现

时间:2011-05-09 21:07:58

标签: c# graphics xna shader

5 个答案:

答案 0 :(得分:6)

看看最后一张图片底部的岩石 - 它有穿过它的沙色线条。据推测,你先画沙子,然后画上岩石。

这告诉我,不是通过纹理“绘制黑线”,而是绘制了部分纹理。由于它是在垂直拉伸时发生的,这几乎可以肯定意味着您正在创建从旧像素到新像素的映射,而无需在新纹理中插入值。

例如,使用映射(x,y) --> (x, 2y),这些点将被映射为(0,0) --> (0,0)(0,1) --> (0,2)(0,2) --> (0, 4)。请注意,源纹理中没有点映射到(0,1)(0,3)。这会导致背景渗透。我打赌如果你把它改成水平伸展,你会看到垂直线。

您需要做的是以另一种方式映射:给定目标纹理中的每个像素,使用上述变换的反转在源图像中找到它的值。您可能会获得小数值像素坐标,因此您需要插值。

我对XNA一点也不熟悉,但有可能比手工更方便。

答案 1 :(得分:5)

问题与HLSL中的数字类型有关。

首先,让我们清理那个着色器。我会这样做,因为这是我们找到实际问题的方式。在放入taperfix之前,这是SplattedTileShader.fx的统一差异:

@@ -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功能而不是三个。

接下来是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,那么在代码方面会很好,但事实并非如此。

那么如何解决呢?好吧,中途可能有规则。如果移动代码位于中间位置,那么移动代码会渲染一个像素。我们对图层做了同样的事情。

这是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 :(得分:3)

我无法弄清楚为什么会出现黑线,但我可以给你另一种方式来渲染可能导致它看起来正确的景观(并且希望能给你一点速度提升)。

精灵

为此,您需要texture atlas(a.k.a。精灵表)。您可以将altas拆分为多个地图集并使用多纹理。

顶点缓冲区

我要做的是刮开SpriteBatch,你总是知道你的精灵将在哪里 - 在启动时创建一个VertexBuffer(可能每层一个)并用它来绘制图层。像这样的东西(这是一个2D缓冲区,它只是'看起来像'你的):

Vertex Buffer Sample

顶点定义可能包括:

  • 职位(Vector2
  • 纹理坐标(Vector2
  • 颜色(Vector4/Color

每次需要“循环”景观时(稍后会详细介绍),您将通过相机下方的地图并更新VertexBuffer中的纹理坐标和/或颜色。不要每帧刷新缓冲区。我不会将纹理坐标发送到[0, 1]范围内的GPU,而是[0, Number of Sprites] - 计算顶点着色器中的[0, 1]

重要:不共享顶点(即使用IndexBuffer),因为两个以上面共享的顶点需要保持不同(它们具有不同的纹理坐标) ) - 建立缓冲区,就好像IndexBuffer不存在一样。在这种情况下使用IndexBuffer是浪费的,所以只需坚持使用VertexBuffer

渲染

您使用的世界矩阵会将[0, 1]映射到屏幕大小加上图块的大小(即简单比例x = Viewport.Width + 32y = Viewport.Height + 32)。您的投影矩阵将是一个单位矩阵。

视图矩阵很棘手。想象一下,你的地图正在{0,0}处查看当前的瓷砖块(它是),您需要做的是计算出相机所在的偏移量(以像素为单位)。所以基本上它将是一个x = Camera.X - LeftTile.X * (Viewport.Width / NumTiles.X)的偏移矩阵,类似于y

矩阵是唯一棘手的一点,一旦你设置它就是一个简单的DrawUserPrimitives()电话,你就完成了。

请注意,这只会影响你的风景,像今天一样吸引你的其他精灵。

景观骑行

当相机的位置发生变化时,您基本上需要确定它是否正在查看新的瓷砖块并适当更新VertexBuffer(纹理坐标和颜色 - 单独保留位置,不需要重新计算它。)

替代地

另一个选择是将每个图层渲染为RenderTarget2D,并对整个图层使用一次当前变换。这可以解决你的问题,或者,它会使真正的原因非常明显。

旁注:如果不是00h40,我会提供示例代码,这个问题值得。我会看到明天晚上有多少时间。

答案 3 :(得分:2)

根据您提供给我们的内容,我非常怀疑您的分层代码。这看起来像底层是有时戳穿应该在顶部的层并隐藏它们,这取决于浮点舍入。垂直于视角的条纹是一个非常常见的效果,当你有两个三角形应该是完全共面的,但由于某种原因没有完全相同的顶点坐标(例如一个比另一个大)。如果您将各个层彼此分开绘制非常小的量,会发生什么?比如,在-0.00002处绘制底部实体图层,在-0.00001处绘制下一个实体图层,在0处绘制顶层图层(假设所有三个图层现在都在0处绘制)。

我不是特别了解XNA,但分层问题始终是使用浮点表示几何的基本问题,如果XNA“神奇地”为你避免它,我会感到惊讶。不知道为什么有些瓷砖很好,但大多数是拧紧的。可能那些瓷砖很幸运,或者其他东西。由浮点错误引起的问题通常会像这样非常奇怪。

如果稍微分离图层没有帮助,那么你几乎可以简化为基于注释的标准调试;尝试没有精灵,没有动画瓷砖,没有透明层,&amp; c。当它停止发生时,无论你刚刚评论出来的是打破它:P

答案 4 :(得分:0)

问题出在您的SplattedTileShader.fx着色器中。

出现黑线是因为丢弃的像素。

很难在没有更多信息的情况下关注着色器代码,但问题出在那里。

我认为你做多重纹理的方式太复杂了。

也许更容易制作一次性支持多重纹理的着色器。

在您的顶点中,您可以为每个纹理传递四个权重值,并在着色器中自行混合。

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