我花了一些时间,试图了解苹果公司的金属图形API的夜晚。我遇到了一个令人沮丧的问题,因此必须缺少一些基本的东西:仅当禁用深度测试或将深度功能更改为“更大”时,我才能使渲染的对象显示在屏幕上。可能出什么问题了?另外,我可以为了调试这个问题检查什么样的事情?
这是我在做什么:
1)我正在使用SDL创建我的窗口。设置Metal时,我手动创建一个CAMetalLayer并将其插入到图层层次结构中。需要明确的是,我没有使用MTKView,也不想使用MTKView。尽可能远离Objective-C和Cocoa似乎是将应用程序编写为跨平台的最佳策略。目的是使用SDL和可在运行时交换的渲染引擎编写与平台无关的C ++代码。这个界面的背后是所有苹果专用的代码将生活。但是,我强烈怀疑出现问题的部分原因与设置层有关:
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];
Run Code Online (Sandbox Code Playgroud)
2)I创建深度纹理深度缓冲器来使用。我的理解是,此步骤在Metal中是必需的。虽然,在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";
Run Code Online (Sandbox Code Playgroud)
3)创建将在渲染时设定的深度模版状态对象:
MTLDepthStencilDescriptor *depthDescriptor = [[MTLDepthStencilDescriptor alloc] init];
depthDescriptor.depthWriteEnabled = YES;
depthDescriptor.depthCompareFunction = MTLCompareFunctionLess;
_depthState = [device newDepthStencilStateWithDescriptor:depthDescriptor];
Run Code Online (Sandbox Code Playgroud)
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;
Run Code Online (Sandbox Code Playgroud)
5)最后,在渲染时,我设置了深度模版对象绘制我的对象之前:
[_encoder setDepthStencilState:_depthState];
Run Code Online (Sandbox Code Playgroud)
请注意,如果我进入第3步,并将depthCompareFunction更改为MTLCompareFunctionAlways或MTLCompareFunctionGreater,则我会在屏幕上看到多边形,但是(预期)排序不正确。如果我将depthCompareFunction设置为MTLCompareFunctionLess,那么我只会看到背景色。它的作用就好像所有的片段在任何时候都不能深度测试。
金属API验证报告没有错误,也没有警告...
我已经尝试了各种东西,如深度模板纹理格式设置的组合并没有取得任何向前进展。老实说,我不知道下一个尝试的东西。
编辑:在Xcode GPU帧捕捉显示我的多边形的绿色轮廓,但没有那些片段的实际绘制。
编辑2:我已经知道Metal 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.
Run Code Online (Sandbox Code Playgroud)
当我遇到这两个警告时,我得到了这两个错误。(警告和错误似乎是相互矛盾的。)
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.'
Run Code Online (Sandbox Code Playgroud)
编辑3:当我运行样品金属的应用程序和使用GPU帧捕获工具然后我看到深度缓冲器的灰度表示和所渲染的对象是清晰可见的。这不会发生我的应用程序。在那里,GPU帧捕获工具始终将深度缓冲区显示为纯白色图像。
好的,我知道了。我将在此处发布答案以帮助下一个人。有没有问题写深度缓存。这解释了为什么花时间浪费在深度纹理和深度模板状态设置上却无济于事。
问题出在用于金属归一化设备坐标与OpenGL的坐标系统的差异。在Metal中,NDC在[-1,+ 1] x [-1,+ 1] x [0,1]空间中。在OpenGL中,NDC是[-1,+ 1]×[-1,+ 1]×[-1,+ 1]。如果我简单地使用glm :: perspective生成的投影矩阵并将其推过Metal,那么结果将不会达到预期。为了补偿使用Metal渲染时的NDC空间差异,必须将投影矩阵与对角线上的(1,1,0.5,1)的缩放矩阵左乘。
我发现这些链接对您有所帮助:1. http://blog.athenstean.com/post/135771439196/from-opengl-to-metal-the-projection-matrix 2. http://www.songho.ca/opengl /gl_projectionmatrix.html
编辑:用更完整和准确的解释代替了解释。更换一个更好的解决方案。