首页 文章

从金属中的顶点缓冲区渲染四边形

提问于
浏览
0

我正在尝试使用Metal渲染像素对齐的2D四边形,但似乎无法使其正确 .

我的顶点缓冲区的内容(在创建时从CPU端记录)是:

Vertex(position: float4(0.0, 0.0, 0.5, 1.0), textureCoordinate: float2(0.0, 0.0))
Vertex(position: float4(0.0, 64.0, 0.5, 1.0), textureCoordinate: float2(0.0, 1.0))
Vertex(position: float4(64.0, 0.0, 0.5, 1.0), textureCoordinate: float2(1.0, 0.0))
Vertex(position: float4(64.0, 64.0, 0.5, 1.0), textureCoordinate: float2(1.0, 1.0))

绘制两个三角形的索引缓冲区包含以下索引:

0, 1, 2, 2, 1, 3

我正在使用的纹理是:

enter image description here

......但我得到这样的东西:

enter image description here

捕获帧并检查顶点缓冲区时,我得到:

enter image description here

Clearly, position and texture coordinates are mixed up .

这是我用来创建几何的代码:

import Metal
import simd

struct Vertex {
    var position = float4(x: 0, y: 0, z: 0, w: 1)
    var textureCoordinate = float2(x: 0, y: 0)
}

class Quad {
    let vertexBuffer: MTLBuffer
    let indexBuffer: MTLBuffer
    let indexCount: Int
    let indexType: MTLIndexType
    let primitiveType: MTLPrimitiveType

    init(sideLength: Float, device: MTLDevice) {
        self.primitiveType = .triangle

        var vertexData = [Vertex]()

        var topLeft = Vertex()
        topLeft.position.x = 0
        topLeft.position.y = 0
        topLeft.position.z = 0.5
        topLeft.textureCoordinate.x = 0
        topLeft.textureCoordinate.y = 0
        vertexData.append(topLeft)

        var bottomLeft = Vertex()
        bottomLeft.position.x = 0
        bottomLeft.position.y = sideLength
        bottomLeft.position.z = 0.5
        bottomLeft.textureCoordinate.x = 0
        bottomLeft.textureCoordinate.y = 1
        vertexData.append(bottomLeft)

        var topRight = Vertex()
        topRight.position.x = sideLength
        topRight.position.y = 0
        topRight.position.z = 0.5
        topRight.textureCoordinate.x = 1
        topRight.textureCoordinate.y = 0
        vertexData.append(topRight)

        var bottomRight = Vertex()
        bottomRight.position.x = sideLength
        bottomRight.position.y = sideLength
        bottomRight.position.z = 0.5
        bottomRight.textureCoordinate.x = 1
        bottomRight.textureCoordinate.y = 1
        vertexData.append(bottomRight)

        for vertex in vertexData {
            Swift.print(vertex) // logs the structs posted above
        }

        let vertexBufferSize = vertexData.count * MemoryLayout<Vertex>.stride
        self.vertexBuffer = device.makeBuffer(bytes: vertexData, length: vertexBufferSize, options: [])

        var indexData = [UInt32]()

        // First triangle: Top left, bottom left, top right (CCW)
        indexData.append(0)
        indexData.append(1)
        indexData.append(2)

        // Second triangle: top right, bottom left, bottom right (CCW)
        indexData.append(2)
        indexData.append(1)
        indexData.append(3)

        for index in indexData {
            Swift.print(index) // logs the integers posted before
        }

        self.indexType = .uint32
        self.indexCount = indexData.count

        let indexBufferSize = indexData.count * MemoryLayout<UInt32>.stride
        self.indexBuffer = device.makeBuffer(bytes: indexData, length: indexBufferSize, options: [])
    }
}

...着色器:

#include <metal_stdlib>
using namespace metal;

struct Constants {
    float4x4 modelViewProjectionMatrix;
    float4 tintColor;
};
struct VertexIn {
    packed_float4 position [[ attribute(0) ]];
    packed_float2 texCoords [[ attribute(1) ]];
};
struct VertexOut {
    float4 position [[position]];
    float2 texCoords;
};

vertex VertexOut sprite_vertex_transform(device VertexIn *vertices [[buffer(0)]],
                                     constant Constants &uniforms [[buffer(1)]],
                                     uint vertexId [[vertex_id]]) {

    float4 modelPosition = vertices[vertexId].position;

    VertexOut out;

    out.position = uniforms.modelViewProjectionMatrix * modelPosition;
    out.texCoords = vertices[vertexId].texCoords;

    return out;
}

fragment half4 sprite_fragment_textured(
    VertexOut fragmentIn [[stage_in]],
    texture2d<float, access::sample> tex2d [[texture(0)]],
    sampler sampler2d [[sampler(0)]]){

    half4 surfaceColor = half4(tex2d.sample(sampler2d, fragmentIn.texCoords).rgba);

    return surfaceColor;
}

...这是渲染它的代码(索引):

renderEncoder.setVertexBuffer(quad.vertexBuffer, offset: 0, at: 0)
renderEncoder.setVertexBytes(&constants, length: MemoryLayout<Constants>.stride, at: 1)
renderEncoder.setFragmentTexture(texture, at: 0)
renderEncoder.setFragmentSamplerState(sampler, at: 0)

// quad is an instance  of the class above, with sideLength == 64
renderEncoder.drawIndexedPrimitives(
        type: quad.primitiveType,
        indexCount: quad.indexCount,
        indexType: quad.indexType,
        indexBuffer: quad.indexBuffer,
        indexBufferOffset: 0)

很明显,这些数据并没有在某个地方出错,但我无法弄清楚 where .

整个项目是on GitHub .

1 回答

  • 0

    这部分似乎造成了麻烦:

    struct VertexIn {
        packed_float4 position [[ attribute(0) ]];
        packed_float2 texCoords [[ attribute(1) ]];
    };
    

    从调试器控制台,在CPU端:

    (lldb) print MemoryLayout<Vertex>.stride
    (Int) $R0 = 32
    (lldb) print MemoryLayout<Vertex>.size
    (Int) $R1 = 24
    (lldb)
    

    删除 packed_ 前缀解决了它:

    struct VertexIn {
        float4 position [[ attribute(0) ]];
        float2 texCoords [[ attribute(1) ]];
    };
    

    但我想现在我的缓冲区在空间使用方面并不是最佳的 . 我将不得不弄清楚如何使 packed_ 版本工作......

    我真的需要对结构对齐和包装进行复习......

    Note: 最初,我调整了Apple的示例项目中的代码,其中顶点结构有 positionnormal ,两者都是 float4 类型 . 清楚地将struct "net" size从 8*sizeof(float) 更改为 6*sizeof(float) 是导致问题的原因 .

相关问题