无法使用采样器

时间:2016-01-25 08:39:29

标签: swift macos metal

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

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

  • 如果我将坐标直接提供给样本函数,它就可以工作(使用与给定坐标对应的颜色为三角形着色)。

  • 如果我将作为颜色分量传递给采样器的坐标给出,它还会显示一些相干的东西(三角形颜色与给定坐标一致)。

所以它似乎不是来自采样器,也不是来自坐标,这是我不理解的。

这是我的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.
        }
    }


}  

这是我的金属代码:

#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;
}

1 个答案:

答案 0 :(得分:1)

您为mipmap分配空间,但实际上并未生成它们。 The docs say在指定MTKTextureLoaderOptionAllocateMipmaps时,“在加载纹理时为纹理分配了一整套mipmap级别,并且您有责任生成mipmap内容。”

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

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