iOS Metal:Jaggies 抗锯齿

Joh*_*ohn 5 opengl-es objective-c antialiasing ios metal

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

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

[renderEncoder drawIndexedPrimitives:MTLPrimitiveTypeTriangleStrip
                              indexCount:self.indexCount
                               indexType:MTLIndexTypeUInt16
                             indexBuffer:self.indicesBuffer
                       indexBufferOffset:0];
Run Code Online (Sandbox Code Playgroud)

其中圆的 vertexBuffer 和 IndexBuffer 是通过计算创建的

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

然后创建vertexBuffer和indicesBuffer

self.vertexBuffer = [device newBufferWithBytes:vertexArrayPtr
                                            length:vertexDataSize
                                           options:MTLResourceOptionCPUCacheModeDefault];
self.indicesBuffer = [device newBufferWithBytes:indexArrayPtr
                                             length:indicesDataSize 
                                            options:MTLResourceOptionCPUCacheModeDefault];
Run Code Online (Sandbox Code Playgroud)

结果是这样的:

金属半圆

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

有什么建议来解决这个问题吗?


根据 warrenm 的建议,我应该将 CAMetalLayer 的可绘制大小设置为屏幕大小 x 比例。有改进:

在此输入图像描述


warrenm 的另一个建议,使用 MTKView 并设置 sampleCount = 4 解决了问题: 在此输入图像描述

war*_*enm 4

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

\n\n

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

\n\n

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

\n\n

为了更精细地采样,我们可以选择多种抗锯齿技术,但其中最简单的可能是多重采样抗锯齿 (MSAA),这是一种硬件功能\xe2\x80\x94,顾名思义\xe2\x80\x94沿图元边缘的每个像素有多个样本,以减少锯齿的锯齿效果。

\n\n

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

\n\n

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

\n