在SCNShadable入口点之间传递值

时间:2016-05-31 12:53:47

标签: opengl-es scenekit metal glsles

在OpenGL程序中,您通常在顶点着色器中声明类似的内容

varying bool aBooleanVariable;

然后读取片段着色器中的值。你如何在SCNShadable入口点的框架内做到这一点?例如,从SCNShaderModifierEntryPointGeometrySCNShaderModifierEntryPointFragment

接收参数似乎是通过使用pragma参数定义的,我提供了我的测试SCNShaderModifierEntryPointFragment来说明。

#pragma arguments
bool clipFragment

#pragma body

if (clipFragment) {
    discard_fragment();
}

但是,参数pragma不能用于输出SCNShaderModifierEntryPointGeometry入口点中的值。

found an article表示它可以用GLSL语法完成,但我试图找到Metal方式,我甚至无法重现结果。

4 个答案:

答案 0 :(得分:3)

强制使用GLSL

如果将SCNView渲染API切换为OpenGL ES,则可以使其正常工作。然后它将接受包含变化限定符的GLSL代码。

几何入口点

varying float clipFragment;

#pragma body
clipFragment = 1.0;

片段入口点

varying float clipFragment;

#pragma body
if (clipFragment > 0.0) {
    discard;
}

Apple的更新

我向Apple开了一张票,得到了以下回复 -

这是对的。 SceneKit团队已经开放了一个正式的增强请求(错误#28206462),以调查将此功能添加到Metal。在此之前,您必须选择使用OpenGL渲染器。

答案 1 :(得分:3)

我有类似的问题,并设法找出解决方案。我不知道它的工作原理有多远,但它可以在iOS 11的XCode 9上运行。以下是使用变量float3为坐标查找自定义立方体贴图纹理的示例。

几何修改器:

#include <metal_stdlib>

#pragma varyings
float3 cubemapCoord;

#pragma body;
out.cubemapCoord = _geometry.normal;

表面修饰符:

#include <metal_stdlib>

#pragma arguments
texturecube<float> texture;

#pragma body
constexpr sampler s(filter::linear, mip_filter::linear);
_surface.diffuse = texture.sample(s, in.cubemapCoord);

用于分配材料的Swift代码:

do {
    let loader = MTKTextureLoader.init(device: MTLCreateSystemDefaultDevice()!)
    let url = Bundle.main.url(forResource: "cubemap", withExtension: "pvr", subdirectory: "art.scnassets")!
    let texture = try loader.newTexture(URL: url, options: nil)
    let materialProperty = SCNMaterialProperty.init(contents: texture)
    geometry.setValue(materialProperty, forKey: "texture")
} catch {
    print(error.localizedDescription)
}

答案 2 :(得分:2)

在某些时候,您将遇到SCNShadable可以完成的限制,需要转到SCNProgram。遗憾的是,这样做意味着您需要提供顶点和片段着色器的完整实现。从积极的方面来说,将值从顶点传递到片段着色器非常简单。您只需将变量添加到使用stage_in限定符标识的结构中(假设您使用的是Metal)。但这并不是你所要求的,而且有一种解决方法。

你注意到,

#pragma arguements在这种情况下不会工作;它是用于传递在渲染过程中不变的常量。

相反,我建议您重新调整一下SceneKit在其着色器中使用的现有变量之一。到目前为止,毫无疑问,当SceneKit无法编译时,它会将其着色器源代码转储到stdout。 Metal着色器定义了一个commonprofile_io结构,它从顶点传递到片段着色器,它包括:位置,颜色,纹理坐标,法线等。如果你没有使用顶点颜色,那么就可以使用它存储4个浮点值(rgba)。需要注意的是,SceneKit会优化要渲染的几何体源的着色器;如果几何源不包含顶点颜色,则commonprofile_io结构将不会有vertexColor变量。

非常有兴趣了解更好的解决方案。

答案 3 :(得分:0)

我也在这个问题上苦苦挣扎,而且我最近发现了这个东西,这让我大开眼界。它极大地帮助了我掌握SceneKit,着色器,着色器修改器和OpenGL / Metal的协同工作,以及在编写着色器修改器时可以使用的工具/方法。 此外,我相信当前接受的答案不再正确(幸运的是!?)。

SceneKit提供默认的顶点和片段Metal着色器。将着色器修改器注入到这些着色器中。 在Xcode中捕获GPU帧时,可以找到这些默认的Metal着色器。 双击commonprofile_vertcommonprofile_frag(两个着色器都在同一“文件”中,如“详细信息”列中的相同值所示)。

Finding the default SceneKit shader from a GPU capture

此文件中包含很多信息,它演示了SceneKit在渲染时如何与GPU对话的一些内部工作原理。 This is an example of the file(尽管此版本已有两年的历史了,但仍是典范)。

请注意以下几行是着色器修改器的注入位置。

// DoLightModifier START

// DoLightModifier END

...

// DoGeometryModifier START

// DoGeometryModifier END

...

// DoSurfaceModifier START

// DoSurfaceModifier END

...

// DoFragmentModifier START

// DoFragmentModifier END

您将看到您的着色器修改器已放置在此处。

寻找#ifdef USE_EXTRA_VARYINGS。您在geometry修改器中声明的所有varying变量都显示为commonprofile_io结构上的属性。您将使用以下实例:commonprofile_io out;

此文件还演示了为什么需要varyings才能在几何体和片段着色器之间进行通信。以及为什么在片段着色器中可以访问_surface:片段修改器和表面修改器具有相同的作用域。

我绝对为苹果没有记录或暗示这些东西而感到困惑,除了一些隐藏的WWDC幻灯片。我不知道没有几个空闲时间的人应该怎么弄清楚这是怎么回事。