如何让SceneKit的SCNTechnique与includeCategoryBitmask配合生效?
用SCNTechnique实现多指定categoryBitmask节点置顶绘制
我需要用SCNTechnique让多个带有指定categoryBitmask(值为2)的SCNNode(角度关节Gizmo)始终绘制在其他节点上方。之前用StackOverflow上Simeon的方法(基于Gizmo.metal)实现了单个节点的效果,但扩展到多节点时,修改plist配置后没有生效。
我尝试在pass_gizmo阶段添加了以下配置:
<key>includesCategoryMask</key> <integer>2</integer> <key>excludesCategoryMask</key> <integer>0</integer> <key>inputs</key>
已知同一时间场景只能用一个SCNTechnique,所以按节点名称区分的方案不适用。项目基于Xcode游戏模板,已添加Gizmo.metal和名为scntec的plist文件,GameViewController实现了SCNSceneRendererDelegate协议,加载plist的代码如下:
// retrieve the SCNView let scnView = self.view as! SCNView scnView.delegate = self // set the scene to the view scnView.scene = scene if let path = Bundle.main.path(forResource: "scntec", ofType: "plist"), let dict = NSDictionary(contentsOfFile: path) { let technique = SCNTechnique(dictionary: dict as! [String : AnyObject]) scnView.technique = technique }
解决方法
正确的SCNTechnique plist配置
要实现多节点按category过滤并置顶,需要构建包含两个核心渲染通道的技术配置:
- 先渲染所有非Gizmo节点(排除categoryBitmask=2的节点)
- 再渲染所有Gizmo节点(仅包含categoryBitmask=2的节点),并依赖第一个通道的输出作为底层画面,保证Gizmo叠加在最上层。
完整的scntec.plist配置示例:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>passes</key> <dict> <!-- 通道1:渲染所有非Gizmo节点 --> <key>pass_scene</key> <dict> <key>draw</key> <string>DRAW_SCENE</string> <key>excludesCategoryMask</key> <integer>2</integer> <key>outputs</key> <dict> <key>color</key> <string>COLOR_TEXTURE</string> </dict> </dict> <!-- 通道2:渲染Gizmo节点,叠加在通道1的结果上 --> <key>pass_gizmo</key> <dict> <key>draw</key> <string>DRAW_SCENE</string> <key>includesCategoryMask</key> <integer>2</integer> <key>inputs</key> <dict> <key>colorSampler</key> <string>COLOR_TEXTURE</string> </dict> <key>outputs</key> <dict> <key>color</key> <string>COLOR</string> </dict> <key>program</key> <string>Gizmo</string> <!-- 对应你的Gizmo.metal程序 --> </dict> </dict> <key>sequence</key> <array> <string>pass_scene</string> <string>pass_gizmo</string> </array> <key>targets</key> <dict> <key>COLOR_TEXTURE</key> <dict> <key>type</key> <string>texture</string> <key>size</key> <string>framebuffer</string> <key>format</key> <string>RGBA8</string> </dict> </dict> <key>symbols</key> <dict> <key>Gizmo</key> <dict> <key>type</key> <string>program</string> <key>metalLibrary</key> <string>Gizmo</string> <key>vertexFunction</key> <string>vertex_gizmo</string> <key>fragmentFunction</key> <string>fragment_gizmo</string> </dict> </dict> </dict> </plist>
关键细节调整
- 通道顺序:必须先渲染普通场景,再渲染Gizmo,确保层级叠加逻辑正确
- Gizmo.metal适配:片元函数需要接收上一通道的场景纹理输入,实现透明叠加效果,示例代码:
#include <metal_stdlib> #include <SceneKit/scn_metal> using namespace metal; struct NodeBuffer { float4x4 modelViewProjectionTransform; }; struct FragmentInput { float4 position [[position]]; float4 color [[color(0)]]; }; fragment float4 fragment_gizmo(FragmentInput in [[stage_in]], texture2d<float, access::sample> colorSampler [[texture(0)]], sampler sampler [[sampler(0)]]) { // 获取上一通道的场景颜色 float2 uv = in.position.xy / scn_framebuffer_size(); float4 sceneColor = colorSampler.sample(sampler, uv); // 叠加Gizmo颜色(保留Gizmo自身透明度) return sceneColor * (1.0 - in.color.a) + in.color * in.color.a; }
节点配置验证
确保所有需要置顶的Gizmo节点的categoryBitmask设置为2:
gizmoNode.categoryBitmask = 2
内容的提问来源于stack exchange,提问作者bhartsb
相关产品推荐
相关产品推荐

