有没有办法让一个SCNNode一直画在别人的前面?

Is there a way to draw a SCNNode always in front of others?

我一直在尝试使用场景工具包制作一个场景,其中指定的对象总是在其他对象的前面,尽管它实际上在其他对象的后面。与 blender.

中使用的类似效果

显然,blender 使用 GUI 和大量数学来转换 2D 对象,但我需要在具有 SCNGeometry 的 SCNNode 中实现这种效果,换句话说,当前位于场景中的 3D 对象。

我考虑过使用类别掩码,但在阅读了 Apple 的文档后我意识到这对我正在寻找的效果不起作用。

有谁知道在 SceneKit 中这样做的方法吗?或者更好的是,甚至可以这样做吗?

提前非常感谢大家,现在以及我从 StackExchange 获得的所有其他帮助!

SCNMaterial 公开了 writesToDepthBufferreadsFromDepthBuffer 让你这样做(必要时结合 SCNNoderenderingOrder

原来我找到了问题的答案(在 mnuages 的帮助下)。我只是想 post 一个完整的答案。


正如 mnuages 所建议的那样,我尝试将 readsFromDepthBufferwritesToDepthBuffer 都设置为 false,并将节点的 renderingOrder 设置为较高的数字。它以错误的方式工作。它不是总是在前面,而是总是在每个对象的后面。得到如图结果的方法是只将readsFromDepthBuffer设置为false,将立方体的renderingOrder设置为-1,否则无法绘制。

由于立方体和其他节点的材质readsFromDepthBufferwritesToDepthBuffer设置为默认值true,它仍然会在它后面的物体前面,而在它前面的物体后面,换句话说,它将是正常的,只有箭头会按照我们想要的方式运行。

从图中可以看出,立方体前面的线条部分是可见的。后面的部分就不一样了。

编辑: 这只对我有用,因为在我的情况下,相机不必围绕目的。如果无论摄像机角度如何,您都需要 Gizmo 始终位于前方,则它不会起作用。检查已接受的答案以获取有关如何实现该目标的解释,此外,还有 simeon 对 Metal 实现的回答。

readsFromDepthBuffer 答案可能适用于所示图像中的角度,但如果您要旋转并查看不同的角度,您会看到 gizmo 的轴从某些角度不正确地重叠。换句话说,一些轴在它应该在它前面的时候会在另一个轴后面,这取决于面部渲染的顺序。这显然是因为对象 (material) 没有读取深度缓冲区,包括它自己已经渲染的部分。

一般来说,在主场景前面渲染 SCNNode 的一个简单解决方案是在场景的 overlaySKScene 中使用 SK3DNode(具有自己的场景和深度缓冲区)。对于答案图片中显示的场景,这将需要额外的代码来准确地旋转和定位 SK3DNode,因此这可能不是 OP 正在寻找的内容,但这可能对发现此问题的其他人有所帮助。

正如我在之前的回答中所解释的那样,接受的答案并不是最佳的,并且仅适用于广告牌、huds 和其他通常平坦的物体(不一定完全是 2D)。当使用 3D 对象并禁用从深度缓冲区和对象(如上图中的对象)读取时,它不会从每个角度正确渲染。即 3D 对象 需要 从深度缓冲区读取以检测其自身的像素和深度。综上所述,我给出正确答案:

SCNTechnique

简而言之,渲染 2 个附加通道。一个用于控制 gizmo (DRAW_NODE),一个用于通过另一个通道将其与场景混合在一起(DRAW_QUAD,着色器使用之前的通道作为输入)。

以下是技巧的scntec.plist内容:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>passes</key>
    <dict>
        <key>gizmoonly</key>
        <dict>
            <key>colorStates</key>
            <dict>
                <key>clear</key>
                <true/>
                <key>clearColor</key>
                <string>0.5 0.5 0.5 0.0</string>
            </dict>
            <key>depthStates</key>
            <dict>
                <key>clear</key>
                <true/>
            </dict>
            <key>inputs</key>
            <dict>
                <key>colorSampler</key>
                <string>COLOR</string>
            </dict>
            <key>outputs</key>
            <dict>
                <key>color</key>
                <string>gizmonode</string>
            </dict>
            <key>draw</key>
            <string>DRAW_NODE</string>
            <key>node</key>
            <string>movegizmo</string>
        </dict>
        <key>quadscene</key>
        <dict>
            <key>colorStates</key>
            <dict>
                <key>clear</key>
                <true/>
                <key>clearColor</key>
                <string>sceneBackground</string>
            </dict>
            <key>depthStates</key>
            <dict>
                <key>clear</key>
                <true/>
            </dict>
            <key>inputs</key>
            <dict>
                <key>totalSceneO</key>
                <string>COLOR</string>
                <key>a_texcoord</key>
                <string>a_texcoord-symbol</string>
                <key>gizmoNodeO</key>
                <string>gizmonode</string>
            </dict>
            <key>outputs</key>
            <dict>
                <key>color</key>
                <string>COLOR</string>
            </dict>
            <key>draw</key>
            <string>DRAW_QUAD</string>
            <key>program</key>
            <string>gizmo</string>
        </dict>
    </dict>
    <key>sequence</key>
    <array>
        <string>gizmoonly</string>
        <string>quadscene</string>
    </array>
    <key>targets</key>
    <dict>
        <key>totalscene</key>
        <dict>
            <key>type</key>
            <string>color</string>
        </dict>
        <key>gizmonode</key>
        <dict>
            <key>type</key>
            <string>color</string>
        </dict>
    </dict>
    <key>symbols</key>
    <dict>
        <key>a_texcoord-symbol</key>
        <dict>
            <key>semantic</key>
            <string>texcoord</string>
        </dict>
        <key>vertexSymbol</key>
        <dict>
            <key>semantic</key>
            <string>vertex</string>
        </dict>
    </dict>
</dict>
</plist>

以下是第二遍的顶点着色器:

attribute vec4 a_position;
varying vec2 uv;

void main() {
    gl_Position = a_position;
    uv = (a_position.xy + 1.0) * 0.5;
}

第二遍的片段着色器:

uniform sampler2D totalSceneO;
uniform sampler2D gizmoNodeO;

varying vec2 uv;

void main() {
    vec4 t0 = texture2D(totalSceneO, uv);
    vec4 t1 = texture2D(gizmoNodeO, uv);
    gl_FragColor = (1.0 - t1.a) * t0 + t1.a * t1;
}

Swift代码:

if let path = NSBundle.mainBundle().pathForResource("scntec", ofType: "plist") {
            if let dico1 = NSDictionary(contentsOfFile: path)  {
                let dico = dico1 as! [String : AnyObject]

                let technique = SCNTechnique(dictionary:dico)
                scnView.technique = technique
            }
}

Objective-C代码:

NSURL *url = [[NSBundle mainBundle] URLForResource:@"scntec" withExtension:@"plist"];
SCNTechnique *technique = [SCNTechnique techniqueWithDictionary:[NSDictionary dictionaryWithContentsOfURL:url]];
    self.myView.technique = technique;

设置小工具节点的名称:

theGizmo.name = @"movegizmo";

@Xartec 的回答是要走的路。您基本上是在透明背景上单独渲染 gizmo,然后使用简单的着色器将其与场景混合。这是 Metal

的相同设置

Gizmo.metal

#include <metal_stdlib>
using namespace metal;

#include <SceneKit/scn_metal>

struct custom_vertex_t
{
    float4 position [[attribute(SCNVertexSemanticPosition)]];
    float4 normal [[attribute(SCNVertexSemanticNormal)]];
};

struct out_vertex_t
{
    float4 position [[position]];
    float2 uv;
};

vertex out_vertex_t gizmo_vertex(custom_vertex_t in [[stage_in]])
{
    out_vertex_t out;
    out.position = in.position;
    out.uv = float2((in.position.x + 1.0) * 0.5 , (in.position.y + 1.0) * -0.5);

    return out;
};

constexpr sampler s = sampler(coord::normalized,
                              r_address::clamp_to_edge,
                              t_address::repeat,
                              filter::linear);

fragment half4 gizmo_fragment(out_vertex_t vert [[stage_in]],
                              texture2d<float, access::sample> totalSampler [[texture(0)]],
                              texture2d<float, access::sample> gizmoSampler [[texture(1)]])
{

    float4 t0 = totalSampler.sample(s, vert.uv);
    float4 t1 = gizmoSampler.sample(s, vert.uv);

    return half4((1.0 - t1.a) * t0 + t1.a * t1);
}

GizmoTechnique.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>passes</key>
    <dict>
        <key>pass_scene</key>
        <dict>
            <key>draw</key>
            <string>DRAW_SCENE</string>
            <key>inputs</key>
            <dict/>
            <key>outputs</key>
            <dict>
                <key>color</key>
                <string>color_scene</string>
            </dict>
            <key>colorStates</key>
            <dict>
                <key>clear</key>
                <true/>
                <key>clearColor</key>
                <string>sceneBackground</string>
            </dict>
        </dict>
        <key>pass_gizmo</key>
        <dict>
            <key>colorStates</key>
            <dict>
                <key>clear</key>
                <true/>
                <key>clearColor</key>
                <string>0 0 0 0</string>
            </dict>
            <key>depthStates</key>
            <dict>
                <key>clear</key>
                <true/>
            </dict>
            <key>outputs</key>
            <dict>
                <key>color</key>
                <string>color_gizmo</string>
            </dict>
            <key>inputs</key>
            <dict/>
            <key>draw</key>
            <string>DRAW_NODE</string>
            <key>node</key>
            <string>transformGizmo</string>
        </dict>
        <key>mix</key>
        <dict>
            <key>colorStates</key>
            <dict>
                <key>clear</key>
                <true/>
            </dict>
            <key>depthStates</key>
            <dict>
                <key>clear</key>
                <true/>
            </dict>
            <key>inputs</key>
            <dict>
                <key>totalSampler</key>
                <string>COLOR</string>
                <key>gizmoSampler</key>
                <string>color_gizmo</string>
            </dict>
            <key>outputs</key>
            <dict>
                <key>color</key>
                <string>COLOR</string>
            </dict>
            <key>draw</key>
            <string>DRAW_QUAD</string>
            <key>program</key>
            <string>doesntexist</string>
            <key>metalFragmentShader</key>
            <string>gizmo_fragment</string>
            <key>metalVertexShader</key>
            <string>gizmo_vertex</string>
        </dict>
    </dict>
    <key>sequence</key>
    <array>
        <string>pass_gizmo</string>
        <string>mix</string>
    </array>
    <key>targets</key>
    <dict>
        <key>color_gizmo</key>
        <dict>
            <key>type</key>
            <string>color</string>
        </dict>
    </dict>
    <key>symbols</key>
    <dict>
        <key>vertexSymbol</key>
        <dict>
            <key>semantic</key>
            <string>vertex</string>
        </dict>
    </dict>
</dict>
</plist>

我不确定它是否有任何帮助,但我很高兴地发现,Apple 在其文档中描述的方法实际上效果很好。 我有一个应该永久显示在所有其他对象面前的对象。将其渲染顺序(节点检查器)设置为更高的值(但所有所述节点都相同)并取消选中所有使用材料的“读取深度”复选框(Material 检查器)会导致所需的结果。 所有其他对象的渲染顺序保持为零,不应用负顺序。 我知道您在代码中需要这个,但也许您可以从中得到一些东西?