无法使用采样器显示纹理



我正在尝试显示加载的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;
}

您正在为 mipmap 分配空间,但实际上并没有生成它们。文档说,在指定MTKTextureLoaderOptionAllocateMipmaps时,"加载纹理时会为纹理分配一整套 mipmap 级别,生成 mipmap 内容是您的责任。

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

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

最新更新