【问题标题】:iOS Metal: Jaggies Anit-aliasingiOS Metal:锯齿状抗锯齿
【发布时间】:2019-12-17 05:09:06
【问题描述】:

我试图用 renderEncoder 的 drawIndexedPrimitives 画一个半圆

[renderEncoder setVertexBuffer:self.vertexBuffer offset:0 atIndex:0];

[renderEncoder drawIndexedPrimitives:MTLPrimitiveTypeTriangleStrip
                              indexCount:self.indexCount
                               indexType:MTLIndexTypeUInt16
                             indexBuffer:self.indicesBuffer
                       indexBufferOffset:0];

圆的vertexBuffer和indicesBuffer是通过计算创建的

int segments = 10;


float vertices02[ (segments +1)* (3+4)];
vertices02[0] = centerX;
vertices02[1] = centerY;
vertices02[2] = 0;

//3, 4, 5, 6 are RGBA
vertices02[3] = 1.0;
vertices02[4] = 0;
vertices02[5] = 0.0;
vertices02[6] = 1.0;    

uint16_t indices[(segments -1)*3];

for (int i = 1; i <= segments ; i++){
    float degree = (i -1) * (endDegree - startDegree)/ (segments -1) + startDegree;

    vertices02[i*7] = (centerX + cos([self degreesToRadians:degree])*radius);
    vertices02[i*7 +1] = (centerY +  sin([self degreesToRadians:degree])*radius);
    vertices02[i*7 +2] = 0;

    vertices02[i*7 +3] = 1.0;
    vertices02[i*7 +4] = 0;
    vertices02[i*7 +5] = 0.0;
    vertices02[i*7 +6] = 1.0;

    if (i < segments){
        indices[(i-1)*3 + 0] = 0;
        indices[(i-1)*3 + 1] = i;
        indices[(i-1)*3 + 2] = i+1;
    }
}

所以我将 9 个三角形组合成一个 180 度的圆。

然后创建vertexBuffer和indicesBuffer

self.vertexBuffer = [device newBufferWithBytes:vertexArrayPtr
                                            length:vertexDataSize
                                           options:MTLResourceOptionCPUCacheModeDefault];
self.indicesBuffer = [device newBufferWithBytes:indexArrayPtr
                                             length:indicesDataSize 
                                            options:MTLResourceOptionCPUCacheModeDefault];

结果是这样的:

我相信这是来自 iOS 的 Metal 的抗锯齿问题。我曾经使用相同的技术在 OpenGL 中创建半圆,但边缘更平滑。

有解决这个问题的建议吗?


warrenm 建议,我应该将 CAMetalLayer 的 drawableSize 设置为 screenSize x 比例。有改进:


warrenm 的另一个建议,使用 MTKView 并设置 sampleCount = 4 解决了这个问题:

【问题讨论】:

  • 看起来你的金属层的可绘制尺寸明显小于你的视图的像素尺寸。如果您直接使用CAMetalLayer,您是否尝试过将其drawableSize 属性设置为视图大小乘以屏幕比例?
  • 您还可以通过将渲染管道的 sampleCount 设置为 4 来启用 MSAA,并从适当大小的渲染目标进行渲染/解析,但我认为通过匹配您会看到显着的改进将渲染目标大小调整为视图的像素尺寸,即使不使用 MSAA。
  • @warrenm 感谢您的帮助。是的,我一直在使用 CAMetalLayer,并且在您提到的设置 drawableSize 后有了显着的改进。我想尝试将 pipelineDescriptor 的 sampleCount 设置为 4,但是当我尝试运行时崩溃,我想我需要 MTKView 来匹配 renderPipeline 和 renderTarget 之间的 sampleCount?
  • 没错;视图的样本数需要与渲染管线状态的样本数相匹配。如果您使用MTKView,它将为您创建适当的多重采样纹理;您只需要设置样本数。
  • 很高兴这对你有用。在此之前我已经添加了一个答案,其中列出了有关幕后发生的事情的更多详细信息,希望它对将来访问此问题的人有所帮助。

标签: ios objective-c opengl-es metal antialiasing


【解决方案1】:

这里有几件事需要考虑。首先,您需要确保(在可能的情况下)您要栅格化的网格的大小与将在其上查看的显示器的分辨率相匹配。其次,您可能需要使用亚像素技术来获得额外的平滑度,因为光栅技术往往会欠采样连续函数。

在 Metal 中,我们将渲染图像大小与显示匹配的方式是确保 Metal 层的 可绘制大小 与它将占据的 像素尺寸 匹配屏幕。当直接使用CAMetalLayer 时,默认行为是图层的可绘制大小为图层边界的大小乘以图层的contentsScale 属性。将后者设置为合成图层的UIScreenscale 将使图层的尺寸与屏幕的像素相匹配(忽略可能应用于图层或其视图层次结构的其他转换)。

使用MTKView 时,autoResizeDrawable 属性确定视图是否自动管理其图层的可绘制大小。这是默认行为,但如果将此属性设置为 NO,则可以手动将可绘制大小设置为其他值(例如,在片段绑定时使用自适应分辨率渲染)。

为了更精细地采样,我们可以在任意数量的抗锯齿技术中进行选择,但其中最简单的可能是多重采样抗锯齿 (MSAA),顾名思义,该硬件功能对每个像素进行多次采样沿着图元的边缘,以减少锯齿的锯齿效果。

在 Metal 中,使用 MSAA 需要在渲染管线状态和用于渲染的纹理上设置多重采样状态(即采样计数)。 MSAA 是一个两步过程,其中渲染目标可以保存每个像素的多个片段的数据,然后 resolve 步骤将这些样本组合成每个像素的最终颜色。使用CAMetalLayer(或离屏绘图)时,您必须为每个活动颜色/深度附件创建MTLTextureType2DMultisample 类型的纹理。这些纹理被配置为它们各自颜色/深度附件的texture 属性,resolveTexture 属性被设置为MTLTextureType2D 类型的纹理,MSAA 目标被解析到其中。

使用MTKView 时,只需在视图上设置sampleCount 以匹配渲染管线描述符的sampleCount,就足以让MetalKit 创建和管理适当的资源。默认情况下,您从视图接收的渲染通道描述符将内部管理的 MSAA 颜色目标设置为主要颜色附件,并将当前可绘制的纹理设置为该附件的解析纹理。这样,使用 MetalKit 启用 MSAA 只需要几行代码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-02-18
    • 2013-01-02
    • 1970-01-01
    • 2016-06-28
    • 2011-07-05
    • 2014-03-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多