iOS金属:锯齿状和锯齿状别名

iOS金属:锯齿状和锯齿状别名,ios,objective-c,opengl-es,metal,antialiasing,Ios,Objective C,Opengl Es,Metal,Antialiasing,我试图用RenderCoder的DrawIndexedPrimitions画一个半圆 [renderEncoder setVertexBuffer:self.vertexBuffer offset:0 atIndex:0]; [renderEncoder drawIndexedPrimitives:MTLPrimitiveTypeTriangleStrip indexCount:self.indexCount

我试图用RenderCoder的DrawIndexedPrimitions画一个半圆

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

[renderEncoder drawIndexedPrimitives:MTLPrimitiveTypeTriangleStrip
                              indexCount:self.indexCount
                               indexType:MTLIndexTypeUInt16
                             indexBuffer:self.indicesBuffer
                       indexBufferOffset:0];
其中,圆的vertexBuffer和IndicatesBuffer是通过计算创建的

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;
    }
}
结果如下:

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

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


根据warrenm的建议,我应该将CametLayer的drawableSize设置为屏幕大小x比例。有以下改进:


warrenm的另一个建议是使用MTKView并将sampleCount设置为4,解决了这个问题:
这里有两件事要考虑。首先,您需要确保(在可能的情况下)要栅格化的网格大小与将在其上查看的显示器的分辨率相匹配。其次,您可能需要使用亚像素技术来弥补额外的平滑度,因为光栅技术往往对连续函数采样不足

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

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

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

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


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

看起来金属层的可绘制尺寸明显小于视图的像素尺寸。如果您直接使用的是
CametLayer
,您是否尝试将其
drawableSize
属性设置为视图大小乘以屏幕比例?您还可以通过将渲染管道的
sampleCount
设置为4,并渲染到适当大小的渲染目标中/从中解析来启用MSAA,但我认为,通过将渲染目标大小与视图的像素尺寸相匹配,即使不使用MSAA,您也会看到显著的改进。@warrenm谢谢您的帮助。是的,我一直在使用Cametlayer,在您提到的设置drawableSize后,有了显著的改进。我想尝试将pipelineDescriptor的sampleCount设置为4,但在尝试运行时崩溃,我想我需要MTKView来匹配renderPipeline和renderTarget之间的sampleCount?没错;视图的采样数需要与渲染管道状态的采样数匹配。如果您使用的是
MTKView
,它将为您创建适当的多采样纹理;您只需设置样本数。很高兴这对您有效。在此之前,我已经添加了一个答案,列出了关于幕后发生的事情的更多细节,希望它能帮助那些将来访问这个问题的人。
self.vertexBuffer = [device newBufferWithBytes:vertexArrayPtr
                                            length:vertexDataSize
                                           options:MTLResourceOptionCPUCacheModeDefault];
self.indicesBuffer = [device newBufferWithBytes:indexArrayPtr
                                             length:indicesDataSize 
                                            options:MTLResourceOptionCPUCacheModeDefault];