有没有办法让一个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
公开了 writesToDepthBuffer
和 readsFromDepthBuffer
让你这样做(必要时结合 SCNNode
的 renderingOrder
)
原来我找到了问题的答案(在 mnuages 的帮助下)。我只是想 post 一个完整的答案。
正如 mnuages 所建议的那样,我尝试将 readsFromDepthBuffer
和 writesToDepthBuffer
都设置为 false,并将节点的 renderingOrder
设置为较高的数字。它以错误的方式工作。它不是总是在前面,而是总是在每个对象的后面。得到如图结果的方法是只将readsFromDepthBuffer
设置为false,将立方体的renderingOrder
设置为-1,否则无法绘制。
由于立方体和其他节点的材质readsFromDepthBuffer
和writesToDepthBuffer
设置为默认值true
,它仍然会在它后面的物体前面,而在它前面的物体后面,换句话说,它将是正常的,只有箭头会按照我们想要的方式运行。
从图中可以看出,立方体前面的线条部分是可见的。后面的部分就不一样了。
编辑: 这只对我有用,因为在我的情况下,相机不必围绕目的。如果无论摄像机角度如何,您都需要 Gizmo 始终位于前方,则它不会起作用。检查已接受的答案以获取有关如何实现该目标的解释,此外,还有 simeon 对 Metal 实现的回答。
readsFromDepthBuffer 答案可能适用于所示图像中的角度,但如果您要旋转并查看不同的角度,您会看到 gizmo 的轴从某些角度不正确地重叠。换句话说,一些轴在它应该在它前面的时候会在另一个轴后面,这取决于面部渲染的顺序。这显然是因为对象 (material) 没有读取深度缓冲区,包括它自己已经渲染的部分。
一般来说,在主场景前面渲染 SCNNode 的一个简单解决方案是在场景的 overlaySKScene 中使用 SK3DNode(具有自己的场景和深度缓冲区)。对于答案图片中显示的场景,这将需要额外的代码来准确地旋转和定位 SK3DNode,因此这可能不是 OP 正在寻找的内容,但这可能对发现此问题的其他人有所帮助。
正如我在之前的回答中所解释的那样,接受的答案并不是最佳的,并且仅适用于广告牌、huds 和其他通常平坦的物体(不一定完全是 2D)。当使用 3D 对象并禁用从深度缓冲区和对象(如上图中的对象)读取时,它不会从每个角度正确渲染。即 3D 对象 需要 从深度缓冲区读取以检测其自身的像素和深度。综上所述,我给出正确答案:
简而言之,渲染 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 检查器)会导致所需的结果。
所有其他对象的渲染顺序保持为零,不应用负顺序。
我知道您在代码中需要这个,但也许您可以从中得到一些东西?
我一直在尝试使用场景工具包制作一个场景,其中指定的对象总是在其他对象的前面,尽管它实际上在其他对象的后面。与 blender.
中使用的类似效果显然,blender 使用 GUI 和大量数学来转换 2D 对象,但我需要在具有 SCNGeometry 的 SCNNode 中实现这种效果,换句话说,当前位于场景中的 3D 对象。
我考虑过使用类别掩码,但在阅读了 Apple 的文档后我意识到这对我正在寻找的效果不起作用。
有谁知道在 SceneKit 中这样做的方法吗?或者更好的是,甚至可以这样做吗?
提前非常感谢大家,现在以及我从 StackExchange 获得的所有其他帮助!
SCNMaterial
公开了 writesToDepthBuffer
和 readsFromDepthBuffer
让你这样做(必要时结合 SCNNode
的 renderingOrder
)
原来我找到了问题的答案(在 mnuages 的帮助下)。我只是想 post 一个完整的答案。
正如 mnuages 所建议的那样,我尝试将 readsFromDepthBuffer
和 writesToDepthBuffer
都设置为 false,并将节点的 renderingOrder
设置为较高的数字。它以错误的方式工作。它不是总是在前面,而是总是在每个对象的后面。得到如图结果的方法是只将readsFromDepthBuffer
设置为false,将立方体的renderingOrder
设置为-1,否则无法绘制。
由于立方体和其他节点的材质readsFromDepthBuffer
和writesToDepthBuffer
设置为默认值true
,它仍然会在它后面的物体前面,而在它前面的物体后面,换句话说,它将是正常的,只有箭头会按照我们想要的方式运行。
从图中可以看出,立方体前面的线条部分是可见的。后面的部分就不一样了。
编辑: 这只对我有用,因为在我的情况下,相机不必围绕目的。如果无论摄像机角度如何,您都需要 Gizmo 始终位于前方,则它不会起作用。检查已接受的答案以获取有关如何实现该目标的解释,此外,还有 simeon 对 Metal 实现的回答。
readsFromDepthBuffer 答案可能适用于所示图像中的角度,但如果您要旋转并查看不同的角度,您会看到 gizmo 的轴从某些角度不正确地重叠。换句话说,一些轴在它应该在它前面的时候会在另一个轴后面,这取决于面部渲染的顺序。这显然是因为对象 (material) 没有读取深度缓冲区,包括它自己已经渲染的部分。
一般来说,在主场景前面渲染 SCNNode 的一个简单解决方案是在场景的 overlaySKScene 中使用 SK3DNode(具有自己的场景和深度缓冲区)。对于答案图片中显示的场景,这将需要额外的代码来准确地旋转和定位 SK3DNode,因此这可能不是 OP 正在寻找的内容,但这可能对发现此问题的其他人有所帮助。
正如我在之前的回答中所解释的那样,接受的答案并不是最佳的,并且仅适用于广告牌、huds 和其他通常平坦的物体(不一定完全是 2D)。当使用 3D 对象并禁用从深度缓冲区和对象(如上图中的对象)读取时,它不会从每个角度正确渲染。即 3D 对象 需要 从深度缓冲区读取以检测其自身的像素和深度。综上所述,我给出正确答案:
简而言之,渲染 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 检查器)会导致所需的结果。 所有其他对象的渲染顺序保持为零,不应用负顺序。 我知道您在代码中需要这个,但也许您可以从中得到一些东西?