【发布时间】:2020-06-23 13:12:09
【问题描述】:
我正在尝试使用 Swift 在 Metal 中绘制一个带纹理的 2D 矩形。所有在线示例,包括以下示例,都使用 Objective-C 或更早版本的 Swift,并且很难移植到 Swift 5:
How to draw a textured rectangle with Metal https://developer.apple.com/documentation/metal/creating_and_sampling_textures
我的目标是在视频的一角画一个简单的水印。我有一个Renderer 类,用于捕获帧,将它们绘制到我的金属视图(这很好),然后想在顶部绘制一个徽标。我不能对相机帧使用相同的方法,因为它们是 YUV 格式,所以使用特殊的着色器。我的水印只是一个普通的旧 PNG 文件。
我能够绘制无纹理的矩形,但尝试应用纹理失败 - 没有错误,什么都没有。屏幕上什么也没有出现。当 Metal 没有返回错误来调试这些类型的问题时,我应该检查哪些关键事项?
这里有一些代码:
func drawRect(renderEncoder: MTLRenderCommandEncoder, x:Double, y:Double, w:Double, h:Double, pipelineState:MTLRenderPipelineState, viewSize: CGSize)
{
//new viewport to size of image we want to display
var viewPort = MTLViewport(originX: x, originY: y, width: w, height: h, znear: 0.0, zfar: 1.0)
var viewPortSize = simd_float2(x: Float(w), y: Float(h))
var logoQuadVertices:[AAPLVertex] = [
//Triangle1: top left, top right, bottom left
AAPLVertex(position: simd_float2(x: 0, y: 0), textureCoordinate: simd_float2(x: 0.0, y: 0.0)),
AAPLVertex(position: simd_float2(x: 100, y: 0), textureCoordinate: simd_float2(x: 1.0, y: 0.0)),
AAPLVertex(position: simd_float2(x: 0, y: 100), textureCoordinate: simd_float2(x: 0.0, y: 1.0)),
//Triangle2: bottom left, bottom right, top right
AAPLVertex(position: simd_float2(x: 0, y: 100), textureCoordinate: simd_float2(x: 0.0, y: 1.0)),
AAPLVertex(position: simd_float2(x: 100, y: 100), textureCoordinate: simd_float2(x: 1.0, y: 1.0)),
AAPLVertex(position: simd_float2(x: 100, y: 0), textureCoordinate: simd_float2(x: 1.0, y: 0.0))
]
//Set the new viewport
renderEncoder.setViewport(MTLViewport(originX: x, originY: y, width: w, height: h, znear: 0.0, zfar: 1.0))
//Debug string
renderEncoder.pushDebugGroup("DrawRect")
//Set pipeline state
renderEncoder.setRenderPipelineState(pipelineState)
//Send vertex data of rectangle to shaders
renderEncoder.setVertexBytes(logoQuadVertices, length: logoQuadVertices.count*MemoryLayout<AAPLVertex>.stride, index: Int(AAPLVertexInputIndexVertices.rawValue))
//Send vertex bytes of viewport to shaders
renderEncoder.setVertexBytes(&viewPortSize, length: MemoryLayout<simd_float2>.stride, index: Int(AAPLVertexInputIndexViewportSize.rawValue))
// Set the texture
renderEncoder.setFragmentTexture(texture, index: Int(kTextureIndexPNG.rawValue))
//Sampler state
if let samplerState = logoSamplerState {
renderEncoder.setFragmentSamplerState(samplerState, index: 0)
}
//Draw the textured rectangle
renderEncoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 6)
//Debug
renderEncoder.popDebugGroup()
}
...
// Pipeline configuration
let logoPipelineStateDescriptor = MTLRenderPipelineDescriptor()
logoPipelineStateDescriptor.label = "MyLogoPipeline"
logoPipelineStateDescriptor.sampleCount = renderDestination.sampleCount
logoPipelineStateDescriptor.vertexFunction =
defaultLibrary.makeFunction(name: "vertexShader")!
logoPipelineStateDescriptor.fragmentFunction =
defaultLibrary.makeFunction(name: "samplingShader")
logoPipelineStateDescriptor.vertexDescriptor = logoPlaneVertexDescriptor
logoPipelineStateDescriptor.colorAttachments[0].pixelFormat =
renderDestination.colorPixelFormat
logoPipelineStateDescriptor.depthAttachmentPixelFormat =
renderDestination.depthStencilPixelFormat
logoPipelineStateDescriptor.stencilAttachmentPixelFormat =
renderDestination.depthStencilPixelFormat
do {
try logoPipelineState = device.makeRenderPipelineState(descriptor: logoPipelineStateDescriptor)
} catch let error {
print("Failed to created logo pipeline state, error \(error)")
}
...
着色器:
typedef struct
{
float4 position [[position]];
float2 textureCoordinate;
} RasterizerData;
// Vertex Function
vertex RasterizerData
vertexShader(uint vertexID [[ vertex_id ]],
constant AAPLVertex *vertexArray [[ buffer(AAPLVertexInputIndexVertices) ]],
constant vector_uint2 *viewportSizePointer [[ buffer(AAPLVertexInputIndexViewportSize) ]])
{
RasterizerData out;
float2 pixelSpacePosition = vertexArray[vertexID].position.xy;
float2 viewportSize = float2(*viewportSizePointer);
out.position = vector_float4(0.0, 0.0, 0.0, 1.0);
out.position.xy = pixelSpacePosition / (viewportSize / 2.0);
out.textureCoordinate = vertexArray[vertexID].textureCoordinate;
return out;
}
// Fragment function
fragment float4
samplingShader(RasterizerData in [[stage_in]],
texture2d<half> colorTexture [[ texture(AAPLTextureIndexBaseColor) ]])
{
constexpr sampler textureSampler (mag_filter::linear,
min_filter::linear);
// Sample the texture to obtain a color
const half4 colorSample = colorTexture.sample(textureSampler, in.textureCoordinate);
// return the color of the texture
return float4(colorSample);
}
【问题讨论】:
-
您对 Xcode 中的图形调试器有兴趣吗?这是一个非常好的工具,可以查看正在发生的事情......
-
@trojanfoe 我没有,谢谢!今晚会试试这个。
-
还要注意顶点的缠绕顺序(背面被剔除)并使用索引缓冲区,因此您只需要 4 个顶点。
-
不需要。很高兴您解决了您的问题。
-
@Fattie 对不起,不知道。
标签: ios swift arkit swift5 metal