我的问题是,因为一个四边形只是两个三角形。纹理在每个三角形上的渲染不一致,而且纹理在两个三角形之间的边界上被破坏了。我使用我可爱的Minecraft房子的截图作为纹理示例。
如你所见,从截图的左上角和右下角,似乎被剪掉了,或者是折叠了什么的。它只是扭曲了。而我所说的变形并不是因为它被应用到了梯形上,而是它被不一致地应用到了构成梯形的两个三角形上。
那么如何解决这个问题呢?
在viewDidLoad:
let VertexDescriptor = MTLVertexDescriptor()
let Attribute1Offset = MemoryLayout<simd_float3>.stride
let Attribute2Offset = Attribute1Offset+MemoryLayout<simd_float4>.stride
VertexDescriptor.attributes[0].format = .float3
VertexDescriptor.attributes[1].format = .float4
VertexDescriptor.attributes[1].offset = Attribute1Offset
VertexDescriptor.attributes[2].format = .float2
VertexDescriptor.attributes[2].offset = Attribute2Offset
VertexDescriptor.layouts[0].stride = Attribute2Offset+MemoryLayout<simd_float2>.stride
PipelineDescriptor.vertexDescriptor = VertexDescriptor
let TextureLoader = MTKTextureLoader(device: Device)
Texture = try? TextureLoader.newTexture(URL: Bundle.main.url(forResource: "Texture.png", withExtension: nil)!)
在viewDidLoad: Vertices:
//First four = position, second four = color, last two = texture coordinates
let Vertices: [Float] = [-0.5, 0.5, 0, 0, 1, 1, 0, 1, 0, 0,
0.5, 0.5, 0, 0, 0, 1, 1, 1, 1, 0,
1, -1, 0, 0, 0, 1, 0, 1, 1, 1,
-1, -1, 0, 0, 0, 0, 1, 1, 0, 1]
类型在 Shaders.metal
typedef struct {
float4 Position [[attribute(0)]];
float4 Color [[attribute(1)]];
float2 TexCoord [[attribute(2)]];
} VertexIn;
typedef struct {
float4 Position [[position]];
float4 Color;
float2 TexCoord;
} VertexOut;
忍着点,我用PascalCase是因为我觉得camelCase很丑。我就是不喜欢它。好吧,不管怎么说,我如何正确地将纹理放置在一个由两个三角形组成的四边形中,使它看起来不那么奇怪?
如你所知,Metal执行 透视校正顶点属性插值法 通过使用顶点位置的 z 坐标提供的深度信息来代表你。
您在没有向图形管道提供透视信息的情况下,扭曲了四边形的 "投影 "形状,从而颠覆了这个过程。这意味着您需要传递一点额外的信息,以获得正确的插值。具体来说,你需要在纹理坐标中加入 "深度 "信息,并在片段着色器中手动执行 "透视 "划分。
对于一个完全通用的解决方案,请参考 本回答但为了简单地固定四边形垂直轴的对称比例,可以使用 float3
纹理坐标,而不是 float2
并设定 x 和 z 坐标,使 z 是你的伪透视投影引入的比例因子,当 x 除以 z,结果是什么?x 将会是没有分界线的。
例如,如果顶部两个顶点之间的距离是底部两个顶点的一半(如您的截图所示),将左上角的纹理坐标设置为(0,0,0.5),右上角的纹理坐标设置为(0.5,0,0.5)。将这些 "3D "纹理坐标传递给你的碎片着色器,然后除以 z
在取样前。
half4 color = myTexture.sample(mySampler, in.texCoords.xy / in.texCoords.z);