答案 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缓冲区,它只是'看起来像'你的):
顶点定义可能包括:
Vector2
)Vector2
)Vector4/Color
)每次需要“循环”景观时(稍后会详细介绍),您将通过相机下方的地图并更新VertexBuffer
中的纹理坐标和/或颜色。不要每帧刷新缓冲区。我不会将纹理坐标发送到[0, 1]
范围内的GPU,而是[0, Number of Sprites]
- 计算顶点着色器中的[0, 1]
。
重要:不共享顶点(即使用IndexBuffer
),因为两个以上面共享的顶点需要保持不同(它们具有不同的纹理坐标) ) - 建立缓冲区,就好像IndexBuffer
不存在一样。在这种情况下使用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,我会提供示例代码,这个问题值得。我会看到明天晚上有多少时间。
答案 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