我正在使用 SceneKit shader modifiers 向我的应用添加一些视觉元素|像这样:
// A SceneKit scene with orthographic projection
let shaderBundle = Bundle(for: Self.self)
let shaderUrl = shaderBundle.url(forResource: "MyShader.frag", withExtension: nil)!
let shaderString = try! String(contentsOf: shaderUrl)
let plane = SCNPlane(width: 512, height: 512) // 1024x1024 pixels on devices with x2 screen resolution
plane.firstMaterial!.shaderModifiers = [SCNShaderModifierEntryPoint.fragment: shaderString]
let planeNode = SCNNode(geometry: plane)
rootNode.addChildNode(planeNode)
问题是性能低下,因为 SceneKit 正在煞费苦心地渲染筛选着色器的平面的每个像素。如何降低着色器的分辨率以保持平面大小不变?
我已经尝试让 plane
变小,并在 planeNode
上使用放大比例变换但没有结果,着色器的再现仍然像以前一样非常详细。
使用 plane.firstMaterial!.diffuse.contentsTransform
也没有帮助(或者我做错了)。
我知道我可以使全局 SCNView
变小,然后应用仿射缩放变换,如果该着色器是场景中唯一的节点,但它不是,还有其他节点(不是着色器) ) 在同一场景中,我宁愿避免以任何方式改变它们的外观。
最佳答案
似乎我通过将 SceneKit 场景嵌套在顶层 SceneKit 场景显示的 SpriteKit 场景中,使用一种“渲染到纹理”的方法设法解决了这个问题。
更详细地说,SCNNode
的以下子类在 SpriteKit 的 SK3DNode
中放置一个缩小的着色器平面,然后采用该 SK3DNode
和将其作为 SceneKit 的 SKScene
放入 SpriteKit 场景中,然后使用该 SKScene
作为放入顶级 SceneKit 场景中的放大平面的漫反射内容。
奇怪的是,为了保持原始分辨率,我需要使用 scaleFactor*2
,所以为了将渲染分辨率减半(通常是比例因子 0.5),我实际上需要使用 scaleFactor = 1
.
如果有人碰巧知道这种奇怪行为的原因或解决方法,请在评论中告诉我。
import Foundation
import SceneKit
import SpriteKit
class ScaledResolutionFragmentShaderModifierPlaneNode: SCNNode {
private static let nestedSCNSceneFrustumLength: CGFloat = 8
// For shader parameter input
let shaderPlaneMaterial: SCNMaterial
// shaderModifier: the shader
// planeSize: the size of the shader on the screen
// scaleFactor: the scale to be used for the shader's rendering resolution; the lower, the faster
init(shaderModifier: String, planeSize: CGSize, scaleFactor: CGFloat) {
let scaledSize = CGSize(width: planeSize.width*scaleFactor, height: planeSize.height*scaleFactor)
// Nested SceneKit scene with orthographic projection
let nestedSCNScene = SCNScene()
let camera = SCNCamera()
camera.zFar = Double(Self.nestedSCNSceneFrustumLength)
camera.usesOrthographicProjection = true
camera.orthographicScale = Double(scaledSize.height/2)
let cameraNode = SCNNode()
cameraNode.camera = camera
cameraNode.simdPosition = simd_float3(x: 0, y: 0, z: Float(Self.nestedSCNSceneFrustumLength/2))
nestedSCNScene.rootNode.addChildNode(cameraNode)
let shaderPlane = SCNPlane(width: scaledSize.width, height: scaledSize.height)
shaderPlaneMaterial = shaderPlane.firstMaterial!
shaderPlaneMaterial.shaderModifiers = [SCNShaderModifierEntryPoint.fragment: shaderModifier]
let shaderPlaneNode = SCNNode(geometry: shaderPlane)
nestedSCNScene.rootNode.addChildNode(shaderPlaneNode)
// Intermediary SpriteKit scene
let nestedSCNSceneSKNode = SK3DNode(viewportSize: scaledSize)
nestedSCNSceneSKNode.scnScene = nestedSCNScene
nestedSCNSceneSKNode.position = CGPoint(x: scaledSize.width/2, y: scaledSize.height/2)
nestedSCNSceneSKNode.isPlaying = true
let intermediarySKScene = SKScene(size: scaledSize)
intermediarySKScene.backgroundColor = .clear
intermediarySKScene.addChild(nestedSCNSceneSKNode)
let intermediarySKScenePlane = SCNPlane(width: scaledSize.width, height: scaledSize.height)
intermediarySKScenePlane.firstMaterial!.diffuse.contents = intermediarySKScene
let intermediarySKScenePlaneNode = SCNNode(geometry: intermediarySKScenePlane)
let invScaleFactor = 1/Float(scaleFactor)
intermediarySKScenePlaneNode.simdScale = simd_float3(x: invScaleFactor, y: invScaleFactor, z: 1)
super.init()
addChildNode(intermediarySKScenePlaneNode)
}
required init?(coder: NSCoder) {
fatalError()
}
}
关于ios - 如何以较低的分辨率渲染 SceneKit 着色器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62539114/