我正在使用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,
};
}
所以有两个问题:
- SceneKit的z缓冲区使用什么格式?是反对数z缓冲区吗?
1.在为SceneKit生成z缓冲区值时,我做错了什么?
2条答案
按热度按时间ki0zmccv1#
SceneKit使用反对数Z缓冲区。This post和this post向您展示了如何获得归一化线性Map空间
[0...1]
。您需要相反的公式。此外,还可以通过以下方式将值从reverseZ切换为directZ:
svmlkihl2#
Andy Jazz的回答很有帮助,但我还是觉得这些链接很混乱。以下是最终对我起作用的方法(尽管可能还有其他方法):
当生成深度贴图时(在我的原始示例中,这将位于金属着色器内部),传入SceneKit的投影变换矩阵,并使用此矩阵变换深度值:
然后在SceneKit着色器中,只需写出深度,并考虑
usesReverseZ
:以上️假设您使用的是
sceneView.usesReverseZ = true
(默认值),如果您使用的是usesReverseZ = false
,只需使用.depth = depth
即可