我在试图学习苹果金属图形API的晚上花了一些时间。我遇到了一个令人沮丧的问题,因此必须缺少一些基本的东西:当禁用深度测试时,或者将深度功能更改为"更大"时,我只能在屏幕上出现渲染对象。可能出了什么问题?另外,我可以检查什么样的事情才能调试此问题?
这是我在做的:
1)我正在使用SDL来创建我的窗口。设置金属时,我会手动创建一个cametallayer并将其插入层层次结构。需要明确的是,我不使用mtkview,也不想使用mtkview。尽可能远离Objective-C和Cocoa似乎是编写此应用程序跨平台的最佳策略。目的是用SDL和可以在运行时交换的渲染引擎在平台 - 不合时宜的C 代码中编写。该界面的背后是所有特定于Apple的代码寿命的地方。但是,我强烈怀疑出了问题的一部分与设置层有关:
SDL_SysWMinfo windowManagerInfo;
SDL_VERSION(&windowManagerInfo.version);
SDL_GetWindowWMInfo(&window, &windowManagerInfo);
// Create a metal layer and add it to the view that SDL created.
NSView *sdlView = windowManagerInfo.info.cocoa.window.contentView;
sdlView.wantsLayer = YES;
CALayer *sdlLayer = sdlView.layer;
CGFloat contentsScale = sdlLayer.contentsScale;
NSSize layerSize = sdlLayer.frame.size;
_metalLayer = [[CAMetalLayer layer] retain];
_metalLayer.contentsScale = contentsScale;
_metalLayer.drawableSize = NSMakeSize(layerSize.width * contentsScale,
layerSize.height * contentsScale);
_metalLayer.device = device;
_metalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm;
_metalLayer.frame = sdlLayer.frame;
_metalLayer.framebufferOnly = true;
[sdlLayer addSublayer:_metalLayer];
2)我创建了一个深度纹理,以用作深度缓冲区。我的理解是,在金属中,此步骤是必要的。但是,在OpenGL中,该框架会自动为我创建一个深度缓冲区:
CGSize drawableSize = _metalLayer.drawableSize;
MTLTextureDescriptor *descriptor =
[MTLTextureDescriptorr texture2DDescriptorWithPixelFormat:MTLPixelFormatDepth32Float_Stencil8 width:drawableSize.width height:drawableSize.height mipmapped:NO];
descriptor.storageMode = MTLStorageModePrivate;
descriptor.usage = MTLTextureUsageRenderTarget;
_depthTexture = [_metalLayer.device newTextureWithDescriptor:descriptor];
_depthTexture.label = @"DepthStencil";
3)我创建一个将在渲染时间设置的深模式状态对象:
MTLDepthStencilDescriptor *depthDescriptor = [[MTLDepthStencilDescriptor alloc] init];
depthDescriptor.depthWriteEnabled = YES;
depthDescriptor.depthCompareFunction = MTLCompareFunctionLess;
_depthState = [device newDepthStencilStateWithDescriptor:depthDescriptor];
4)创建我的渲染通过对象时,我明确附加了深度纹理:
_metalRenderPassDesc = [[MTLRenderPassDescriptor renderPassDescriptor] retain];
MTLRenderPassColorAttachmentDescriptor *colorAttachment = _metalRenderPassDesc.colorAttachments[0];
colorAttachment.texture = _drawable.texture;
colorAttachment.clearColor = MTLClearColorMake(0.2, 0.4, 0.5, 1.0);
colorAttachment.storeAction = MTLStoreActionStore;
colorAttachment.loadAction = desc.clear ? MTLLoadActionClear : MTLLoadActionLoad;
MTLRenderPassDepthAttachmentDescriptor *depthAttachment = _metalRenderPassDesc.depthAttachment;
depthAttachment.texture = depthTexture;
depthAttachment.clearDepth = 1.0;
depthAttachment.storeAction = MTLStoreActionDontCare;
depthAttachment.loadAction = desc.clear ? MTLLoadActionClear : MTLLoadActionLoad;
MTLRenderPassStencilAttachmentDescriptor *stencilAttachment = _metalRenderPassDesc.stencilAttachment;
stencilAttachment.texture = depthAttachment.texture;
stencilAttachment.storeAction = MTLStoreActionDontCare;
stencilAttachment.loadAction = desc.clear ? MTLLoadActionClear : MTLLoadActionLoad;
5)最后,在渲染时间,我在绘制对象之前设置了深模对象:
[_encoder setDepthStencilState:_depthState];
请注意,如果我进入步骤3并将DepthCompareFunction更改为mtlCompareFunctunctionsways或mtlcomparefunctiongreater,则我在屏幕上看到多边形,但是(预计)订购不正确。如果我将DepthCompareFunction设置为MTLCompareFunction,那么我只会看到背景颜色。它的作用好像所有片段始终失败了深度测试。
金属API验证器没有报告任何错误,也没有警告...
我尝试了各种设置的组合,例如深度模具纹理格式,但没有取得任何前进的进步。老实说,我不确定接下来要做什么。
编辑:Xcode中的GPU框架捕获显示我的多边形的绿色轮廓,但实际上没有绘制这些片段。
编辑2:我了解到金属API验证器具有"扩展"模式。当启用此功能时,我会收到这两个警告:
warning: Texture Usage Should not be Flagged as MTLTextureUsageRenderTarget: This texture is not a render target. Clear the MTLTextureUsageRenderTarget bit flag in the texture usage options. Texture = DepthStencil. Texture is used in the Depth attachment.
warning: Resource Storage Mode Should be MTLStorageModePrivate and it Should be Initialized with a Blit: This resource is rarely accessed by the CPU. Changing the storage mode to MTLStorageModePrivate and initializing it with a blit from a shared buffer may improve performance. Texture = 0x102095000.
当我发出这两个警告时,我会遇到这两个错误。(警告和错误似乎彼此矛盾。)
error 'MTLTextureDescriptor: Depth, Stencil, DepthStencil, and Multisample textures must be allocated with the MTLResourceStorageModePrivate resource option.'
failed assertion `MTLTextureDescriptor: Depth, Stencil, DepthStencil, and Multisample textures must be allocated with the MTLResourceStorageModePrivate resource option.'
编辑3:当我运行示例金属应用程序并使用GPU框架捕获工具时,我会看到深度缓冲区的灰度表示,并且渲染对象清晰可见。我的应用不会发生这种情况。在那里,GPU框架捕获工具总是将我的深度缓冲区显示为纯白色图像。
好吧,我弄清楚了。我将在这里发布答案,以帮助下一个家伙。写作深度缓冲液没有问题。这解释了为什么花时间用深度纹理和深度模具状态设置陷入困境。
问题是用于金属与OpenGL的归一化设备坐标的坐标系的差异。在金属中,NDC在空间[-1, 1] x [-1, 1] x [0,1]中。在OpenGL中,NDC为[-1, 1] X [-1, 1] X [-1, 1]。如果我简单地采用GLM ::透视图产生的投影矩阵并将其推入金属,那么结果就不会如预期的那样。为了补偿用金属渲染时的NDC空间差异,该投影矩阵必须由对角上的(1、1、0.5、1)的缩放矩阵左右进行。
我发现这些链接很有帮助:1. http://blog.athenstean.com/post/135771439196/from-opengl-to-metal-the-proctive-matrix2. http://www.songho.ca/opengl/gl_proctivementmatrix.html
编辑:用更完整和准确的解释替换说明。用更好的解决方案替换解决方案。