问题描述
在金属中,当我已经有一个 RenderCommandEncoder 并且我已经用它做了一些工作时,我如何清除深度缓冲区或模板缓冲区(但不是两个我都需要保留一个)?例如,在 OpenGl 中,我们有 glClearDepthf
/GL_DEPTH_BUFFER_BIT
和 glClearStencil
/GL_STENCIL_BUFFER_BIT
但我没有找到任何等效的在金属中.
In metal, when I already have a RenderCommandEncoder and when I already did some job with it, how can I clear the depth buffer or the stencil buffer (but not both I need to keep one)? For example, in OpenGl we have glClearDepthf
/ GL_DEPTH_BUFFER_BIT
and glClearStencil
/ GL_STENCIL_BUFFER_BIT
but I didn't find any equivalent in metal.
推荐答案
虽然 Metal 确实没有提供一种机制来清除渲染通道中间的深度或模板缓冲区,但可以创建一个接近-微不足道的管道状态,让您可以随心所欲地进行操作.
While it's true that Metal doesn't provide a mechanism to clear the depth or stencil buffers in the middle of a rendering pass, it's possible to create a near-trivial pipeline state that allows you to do so as selectively as you like.
在将一些 OpenGL 代码移植到 Metal 的过程中,我发现自己需要清除与当前设置的视口的边界相对应的深度缓冲区的一部分.这是我的解决方案:
In the course of porting some OpenGL code to Metal, I found myself with a need to clear a section of the depth buffer that corresponds to the bounds of the currently set viewport. Here was my solution:
在我的设置代码中,我创建了一个专门的 MTLRenderPipelineState 和 MTLDepthStencilState,它们仅用于清除深度缓冲区,并将它们与我的其他长期存在的资源一起存放在我的 MTKView 子类中:
In my setup code, I create a specialized MTLRenderPipelineState and MTLDepthStencilState that are used only for the purpose of clearing the depth buffer, and stash them in my MTKView subclass with my other long-lived resources:
@property (nonatomic, retain) id<MTLRenderPipelineState> pipelineDepthClear;
@property (nonatomic, retain) id<MTLDepthStencilState> depthStencilClear;
[...]
// Special depth stencil state for clearing the depth buffer
MTLDepthStencilDescriptor *depthStencilDescriptor = [[MTLDepthStencilDescriptor alloc] init];
// Don't actually perform a depth test, just always write the buffer
depthStencilDescriptor.depthCompareFunction = MTLCompareFunctionAlways;
depthStencilDescriptor.depthWriteEnabled = YES;
depthStencilDescriptor.label = @"depthStencilClear";
depthStencilClear = [self.device newDepthStencilStateWithDescriptor:depthStencilDescriptor];
// Special pipeline state just for clearing the depth buffer.
MTLRenderPipelineDescriptor *renderPipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
// Omit the color attachment, since we don't want to write the color buffer for this case.
renderPipelineDescriptor.depthAttachmentPixelFormat = self.depthStencilPixelFormat;
renderPipelineDescriptor.rasterSampleCount = self.sampleCount;
renderPipelineDescriptor.vertexFunction = [self.library newFunctionWithName:@"vertex_depth_clear"];
renderPipelineDescriptor.vertexFunction.label = @"vertexDepthClear";
renderPipelineDescriptor.fragmentFunction = [self.library newFunctionWithName:@"fragment_depth_clear"];
renderPipelineDescriptor.fragmentFunction.label = @"fragmentDepthClear";
MTLVertexDescriptor *vertexDescriptor = [[MTLVertexDescriptor alloc] init];
vertexDescriptor.attributes[0].format = MTLVertexFormatFloat2;
vertexDescriptor.attributes[0].offset = 0;
vertexDescriptor.attributes[0].bufferIndex = 0;
vertexDescriptor.layouts[0].stepRate = 1;
vertexDescriptor.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex;
vertexDescriptor.layouts[0].stride = 8;
renderPipelineDescriptor.vertexDescriptor = vertexDescriptor;
NSError* error = NULL;
renderPipelineDescriptor.label = @"pipelineDepthClear";
self.pipelineDepthClear = [self.device newRenderPipelineStateWithDescriptor:renderPipelineDescriptor error:&error];
并在我的 .metal 文件中设置匹配的顶点和片段函数:
and set up the matching vertex and fragment functions in my .metal file:
struct DepthClearVertexIn
{
float2 position [[ attribute(0) ]];
};
struct DepthClearVertexOut
{
float4 position [[ position ]];
};
struct DepthClearFragmentOut
{
float depth [[depth(any)]];
};
vertex DepthClearVertexOut
vertex_depth_clear( DepthClearVertexIn in [[ stage_in ]])
{
DepthClearVertexOut out;
// Just pass the position through. We're clearing in NDC space.
out.position = float4(in.position, 0.5, 1.0);
return out;
}
fragment DepthClearFragmentOut fragment_depth_clear()
{
DepthClearFragmentOut out;
out.depth = 1.0;
return out;
}
最后,我的 clearDepthBuffer() 方法的主体如下所示:
Finally, the body of my clearDepthBuffer() method looks like this:
// Set up the pipeline and depth/stencil state to write a clear value to only the depth buffer.
[view.commandEncoder setDepthStencilState:view.depthStencilClear];
[view.commandEncoder setRenderPipelineState:view.pipelineDepthClear];
// Normalized Device Coordinates of a tristrip we'll draw to clear the buffer
// (the vertex shader set in pipelineDepthClear ignores all transforms and just passes these through)
float clearCoords[8] = {
-1, -1,
1, -1,
-1, 1,
1, 1
};
[view.commandEncoder setVertexBytes:clearCoords length:sizeof(float) * 8 atIndex:0];
[view.commandEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
// Be sure to reset the setDepthStencilState and setRenderPipelineState for further drawing
由于顶点着色器根本不变换坐标,所以我在 NDC 空间中指定了输入几何体,因此从 (-1, -1) 到 (1, 1) 的矩形覆盖了整个视口.如果您适当地设置几何和/或变换,可以使用相同的技术清除深度缓冲区的任何部分.
Since the vertex shader doesn't transform the coordinates at all, I specify the input geometry in NDC space, so a rectangle from (-1, -1) to (1, 1) covers the entire viewport. The same technique could be used to clear any portion of your depth buffer if you set up the geometry and/or transforms appropiately.
类似的技术应该适用于清除模板缓冲区,但我将把它留给读者作为练习.;)
A similar technique should work for clearing stencil buffers, but I'll leave that as an exercise to the reader. ;)
这篇关于在金属中如何清除深度缓冲区或模板缓冲区?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!