iOS Metal - 渲染一个完整的三角形

iOS Metal - rendering a complete triangle

我正在尝试 https://www.raywenderlich.com/81399/ios-8-metal-tutorial-swift-moving-to-3d

的 Metal 教程

此问题仅与教程的第一部分有关,其中先渲染三角形,然后渲染立方体。

我似乎已经完成了提到的所有事情,创建了一个顶点结构,节点class,三角形(然后是立方体子class),重构了我的视图控制器和着色器。

应用呈现,但它只呈现一半屏幕,如下所示:

这是我的 ViewController:

import UIKit
import Metal
import QuartzCore

class ViewController: UIViewController {

    var device : MTLDevice! = nil
    var metalLayer : CAMetalLayer! = nil
    var vertexBuffer : MTLBuffer! = nil
    var pipelineState : MTLRenderPipelineState! = nil
    var commandQueue: MTLCommandQueue! = nil
    var timer: CADisplayLink! = nil

    var objectToDraw: Triangle!


    override func viewDidLoad() {
        super.viewDidLoad()

        initialize_2()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func initialize_2(){

        device = MTLCreateSystemDefaultDevice()
        metalLayer = CAMetalLayer()
        metalLayer.device = device
        metalLayer.pixelFormat = .BGRA8Unorm
        metalLayer.framebufferOnly = true
        metalLayer.frame = view.layer.frame
        view.layer.addSublayer(metalLayer)

        objectToDraw = Triangle(device: device)

        let defaultLibrary = device.newDefaultLibrary()
        let fragmentProgram = defaultLibrary!.newFunctionWithName("basic_fragment")
        let vertexProgram = defaultLibrary!.newFunctionWithName("basic_vertex")

        let pipelineStateDescriptor = MTLRenderPipelineDescriptor()
        pipelineStateDescriptor.vertexFunction = vertexProgram
        pipelineStateDescriptor.fragmentFunction = fragmentProgram
        pipelineStateDescriptor.colorAttachments[0].pixelFormat = .BGRA8Unorm

        do{
            try pipelineState = device.newRenderPipelineStateWithDescriptor(pipelineStateDescriptor)
        }catch is NSError{
            print("Failed to create pipeline state")
        }


        commandQueue = device.newCommandQueue()

        timer = CADisplayLink(target: self, selector: #selector(ViewController.gameloop_2))
        timer.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSDefaultRunLoopMode)

    }

    func gameloop_2() {
        autoreleasepool {
            self.render_2()
        }
    }

    func render_2(){
        var drawable = metalLayer.nextDrawable()
        objectToDraw.render(commandQueue, pipelineState: pipelineState, drawable: drawable!, clearColor: nil)
    }
}

这是我的顶点 class:

import Foundation

struct Vertex{

    var x, y, z : Float // position data
    var r, g, b, a : Float // color data

    func floatBuffer() -> [Float]{
        return [x,y,x,r,g,b,a]
    }
}

节点class:

import Foundation
import Metal
import QuartzCore

class Node {

    let name: String
    var vertexCount: Int
    var vertexBuffer: MTLBuffer
    var device: MTLDevice

    init(name: String, vertices: Array<Vertex>, device: MTLDevice){
        // 1
        var vertexData = Array<Float>()
        for vertex in vertices{
            vertexData += vertex.floatBuffer()
        }

        // 2
        let dataSize = vertexData.count * sizeofValue(vertexData[0])
        vertexBuffer = device.newBufferWithBytes(vertexData, length: dataSize, options: [])

        // 3
        self.name = name
        self.device = device
        vertexCount = vertices.count
    }

    func render(commandQueue: MTLCommandQueue, pipelineState: MTLRenderPipelineState, drawable: CAMetalDrawable, clearColor: MTLClearColor?){

        let renderPassDescriptor = MTLRenderPassDescriptor()
        renderPassDescriptor.colorAttachments[0].texture = drawable.texture
        renderPassDescriptor.colorAttachments[0].loadAction = .Clear
        renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0.0, green: 104.0/255.0, blue: 5.0/255.0, alpha: 1.0)
        renderPassDescriptor.colorAttachments[0].storeAction = .Store

        let commandBuffer = commandQueue.commandBuffer()

        let renderEncoderOpt : MTLRenderCommandEncoder! = commandBuffer.renderCommandEncoderWithDescriptor(renderPassDescriptor)

        if let renderEncoder = renderEncoderOpt {
            renderEncoder.setRenderPipelineState(pipelineState)
            renderEncoder.setVertexBuffer(vertexBuffer, offset: 0, atIndex: 0)
            renderEncoder.drawPrimitives(.Triangle, vertexStart: 0, vertexCount: vertexCount, instanceCount: vertexCount/3)
            renderEncoder.endEncoding()
        }

        commandBuffer.presentDrawable(drawable)
        commandBuffer.commit()
    }

}

和三角形class:

import Foundation
import Metal

class Triangle: Node {

    init(device: MTLDevice){

        let V0 = Vertex(x:  0.0, y:   1.0, z:   0.0, r:  1.0, g:  0.0, b:  0.0, a:  1.0)
        let V1 = Vertex(x: -1.0, y:  -1.0, z:   0.0, r:  0.0, g:  1.0, b:  0.0, a:  1.0)
        let V2 = Vertex(x:  1.0, y:  -1.0, z:   0.0, r:  0.0, g:  0.0, b:  1.0, a:  1.0)

        var verticesArray = [V0,V1,V2]
        super.init(name: "Triangle", vertices: verticesArray, device: device)
    }

}

我的着色器文件:

#include <metal_stdlib>
using namespace metal;

struct VertexIn{
    packed_float3 position;
    packed_float4 color;
};

struct VertexOut{
    float4 position [[position]];
    float4 color;
};

vertex VertexOut basic_vertex(
                              const device VertexIn* vertex_array [[ buffer(0) ]],
                              unsigned int vid [[ vertex_id ]]) {

    VertexIn VertexIn = vertex_array[vid];

    VertexOut VertexOut;
    VertexOut.position = float4(VertexIn.position,1);
    VertexOut.color = VertexIn.color;

    return VertexOut;
}

fragment half4 basic_fragment(VertexOut interpolated [[stage_in]]) {
    return half4(interpolated.color[0], interpolated.color[1], interpolated.color[2], interpolated.color[3]); 
}

我想了解为什么着色器不渲染完整的三角形。我对图形编程也(非常)陌生,因此非常感谢任何帮助或指导。教程的 link 再次是:https://www.raywenderlich.com/81399/ios-8-metal-tutorial-swift-moving-to-3d

编辑:我正在 运行 播出 iPad 节目,如果这会有什么不同的话。我也尝试更改 Triangle.swift 中的坐标,但显然我做错了。

我发现顶点设置中有一个小错误可能会导致此问题。

func floatBuffer() -> [Float]{
        return [x,y,x,r,g,b,a]
    }