一般情况下,如果没有使用Metal中的
可变光栅化率或其他地方的
可变速率着色这种相对较新的GPU功能,您无法使场景中的一个对象以不同于场景其余部分的分辨率运行其片段着色器。
对于这种情况,根据您的设置,您可能可以使用
SCNTechnique在单独的通道中以不同的分辨率呈现平面,然后将其合成回场景中,就像某些游戏引擎
以较低的分辨率呈现粒子以节省填充率一样。以下是一个示例。
首先,您需要在项目中添加一个Metal文件(如果已经有一个,请添加到其中),其中包含以下内容:
#include <SceneKit/scn_metal>
struct QuadVertexIn {
float3 position [[ attribute(SCNVertexSemanticPosition) ]];
float2 uv [[ attribute(SCNVertexSemanticTexcoord0) ]];
};
struct QuadVertexOut {
float4 position [[ position ]];
float2 uv;
};
vertex QuadVertexOut quadVertex(QuadVertexIn v [[ stage_in ]]) {
QuadVertexOut o;
o.position = float4(v.position.x, -v.position.y, 1, 1);
o.uv = v.uv;
return o;
}
constexpr sampler compositingSampler(coord::normalized, address::clamp_to_edge, filter::linear);
fragment half4 compositeFragment(QuadVertexOut v [[ stage_in ]], texture2d<half, access::sample> compositeInput [[ texture(0) ]]) {
return compositeInput.sample(compositingSampler, v.uv);
}
然后,在您的SceneKit代码中,您可以像这样设置和应用该技术:
let technique = SCNTechnique(dictionary: [
"passes": ["drawLowResStuff":
["draw": "DRAW_SCENE",
// only draw nodes that are in this category
"includeCategoryMask": 2,
"colorStates": ["clear": true, "clearColor": "0.0"],
"outputs": ["color": "lowResStuff"]],
"drawScene":
["draw": "DRAW_SCENE",
// don’t draw nodes that are in the low-res-stuff category
"excludeCategoryMask": 2,
"colorStates": ["clear": true, "clearColor": "sceneBackground"],
"outputs": ["color": "COLOR"]],
"composite":
["draw": "DRAW_QUAD",
"metalVertexShader": "quadVertex",
"metalFragmentShader": "compositeFragment",
// don’t clear what’s currently there (the rest of the scene)
"colorStates": ["clear": false],
// use alpha blending
"blendStates": ["enable": true, "colorSrc": "srcAlpha", "colorDst": "oneMinusSrcAlpha"],
// supply the lowResStuff render target to the fragment shader
"inputs": ["compositeInput": "lowResStuff"],
// draw into the main color render target
"outputs": ["color": "COLOR"]]
],
"sequence": ["drawLowResStuff", "drawScene", "composite"],
"targets": ["lowResStuff": ["type": "color", "scaleFactor": 0.5]]
])
// mark the plane node as belonging to the category of stuff that gets drawn in the low-res pass
myPlaneNode.categoryBitMask = 2
// apply the technique to the scene view
mySceneView.technique = technique
使用两个具有相同纹理的球体作为测试场景,并将 scaleFactor
设置为0.25而不是0.5以夸大效果,结果如下所示。
如果您更喜欢清晰的像素化而不是上面示例中的模糊调整大小,请在Metal代码中将
filter::linear
更改为
filter::nearest
。此外,请注意,合成的低分辨率内容未考虑深度缓冲区,因此,如果您的平面应该出现在其他对象“后面”,则需要在合成函数中进行更多的工作来修复这个问题。
plane.firstMaterial!.diffuse.contents
设置为一个 16x16 的UIImage
没有任何效果(如果我禁用着色器修改器,我可以很好地看到低分辨率纹理)。 - Desmond Hume