iOS 金属:锯齿消除锯齿
iOS Metal: Jaggies Anit-aliasing
我试图用 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 scale。有改进:
warrenm 的另一个建议,使用 MTKView 并设置 sampleCount = 4 解决了这个问题:
这里有几件事需要考虑。首先,您需要确保(如果可能)要栅格化的网格大小与将在其上查看的显示器的分辨率相匹配。其次,您可能需要使用亚像素技术来获得额外的平滑度,因为光栅技术往往会对连续函数进行欠采样。
在 Metal 中,我们将渲染图像大小与显示器匹配的方式是确保 Metal 层的 drawable 大小 与 像素尺寸相匹配 它将占据屏幕。直接使用 CAMetalLayer
时,图层的可绘制大小的默认行为是图层边界的大小乘以图层的 contentsScale
属性。将后者设置为合成图层的 UIScreen
的 scale
将使图层的尺寸与屏幕的像素相匹配(忽略可能应用于图层或其视图层次结构的其他转换)。
当使用 MTKView
时,autoResizeDrawable
属性 确定视图是否自动管理其图层的可绘制大小。这是默认行为,但如果将此 属性 设置为 NO
,则可以手动将可绘制大小设置为其他值(例如,在片段绑定时使用自适应分辨率渲染)。
为了更精细地采样,我们可以在任意数量的抗锯齿技术中进行选择,但也许其中最简单的是多重采样抗锯齿 (MSAA),这是一种硬件功能,顾名思义,它采用多个采样沿图元边缘的每个像素,以减少锯齿状锯齿效果。
在 Metal 中,使用 MSAA 需要在渲染管线状态和用于渲染的纹理上设置多重采样状态(即样本计数)。 MSAA 是一个两步过程,其中渲染目标可以保存每个像素的多个片段的数据,然后 resolve 步骤将这些样本组合成每个像素的最终颜色.使用 CAMetalLayer
(或在屏幕外绘图)时,您必须为每个活动的 color/depth 附件创建类型 MTLTextureType2DMultisample
的纹理。这些纹理被配置为各自color/depth附件的texture
属性,resolveTexture
属性设置为MTLTextureType2D
类型的纹理,其中解析了 MSAA 目标。
当使用 MTKView
时,只需在视图上设置 sampleCount
以匹配渲染管道描述符的 sampleCount
就足以让 MetalKit 创建和管理适当的资源。默认情况下,您从视图收到的渲染过程描述符会将内部管理的 MSAA 颜色目标设置为主要颜色附件,并将当前可绘制对象的纹理设置为该附件的解析纹理。这样,只需几行代码就可以使用 MetalKit 启用 MSAA。
我试图用 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 scale。有改进:
warrenm 的另一个建议,使用 MTKView 并设置 sampleCount = 4 解决了这个问题:
这里有几件事需要考虑。首先,您需要确保(如果可能)要栅格化的网格大小与将在其上查看的显示器的分辨率相匹配。其次,您可能需要使用亚像素技术来获得额外的平滑度,因为光栅技术往往会对连续函数进行欠采样。
在 Metal 中,我们将渲染图像大小与显示器匹配的方式是确保 Metal 层的 drawable 大小 与 像素尺寸相匹配 它将占据屏幕。直接使用 CAMetalLayer
时,图层的可绘制大小的默认行为是图层边界的大小乘以图层的 contentsScale
属性。将后者设置为合成图层的 UIScreen
的 scale
将使图层的尺寸与屏幕的像素相匹配(忽略可能应用于图层或其视图层次结构的其他转换)。
当使用 MTKView
时,autoResizeDrawable
属性 确定视图是否自动管理其图层的可绘制大小。这是默认行为,但如果将此 属性 设置为 NO
,则可以手动将可绘制大小设置为其他值(例如,在片段绑定时使用自适应分辨率渲染)。
为了更精细地采样,我们可以在任意数量的抗锯齿技术中进行选择,但也许其中最简单的是多重采样抗锯齿 (MSAA),这是一种硬件功能,顾名思义,它采用多个采样沿图元边缘的每个像素,以减少锯齿状锯齿效果。
在 Metal 中,使用 MSAA 需要在渲染管线状态和用于渲染的纹理上设置多重采样状态(即样本计数)。 MSAA 是一个两步过程,其中渲染目标可以保存每个像素的多个片段的数据,然后 resolve 步骤将这些样本组合成每个像素的最终颜色.使用 CAMetalLayer
(或在屏幕外绘图)时,您必须为每个活动的 color/depth 附件创建类型 MTLTextureType2DMultisample
的纹理。这些纹理被配置为各自color/depth附件的texture
属性,resolveTexture
属性设置为MTLTextureType2D
类型的纹理,其中解析了 MSAA 目标。
当使用 MTKView
时,只需在视图上设置 sampleCount
以匹配渲染管道描述符的 sampleCount
就足以让 MetalKit 创建和管理适当的资源。默认情况下,您从视图收到的渲染过程描述符会将内部管理的 MSAA 颜色目标设置为主要颜色附件,并将当前可绘制对象的纹理设置为该附件的解析纹理。这样,只需几行代码就可以使用 MetalKit 启用 MSAA。