iOS Metal:锯齿锯齿锯齿

时间:2019-12-17 05:09:06

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

我试图用renderEncoder的drawIndexedPrimitives画半个圆

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

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

通过计算创建圆的vertexBuffer和indexsBuffer

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和indexsBuffer

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

结果是这样的:

Half Circle by Metal

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

是否有解决问题的建议?


由warrenm建议,我应该将CAMetalLayer的drawableSize设置为screenSize x scale。有改进之处:

enter image description here


warrenm的另一个建议,使用MTKView并设置sampleCount = 4解决了该问题: enter image description here

1 个答案:

答案 0 :(得分:1)

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

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

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

为了更精细地采样,我们可以在许多种抗锯齿技术中进行选择,但也许其中最简单的是多采样抗锯齿(MSAA),这是一种硬件功能,顾名思义,每个像素都需要多个采样沿着图元的边缘,以减少锯齿的锯齿效应。

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

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