为什么带有2D阵列的MTLTexture不起作用?

时间:2018-12-02 20:53:46

标签: ios shader scenekit metal

我正在尝试从Unity教程复制splat映射技术。他们使用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;
}

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

Wrong texture

我想要的结果是:

enter image description here

更新。 这是GPU Frame Debugger中的纹理外观:

enter image description here

当我像这样复制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).'

1 个答案:

答案 0 :(得分:0)

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

  

描述片段的窗口相对坐标(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;
}