【问题标题】:Why MTLTexture with 2D Array doesn't work?为什么带有 2D 数组的 MTLTexture 不起作用?
【发布时间】:2019-05-04 04:50:19
【问题描述】:

我正在尝试复制 Unity 教程中的 splat map 技术。他们使用Texture2DArray,所以我用这种类型创建了MTLTexture:

private func createTerrainTexture(_ bundle: Bundle) -> MTLTexture {
    guard let device = MTLCreateSystemDefaultDevice() else {
        fatalError()
    }

    let names = ["sand", "grass", "earth", "stone", "snow"]

    let loader = MTKTextureLoader(device: device)
    let array = names.map { name -> MTLTexture in
        do {
            return try loader.newTexture(name: name, scaleFactor: 1.0, bundle: bundle, options: nil)
        } catch {
            fatalError()
        }
    }

    guard let queue = device.makeCommandQueue() else {
        fatalError()
    }
    guard let commandBuffer = queue.makeCommandBuffer() else {
        fatalError()
    }
    guard let encoder = commandBuffer.makeBlitCommandEncoder() else {
        fatalError()
    }

    let descriptor = MTLTextureDescriptor()
    descriptor.textureType = .type2DArray
    descriptor.pixelFormat = array[0].pixelFormat
    descriptor.width = array[0].width
    descriptor.height = array[0].height
    descriptor.mipmapLevelCount = array[0].mipmapLevelCount
    descriptor.arrayLength = 5

    guard let texture = device.makeTexture(descriptor: descriptor) else {
        fatalError()
    }

    var slice = 0
    array.forEach { item in
        encoder.copy(from: item,
                     sourceSlice: 0,
                     sourceLevel: 0,
                     sourceOrigin: MTLOrigin(x: 0, y: 0, z: 0),
                     sourceSize: MTLSize(width: item.width, height: item.height, depth: 1),
                     to: texture,
                     destinationSlice: slice,
                     destinationLevel: 0,
                     destinationOrigin: MTLOrigin(x: 0, y: 0, z: 0))
        slice += 1
    }

    encoder.endEncoding()

    commandBuffer.commit()
    commandBuffer.waitUntilCompleted()

    return texture
}

这是我的片段着色器功能:

fragment half4 terrainFragment(TerrainVertexOutput in [[stage_in]],
                               texture2d_array<float> terrainTexture [[texture(0)]])
{
    constexpr sampler sampler2d(coord::normalized, filter::linear, address::repeat);
    float2 uv = in.position.xz * 0.02;
    float4 c1 = terrainTexture.sample(sampler2d, uv, 0);
    return half4(c1);
}

这是教程中的 Unity 着色器:

void surf (Input IN, inout SurfaceOutputStandard o) {
    float2 uv = IN.worldPos.xz * 0.02;
    fixed4 c = UNITY_SAMPLE_TEX2DARRAY(_MainTex, float3(uv, 0));
    Albedo = c.rgb * _Color;
    o.Metallic = _Metallic;
    o.Smoothness = _Glossiness;
    o.Alpha = c.a;
}

由于某些原因,当纹理在列中重复时,我得到了错误的结果。

我想要的结果是:

更新。 这是纹理在 GPU 帧调试器中的样子:

当我像这样复制 mipmap 时:

var slice = 0
array.forEach { item in
    print(item.width, item.height, item.mipmapLevelCount)
    for i in 0..<descriptor.mipmapLevelCount {
        encoder.copy(from: item,
                     sourceSlice: 0,
                     sourceLevel: i,
                     sourceOrigin: MTLOrigin(x: 0, y: 0, z: 0),
                     sourceSize: MTLSize(width: item.width, height: item.height, depth: 1),
                     to: texture,
                     destinationSlice: slice,
                     destinationLevel: i,
                     destinationOrigin: MTLOrigin(x: 0, y: 0, z: 0))
    }

    slice += 1
}

我收到错误:

-[MTLDebugBlitCommandEncoder validateCopyFromTexture:sourceSlice:sourceLevel:sourceOrigin:sourceSize:toTexture:destinationSlice:destinationLevel:destinationOrigin:options:]:254: failed assertion `(sourceOrigin.x + sourceSize.width)(512) must be <= width(256).'

【问题讨论】:

  • 你在 Xcode 的 GPU 帧调试器中查看过纹理吗? array 中的所有源纹理是否具有相同的尺寸?该代码允许这些源纹理具有 mipmap,但没有正确复制它们。您还需要遍历关卡并复制它们。
  • @KenThomases 你能告诉我如何复制 mipmap 吗?我试图做内部循环,但在copy() 方法中失败了。
  • 您使用来自0..&lt;item.mipmapLevelCount 的索引进行循环并执行您现在正在执行的复制,但为sourceLevel:destinationLevel: 参数指定该索引。如果您仍然遇到问题,请编辑您的问题以显示新代码以及您遇到的任何错误。
  • @KenThomases 好的,我尝试复制 mipmap 但出现错误。
  • 哦,对了。我的错。您需要调整要复制的级别 (sourceSize:) 的大小,因为每个级别都小于之前的级别。宽度的 C 为max(1, item.width &gt;&gt; level)。身高类似。不确定它在 Swift 中是否有所不同。

标签: ios shader scenekit metal


【解决方案1】:

问题在于片段着色器输入变量的错误移植。在原始输入中使用了worldPos,但我使用了float4 position [[position]],根据金属规范,这意味着

描述片段的窗口相对坐标(x、y、z、1/w)值。

所以位置不正确。以下是正确的片段着色器输入的样子:

struct TerrainVertexOutput
{
    float4 position [[position]];
    float3 p;
};

和顶点函数:

vertex TerrainVertexOutput terrainVertex(TerrainVertexInput in [[stage_in]],
                                         constant SCNSceneBuffer& scn_frame [[buffer(0)]],
                                         constant MyNodeBuffer& scn_node [[buffer(1)]])
{
    TerrainVertexOutput v;

    v.position = scn_node.modelViewProjectionTransform * float4(in.position, 1.0);
    v.p = (scn_node.modelTransform * float4(in.position, 1.0)).xyz;

    return v;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多