ios - 如何以较低的分辨率渲染 SceneKit 着色器?

标签 ios swift shader scenekit metal

我正在使用 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/

相关文章:

iphone - 在 View Controller 中引用自定义 View 时出错

swift - 类型没有下标成员

OpenGL Barrier() 函数和发散流控制

opengl - imageStore 是原子的吗?

ios - 有没有办法遍历字典?

ios - 在 swift 中检查 xml 元素是否为空的 EXC_BAD_INSTRUCTION 错误

ios - 使用 iOS 中的共享扩展从邮件应用程序共享附件

Swift 4 Codable - API 有时提供 Int 有时提供 String

swift - 布局约束和 NSAnimationContext 的动画不正确

c++ - GLSL 着色器类帧率问题