如何在Metal中实现水平最近邻、垂直线性的放大采样滤镜
Metal 内置的采样器不支持为水平、垂直两个轴单独配置不同的放大过滤规则,你可以通过手动计算采样的方式实现需求,修改后的片段着色器代码如下:
fragment float4 samplingShader(RasterizerData in [[stage_in]], texture2d<half> colorTexture [[ texture(AAPLTextureIndexBaseColor) ]]) { // 取纹理的像素尺寸 uint texWidth = colorTexture.get_width(); uint texHeight = colorTexture.get_height(); // 将归一化UV转为像素坐标 float2 pixelUV = in.textureCoordinate * float2(texWidth, texHeight); // 水平方向取最近整数值,实现nearest效果 float xNearest = round(pixelUV.x); // 垂直方向保留小数,用于线性插值 float yFloor = floor(pixelUV.y); float yFrac = pixelUV.y - yFloor; // 处理纹理边界(按钳位到边缘规则处理,可按需调整为重复等其他规则) xNearest = clamp(xNearest, 0.0f, float(texWidth - 1)); yFloor = clamp(yFloor, 0.0f, float(texHeight - 2)); // 分别采样垂直方向两个相邻纹素,水平方向统一用计算好的xNearest half4 sample0 = colorTexture.read(uint2(xNearest, yFloor)); half4 sample1 = colorTexture.read(uint2(xNearest, yFloor + 1)); // 垂直方向线性混合得到最终颜色 half4 colorSample = mix(sample0, sample1, half(yFrac)); return float4(colorSample); }
上述实现的性能开销和原生线性采样基本一致,不会引入明显的性能损耗。如果需要适配其他纹理寻址模式,调整坐标边界处理的逻辑即可。
替代方案
如果你的应用场景中使用的是静态不变的纹理,也可以提前在CPU侧预先生成垂直方向做过线性插值处理的纹理,后续直接使用mag_filter::nearest的采样器采样即可,这种方案可以降低片段着色器的计算量,但仅适用于纹理内容不会动态更新的场景。
内容的提问来源于stack exchange,提问作者Scaroth
相关产品推荐
相关产品推荐

