从 Metal 中的顶点缓冲区渲染四边形
Rendering Quad from Vertex Buffer in Metal
我正在尝试使用 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
我使用的纹理是:
...但我得到这样的结果:
当捕获帧并检查顶点缓冲区时,我得到:
很明显,位置和纹理坐标混淆了。
这是我用来创建几何图形的代码:
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)
很明显,数据没有被适当地复制到顶点缓冲区,我在某处走错了步,但我不太清楚哪里。
整个项目是on GitHub。
问题似乎出在这部分:
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) ]];
};
但我想现在我的缓冲区在 space 使用方面并不是最佳的。我必须弄清楚如何让 packed_
版本工作...
我真的需要复习一下结构对齐和打包...
注意: 最初,我改编了 Apple 的示例项目中的代码,其中顶点结构具有 position
和 normal
,类型均为 float4
。 Cleary 将 struct "net" 大小从 8*sizeof(float)
更改为 6*sizeof(float)
是导致问题的原因。
我正在尝试使用 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
我使用的纹理是:
...但我得到这样的结果:
当捕获帧并检查顶点缓冲区时,我得到:
很明显,位置和纹理坐标混淆了。
这是我用来创建几何图形的代码:
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)
很明显,数据没有被适当地复制到顶点缓冲区,我在某处走错了步,但我不太清楚哪里。
整个项目是on GitHub。
问题似乎出在这部分:
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) ]];
};
但我想现在我的缓冲区在 space 使用方面并不是最佳的。我必须弄清楚如何让 packed_
版本工作...
我真的需要复习一下结构对齐和打包...
注意: 最初,我改编了 Apple 的示例项目中的代码,其中顶点结构具有 position
和 normal
,类型均为 float4
。 Cleary 将 struct "net" 大小从 8*sizeof(float)
更改为 6*sizeof(float)
是导致问题的原因。