无法使用采样器显示纹理

Can't display texture with sampler

我正在尝试显示使用 MTKTextureLoader 加载的纹理,我有一个缓冲区存储我的顶点坐标(我构建了两个三角形以具有一个矩形来显示我的图像),然后我有一个缓冲区存储每个顶点的纹理坐标。
我做了一个采样器来从我的纹理中采样数据,问题是我什么也没得到(黑色图像)。

我放了 Swift 代码以防我的错误来自那里,但我认为它来自 Metal 代码。如果您查看我的片段着色器,您会看到两条评论,它们显示了一些我无法理解的内容:

所以好像不是来自sampler,也不是来自坐标,这就是我不明白。

这是我的 Swift 代码:

import Cocoa
import MetalKit
import Metal

class ViewController: NSViewController, MTKViewDelegate {

    var device:MTLDevice!
    var texture:MTLTexture!
    var commandQueue:MTLCommandQueue!
    var vertexBuffer:MTLBuffer!
    var vertexCoordinates:[Float] = [
        -1, 1, 0, 1,
        -1, -1, 0, 1,
        1, -1, 0,  1,

        1,-1,0,1,
        1,1,0,1,
        -1,1,0,1,

    ]

    var vertexUVBuffer:MTLBuffer!
    var vertexUVCoordinates:[Float] = [
    0,1,
    0,0,
    1,0,
    1,0,
    1,1,
    0,1
    ]

    var library:MTLLibrary!
    var defaultPipelineState:MTLRenderPipelineState!
    var samplerState:MTLSamplerState!

    @IBOutlet var metalView: MTKView!

    override func viewDidLoad() {
        super.viewDidLoad()

        device = MTLCreateSystemDefaultDevice()
        let textureLoader = MTKTextureLoader(device: device)

        metalView.device = device
        metalView.delegate = self
        metalView.preferredFramesPerSecond = 0
        metalView.sampleCount = 4



        texture = try! textureLoader.newTextureWithContentsOfURL(NSBundle.mainBundle().URLForResource("abeilles", withExtension: "jpg")!, options: [MTKTextureLoaderOptionAllocateMipmaps:NSNumber(bool: true)])

        commandQueue = device.newCommandQueue()
        library = device.newDefaultLibrary()


        vertexBuffer = device.newBufferWithBytes(&vertexCoordinates, length: sizeof(Float)*vertexCoordinates.count, options: [])
        vertexUVBuffer = device.newBufferWithBytes(&vertexUVCoordinates, length: sizeof(Float)*vertexUVCoordinates.count, options: [])



        let renderPipelineDescriptor = MTLRenderPipelineDescriptor()
        renderPipelineDescriptor.vertexFunction = library.newFunctionWithName("passTroughVertex")
        renderPipelineDescriptor.fragmentFunction = library.newFunctionWithName("myFragmentShader")
        renderPipelineDescriptor.sampleCount = metalView.sampleCount
        renderPipelineDescriptor.colorAttachments[0].pixelFormat = metalView.colorPixelFormat
        defaultPipelineState = try! device.newRenderPipelineStateWithDescriptor(renderPipelineDescriptor)
        let samplerDescriptor = MTLSamplerDescriptor()
        samplerDescriptor.minFilter = .Linear
        samplerDescriptor.magFilter = .Linear
        samplerDescriptor.mipFilter = .Linear
        samplerDescriptor.sAddressMode = .ClampToEdge
        samplerDescriptor.rAddressMode = .ClampToEdge
        samplerDescriptor.tAddressMode = .ClampToEdge
        samplerDescriptor.normalizedCoordinates = true

        samplerState = device.newSamplerStateWithDescriptor(samplerDescriptor)
        metalView.draw()






        // Do any additional setup after loading the view.
    }

    func drawInMTKView(view: MTKView) {

        let commandBuffer = commandQueue.commandBuffer()
        let commandEncoder = commandBuffer.renderCommandEncoderWithDescriptor(metalView.currentRenderPassDescriptor!)
        commandEncoder.setRenderPipelineState(defaultPipelineState)

        commandEncoder.setVertexBuffer(vertexBuffer, offset: 0, atIndex: 0)
        commandEncoder.setVertexBuffer(vertexUVBuffer, offset:0, atIndex:1)
        commandEncoder.setFragmentSamplerState(samplerState, atIndex: 0)
        commandEncoder.setFragmentTexture(texture, atIndex: 0)
        commandEncoder.drawPrimitives(MTLPrimitiveType.Triangle, vertexStart: 0, vertexCount: 6, instanceCount: 1)

        commandEncoder.endEncoding()
        commandBuffer.presentDrawable(metalView.currentDrawable!)
        commandBuffer.commit()

    }

    func mtkView(view: MTKView, drawableSizeWillChange size: CGSize) {
        // view.draw()
    }

    override var representedObject: AnyObject? {
        didSet {
        // Update the view, if already loaded.
        }
    }


}  

这是我的 Metal 代码:

#include <metal_stdlib>
using namespace metal;


struct VertexOut {
    float4 position [[position]];
    float2 texCoord;
};


vertex VertexOut passTroughVertex(uint vid [[ vertex_id]],
                                  constant float4 *vertexPosition [[ buffer(0) ]],
                                  constant float2 *vertexUVPos [[ buffer(1)]]) {

    VertexOut vertexOut;
    vertexOut.position = vertexPosition[vid];
    vertexOut.texCoord = vertexUVPos[vid];
    return vertexOut;
}

fragment float4 myFragmentShader(VertexOut inFrag [[stage_in]],
                                 texture2d<float> myTexture [[ texture(0)]],
                                 sampler mySampler [[ sampler(0) ]]) {


    float4 myColor = myTexture.sample(mySampler,inFrag.texCoord);
    // myColor = myTexture.sample(mySampler,float2(1));
    // myColor = float4(inFrag.texCoord.r,inFrag.texCoord.g,0,1);

    return myColor;
}

您正在为 mipmaps 分配 space 但实际上并没有生成它们。 The docs say 指定 MTKTextureLoaderOptionAllocateMipmaps 时,"a full set of mipmap levels are allocated for the texture when the texture is loaded, and it is your responsibility to generate the mipmap contents."

只要纹理相对于屏幕上的矩形较小,您的采样器配置就会导致在基础 mipmap 级别对生成的纹理进行采样,但如果您输入较大的纹理,它会开始对较小的级别进行采样的 mipmap 堆栈,拾取 all-black 像素,然后将它们混合在一起以使图像变暗或使输出完全变黑。

加载纹理后,您应该在 MTLBlitCommandEncoder 上使用 -generateMipmapsForTexture: 方法生成一组完整的 mipmap。