SceneKit/ARKit - 渲染场景的深度图?

3
我有一个包含SCNNode模型和一个相机的场景。
我想生成一个深度图像渲染我的场景(从相机的视角) - 类似于这样:enter image description here
如何实现这个目标?目前正在研究SCNTechnique,但没有找到很多资源。

只是头脑风暴。一个包含金属着色器的SCNProgram怎么样?它可以与Scenekit结合使用,您可以向其添加任何信息,比如深度缓冲信息(或距离)。这个想法很棒。好问题。 - undefined
尝试使用SCNRenderer,查看这篇文章中如何将其用于您所需的相机场景:https://qiita.com/akira108/items/a743138fca532ee193fe关于pointOfView的更多信息,请参阅此处:https://magicien.github.io/JSceneKit/class/js/SceneKit/SCNRenderer.js~SCNRenderer.html#instance-set-pointOfView - undefined
1个回答

2
这里可能不是最终解决方案,但它可以给你一个指导,如何实现深度渲染技术。
注意:这里的一切都取决于相机与几何体之间的距离。非常简化的实现。它使用了一个SCNProgram()来处理材质。
下面是GameViewController.swift的代码:
class GameViewController: UIViewController {
    
    @IBOutlet weak var sceneView            : SCNView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // create a new scene
        let scene = SCNScene()
        
        // create and add a camera to the scene
        let cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        cameraNode.camera?.automaticallyAdjustsZRange = true
        scene.rootNode.addChildNode(cameraNode)
        
        // place the camera
        // cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)
        cameraNode.position = SCNVector3(x: 0, y: 0, z: 100)
        
        // create and add a light to the scene
        let lightNode = SCNNode()
        lightNode.light = SCNLight()
        lightNode.light!.type = .omni
        lightNode.position = SCNVector3(x: 0, y: 10, z: 10)
        scene.rootNode.addChildNode(lightNode)
        
        // create and add an ambient light to the scene
        let ambientLightNode = SCNNode()
        ambientLightNode.light = SCNLight()
        ambientLightNode.light!.type = .ambient
        ambientLightNode.light!.color = UIColor.darkGray
        scene.rootNode.addChildNode(ambientLightNode)
        
        // set the scene to the view
        sceneView.scene = scene
        
        // allows the user to manipulate the camera
        sceneView.allowsCameraControl = true
        
        // show statistics such as fps and timing information
        sceneView.showsStatistics = true
        
        // configure the view
        sceneView.backgroundColor = UIColor.black
        
        sceneView.scene?.lightingEnvironment.contents   = UIColor.black
        sceneView.scene?.background.contents            = UIColor.black
        
        // self.setupCube()
        self.setupRing()
        self.setupStick()
    }
    
    
    func setupCube() {
        let cube = SCNBox(width: 20.0, height: 20.0, length: 20.0, chamferRadius: 2.5)
        let cubeNode = SCNNode(geometry: cube)
        cubeNode.geometry?.firstMaterial =  depthMaterial() // chromeMaterialMetal()
        sceneView.scene?.rootNode.addChildNode(cubeNode)
    }
    
    func setupRing() {
        let ring = SCNTorus(ringRadius: 20.0, pipeRadius: 4.0)
        let ringNode = SCNNode(geometry: ring)
        ringNode.position = SCNVector3(0.0, +10.0, 20.0)
        ringNode.geometry?.firstMaterial =  depthMaterial() // chromeMaterialMetal()
        sceneView.scene?.rootNode.addChildNode(ringNode)
        
        let ring2 = SCNTorus(ringRadius: 20.0, pipeRadius: 4.0)
        let ringNode2 = SCNNode(geometry: ring2)
        ringNode2.position = SCNVector3(0.0, -10.0, 20.0)
        ringNode2.geometry?.firstMaterial =  depthMaterial() // chromeMaterialMetal()
        sceneView.scene?.rootNode.addChildNode(ringNode2)
    }
    
    func setupStick() {
        let stick = SCNCylinder(radius: 5.0, height: 70.0)
        let stickNode = SCNNode(geometry: stick)
        stickNode.position = SCNVector3(0.0, 0.0, 20.0)
        stickNode.geometry?.firstMaterial =  depthMaterial() // chromeMaterialMetal()
        sceneView.scene?.rootNode.addChildNode(stickNode)
    }
    
    
    override var prefersStatusBarHidden: Bool {
        return true
    }
    
    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        if UIDevice.current.userInterfaceIdiom == .phone {
            return .allButUpsideDown
        } else {
            return .all
        }
    }
    
    func depthMaterial() -> SCNMaterial {
        
        // let depthValue: Float = 100.0 // Adjust this value based on your scene's depth range
        
        let sceneProgramDepth = SCNProgram()
        sceneProgramDepth.vertexFunctionName    = "myVertexDepth"
        sceneProgramDepth.fragmentFunctionName  = "myFragmentDepth"
        // sceneProgramDepth.setValue(depthValue, forKey: "maxDepth") // to be implemented, ToDo
        
        let material = SCNMaterial()
        material.name                           = "metal"
        material.program = sceneProgramDepth // doing this will replace the entire built-in SceneKit shaders for that object.
        
        return material
    }
    
}

这里是深度着色器(以及一些变体)shaders.metal
// Default Metal Header for SCNProgram
#include <metal_stdlib>
using namespace metal;
#include <SceneKit/scn_metal>

// Nodebuffer (you only need the enabled Matrix floats)
struct MyNodeBuffer {
    float4x4 modelViewTransform; // required
    float4x4 normalTransform; // required
    // float4x4 inverseModelViewTransform;
    // float4x4 modelTransform;
    // float4x4 inverseModelTransform;
    // float4x4 modelViewProjectionTransform;
    // float4x4 inverseModelViewProjectionTransform;
};

// Input Struct
typedef struct {
    float3 position [[attribute(SCNVertexSemanticPosition)]];
} MyDepthInput;

// Struct filled by the Vertex Shader
struct SimpleVertexDepth {
    float4 position [[position]];
    float depth; // Store depth information
};

// Declare the maximum depth variable // needs to be filed by Scenekit, ToDo
// constant float maxDepth [[function_constant(0)]];

// Simple Vertex Shader
vertex SimpleVertexDepth myVertexDepth(MyDepthInput in [[stage_in]],
                                       constant SCNSceneBuffer& scn_frame [[buffer(0)]],
                                       constant MyNodeBuffer& scn_node [[buffer(1)]])
{
    float4 modelSpacePosition(in.position, 1.0f);
    float4 eyeSpacePosition = scn_node.modelViewTransform * modelSpacePosition;

    // Calculate depth from the vertex position in eye space
    float depth = length(eyeSpacePosition.xyz);

    SimpleVertexDepth out;
    out.position = scn_frame.projectionTransform * eyeSpacePosition;
    out.depth = depth;
    return out;
}

// Simple Depth
// fragment half4 myFragmentDepth(SimpleVertexDepth in [[stage_in]])
// {
//     // Map depth to grayscale color inversely
//     float gray = 1.0 - saturate(in.depth / 100); // Invert depth mapping
//     return half4(gray, gray, gray, 1.0);
// }

// Another Variation (Better)
fragment half4 myFragmentDepth(SimpleVertexDepth in [[stage_in]])
{
    // Define a contrast factor to control the contrast
    float contrastFactor = 3.0; // Adjust this value to control contrast
    
    // Map depth to grayscale color with increased contrast
    float gray = 1.0 - pow(saturate(in.depth / 100), contrastFactor);
    
    return half4(gray, gray, gray, 1.0);
}

// Far away is brighter
// fragment half4 myFragmentDepth(SimpleVertexDepth in [[stage_in]])
// {
//     // Map depth to grayscale color
//     float gray = in.depth / 100; // maxDepth; // You can set maxDepth based on your scene
//     return half4(gray, gray, gray, 1.0);
// }

// Better Variation
// fragment half4 myFragmentDepth(SimpleVertexDepth in [[stage_in]])
// {
//     // Define a contrast factor to control the contrast level
//     float contrastFactor = 3.0; // Adjust this value to control contrast
//
//     // Map depth to grayscale color with increased contrast
//     float gray = pow(in.depth / 100, contrastFactor); // maxDepth is the maximum depth value in your scene
//     return half4(gray, gray, gray, 1.0);
// }

我目前偏爱的一个名字叫做“另一种变体(更好)”。结果看起来是这样的:

SceneKit Depth Render Shader

随意尝试那些东西吧。希望我能以某种方式帮到你。(如果你找到更好的解决方案,我会非常感兴趣看到它。) 附注:作为一个草稿项目,使用SceneKit默认模板和太空飞船,然后删除太空飞船 :)

这里的渲染会受到场景中的光照影响吗?我希望有一种方法可以不依赖于光照。基本上只是根据每个点距离相机的远近来渲染深度图。 - undefined
1
不,这个技术与灯光无关。它完全基于与摄像机的距离。 - undefined
1
谢谢,Zay,明天我会试一试这个! - undefined
1
关于你提到的关于光照的问题:GameViewController中添加的灯光只是苹果模板中的默认灯光。你可以忽略或删除它们。 - undefined
非常感谢你的慷慨奖励! - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接