ios 将金属深度缓冲器与场景工具包渲染集成

bttbmeg0  于 2023-02-10  发布在  iOS
关注(0)|答案(2)|浏览(101)

我正在使用Metal渲染一个带有z缓冲区的场景,现在需要将这个z缓冲区集成到SceneKit的渲染中。但是我不知道如何让SceneKit更好地正确使用这个深度,甚至不100%确定SceneKit希望它的z缓冲区采用什么格式
基于this question,我的理解是SceneKit使用了一个反对数z缓冲区,范围从1(近)到0(远)。然而,我无法让这个工作,我用SceneKit绘制的对象没有正确地考虑深度缓冲区:它们要么始终显示,要么始终隐藏
首先,以下是如何在金属渲染过程中生成z缓冲区纹理:

struct FragmentOut {
    float4 color [[color(0)]];
    float depth [[depth(any)]];
};

fragment FragmentOut metalRenderFragment(const InOut in [[ stage_in ]]) {
    FragmentOut out;
    out.depth = 0; // 0 is far with reverse z buffer

    ...
    
    float cameraSpaceZ = ...; // Computed in shader

    // There constants are taken from SceneKit's camera and inlined here
    const float zNear = 0.0010000000474974513; 
    const float zFar = 1000.0;

    float logDepth = log(z / zNear) / log(zFar / zNear);

    out.depth = 1.0 - logDepth; // Reverse the depth for scenekit

    return out;
}

然后,为了将深度缓冲区集成到SceneKit中,我使用SCNProgram在SceneKit中渲染了一个全屏四边形,其中使用了上一步中生成的深度纹理:

fragment FragmentOut sceneKitFullScreenQuadFragment(const InOut in [[ stage_in ]],
                                depth2d<float, access::sample> depthTexture [[texture(1)]])
{
    constexpr sampler sampler(filter::linear);
    
    const float depth = depthTexture.sample(sampler, in.uv);
    return {
        .color = float4(0),
        .depth = depth,
    };
}

所以有两个问题:

  1. SceneKit的z缓冲区使用什么格式?是反对数z缓冲区吗?
    1.在为SceneKit生成z缓冲区值时,我做错了什么?
ki0zmccv

ki0zmccv1#

SceneKit使用反对数Z缓冲区。This postthis post向您展示了如何获得归一化线性Map空间[0...1]。您需要相反的公式。
此外,还可以通过以下方式将值从reverseZ切换为directZ:

let sceneView = self.view as! SCNView
sceneView.usesReverseZ = true             // default
svmlkihl

svmlkihl2#

Andy Jazz的回答很有帮助,但我还是觉得这些链接很混乱。以下是最终对我起作用的方法(尽管可能还有其他方法):
当生成深度贴图时(在我的原始示例中,这将位于金属着色器内部),传入SceneKit的投影变换矩阵,并使用此矩阵变换深度值:

// In a metal shader generating the depth map

// The z distance from the camera, e.g. if the object
// at the current position is 5 units away, this would be 5.
const float z = ...;

// The camera points along the -z axis, so transform the -z position
// with SceneKit's projection matrix (you can get this from SCNCamera)
const float4 depthPos = (sceneKitState.projectionTransform * float4(0, 0, -z, 1));

// Then do perspective division to get the final depth value
out.depth = depthPos.z / depthPos.w;

然后在SceneKit着色器中,只需写出深度,并考虑usesReverseZ

// In a scenekit, full screen quad shader

const float depth = depthTexture.sample(sampler, in.uv);
return {
    .color = float4(0),
    .depth = 1.0 - depth,
};

以上️假设您使用的是sceneView.usesReverseZ = true(默认值),如果您使用的是usesReverseZ = false,只需使用.depth = depth即可

相关问题