scenekit - SceneKit 在调用 didApplyConstraints 和 willRenderScene 之间做了什么?
问题描述
SceneKit 渲染循环在这里有很好的记录https://developer.apple.com/documentation/scenekit/scnscenerendererdelegate和这里 https://www.raywenderlich.com/1257-scene-kit-tutorial-with-swift-part-4 -渲染循环。然而,这些文档都没有解释 SceneKit 在调用 didApplyConstraints 和 willRenderScene 之间的作用。
我已经修改了我的 SCNSceneRendererDelegate 来测量每次调用之间的时间,我可以看到这两个调用之间经过了大约 5 毫秒。那时它没有运行我的代码,但大概我设置场景的方式的某些方面是创建必须在那里完成的工作。任何深入了解 SceneKit 正在做什么都会非常有帮助。
我自己从 MTKView 的绘图调用(而不是使用 SCNView)调用 SceneKit,以便我可以渲染场景两次。第一个渲染是正常的,第二个使用第一个的深度缓冲区,但只绘制了我想要“发光”到单独颜色缓冲区的场景子集。然后将该颜色缓冲区按比例缩小、高斯模糊、按比例放大,然后在第一个场景的顶部混合(全部使用自定义金属着色器)。
当我引入这个额外的渲染通道时,在 didApplyConstraints 和 willRenderScene 之间花费的 5ms 开始发生。为了控制每个场景中的哪些节点,我在 0 和 1 之间切换少量父节点的不透明度。如果我删除切换不透明度但保留其他所有内容的代码(因此有两个渲染通道,但它们都绘制了所有内容)额外的 5 毫秒消失了,整体帧速率实际上更快,即使正在发生更多的渲染。
我正在 2018 MacBook Pro 上编写针对 MacOS 的 Swift。
更新:mnuages 解释说,改变不透明度会导致 SceneKit 重建场景图,它解释了部分损失的时间。但是,我现在发现我在一次渲染过程中对节点使用自定义 SCNProgram 也会触发 didApplyConstraints 和 willRenderScene 之间的 5ms 暂停。有谁知道这可能是为什么?
这是我设置 SCNProgram 和 SCNMaterial 的代码,都完成了一次:
let device = MTLCreateSystemDefaultDevice()!
let library = device.makeDefaultLibrary()
glowProgram = SCNProgram()
glowProgram.library = library
glowProgram.vertexFunctionName = "emissionGlowVertex"
glowProgram.fragmentFunctionName = "emissionGlowFragment"
...
let glowMaterial = SCNMaterial()
glowMaterial.program = glowProgram
let emissionImageProperty = SCNMaterialProperty(contents: emissionImage)
glowMaterial.setValue(emissionImageProperty, forKey: "tex")
这是我将材料应用于节点的地方:
let nodeWithGeometryClone = nodeWithGeometry.clone()
nodeWithGeometryClone.categoryBitMask = 2
let geometry = nodeWithGeometryClone.geometry!
nodeWithGeometryClone.geometry = SCNGeometry(sources: geometry.sources, elements: geometry.elements)
glowNode.addChildNode(nodeWithGeometryClone)
nodeWithGeometryClone.geometry!.firstMaterial = glowMaterial
发光节点是常规节点的深层克隆,但具有替代 SCNProgram。这是金属代码:
#include <metal_stdlib>
using namespace metal;
#include <SceneKit/scn_metal>
struct NodeConstants {
float4x4 modelTransform;
float4x4 modelViewProjectionTransform;
};
struct EmissionGlowVertexIn {
float3 pos [[attribute(SCNVertexSemanticPosition)]];
float2 uv [[attribute(SCNVertexSemanticTexcoord0)]];
};
struct EmissionGlowVertexOut {
float4 pos [[position]];
float2 uv;
};
vertex EmissionGlowVertexOut emissionGlowVertex(EmissionGlowVertexIn in [[stage_in]],
constant NodeConstants &scn_node [[buffer(1)]]) {
EmissionGlowVertexOut out;
out.pos = scn_node.modelViewProjectionTransform * float4(in.pos, 1) + float4(0, 0, -0.01, 0);
out.uv = in.uv;
return out;
}
constexpr sampler linSamp = sampler(coord::normalized, address::clamp_to_zero, filter::linear);
fragment half4 emissionGlowFragment(EmissionGlowVertexOut in [[stage_in]],
texture2d<half, access::sample> tex [[texture(0)]]) {
return tex.sample(linSamp, in.uv);
}
解决方案
通过更改节点的不透明度,您会使场景图的某些部分无效,这可能会导致渲染器的额外工作。
看看设置相机的categoryBitMask
性能是否更好(它不会修改场景图)会很有趣。
推荐阅读
- export - 验证 Odoo 13 中 one2many 字段列的导出数据
- php - 在语言文件中使用 codeIgniter 常量变量
- angular - HTTP 请求标头不包含 Angular 中的自定义标头
- puppeteer - Puppeteer - 在截屏之前等待生成的页面完全加载
- javascript - 是否可以在反应路由器中创建不是父级子路由的嵌套路由?
- java - JDK 8 在 KeyStore.load 中使用 SHA 哈希算法是否安全?
- sql-server - 生成每个月的股票头寸报告,并在特定月份设置日期变量
- typescript - TypeScript:DefinitelyTyped 中的子函数
- ios - 将 iOS 故事板转换为 tvOS
- c# - 使用执行标量方法检索外键 ID