在 SCNShadable 入口点之间传递值

Posted

技术标签:

【中文标题】在 SCNShadable 入口点之间传递值【英文标题】:Passing values between SCNShadable entry points 【发布时间】:2016-09-29 12:06:43 【问题描述】:

在 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 方式,但我什至无法重现结果。

【问题讨论】:

【参考方案1】:

我遇到了类似的问题并设法找出解决方案。我不确定这可以追溯到多远,但它适用于带有 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)

【讨论】:

这至少对于当前的 ios 和 xcode 来说是正确的答案。感谢您发布此信息,这个问题使我无法切换到金属。关键部分是在几何入口点中使用#include 行、#pragma varyings 行并在变量前面加上 in./.out。 您是如何发现使用inout 的?以前从未见过(甚至在 Apple 的 Metal Shading Language Specification 文档中也没有)。 @CloakedEddy 我记得我花了很多时间才弄清楚如何使这项工作发挥作用,但那是很久以前的事了,所以我不记得我是如何拼凑起来的。我有点记得在 Apple 的一些源代码示例中找到了我需要的东西,但我不确定。 今天早上我发现了this gem。我仍在处理那里的所有内容(包括从 Xcode 访问此文件的指针),但是一旦我把头绕过去,我可能会在此处添加一个包含最新信息的附加答案。【参考方案2】:

在某些时候,您会遇到SCNShadable 的功能限制,需要转向SCNProgram。不幸的是,这样做意味着您需要提供顶点和片段着色器的完整实现。从好的方面来说,将一个值从顶点传递到片段着色器是相当简单的。您只需将一个变量添加到您使用 stage_in 限定符标识的结构中(假设您使用的是 Metal)。但这并不是您真正要求的,并且有一个解决方法。

#pragma arguements 如您所述,在这种情况下不起作用;它用于传递在渲染过程中不会变化的常量。

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

很想知道更好的解决方案。

【讨论】:

当然也很难找到细节。到目前为止,最好的资源是头文件、引用和着色器源的转储。这个人暗示他是使用 GLSL 语法做到的。 ericberna.com/2015/03/20/SCNShadable.html 在某些时候我会碰到极限......你的意思是立即。 :)【参考方案3】:

强制使用 GLSL

事实证明,如果您将SCNView Rendering 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 团队已经提出了一个正式的增强请求 (bug #28206462) 来研究将此功能添加到 Metal 中。在此之前,您必须选择使用 OpenGL 渲染器。

【讨论】:

PBR?你能定义术语吗? 基于物理的渲染/着色。这是一个比 Blinn/phong 等更精细的光照模型。由于某种原因,它只适用于金属,不知道为什么它不兼容 OpenGL/GLSL。我遇到了一个非常奇怪的错误,我认为可以通过使用 _geometry.texcoords[0] 传递自定义变量来解决,但我发现变量仅适用于 glsl :((( 啊。是的,不熟悉缩写,是的,它不起作用。我们可以希望他们添加它 你的问题是 “我如何使用类似 varyings 的东西和 Metal SceneKit shaderModifiers 而你的答案归结为 “不要使用金属”,恕我直言,这不是一个有效的答案。我来到这个问题希望找到像@lock's这样的解决方法,或者——理想情况下——将SceneKit的通用配置文件着色器复制到自定义SCNProgram并将其配置为与几何/纹理/阴影/等一起正常工作的最小实现。 所以恕我直言:在 SO 上回答你自己的问题很好,但回答是“你做不到”是不行的。出于这个原因投反对票。 SCNProgam 可能会出现在您的未来。我也考虑过,但我真的不想放弃免费的东西并写很多样板,所以我可以对一个片段着色器进行 2 行更改。如果他们扩展 Metal 并使这个问题/答案过时,我真的更喜欢它。【参考方案4】:

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

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

此文件中有很多信息,它演示了 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。您在几何修饰符中声明的任何varying 变量都显示为commonprofile_io 结构上的属性。其中您使用以下实例:commonprofile_io out;

该文件还演示了为什么需要varyings 来在几何体和片段着色器之间进行通信。以及为什么在片段着色器中可以访问_surface:片段修饰符和表面修饰符具有相同的范围。

我绝对很难过 Apple 没有记录甚至暗示这些东西,除了一些隐藏的 WWDC 幻灯片。我不知道没有几个星期空闲时间的人应该如何弄清楚这一切是如何运作的。

【讨论】:

以上是关于在 SCNShadable 入口点之间传递值的主要内容,如果未能解决你的问题,请参考以下文章

使用 EnumProcessModules 的 Windows 程序入口点返回意外值

我可以将参数传递给 SageMaker 估计器的入口点吗?

Keil stm32,使用汇编,分散文件和c。如何将c代码入口点导出到程序集?

是否可以在没有代码覆盖率工具作为入口点的情况下收集代码覆盖率数据?

C#提示不只定义了一个入口点,请使用/main进行编译以指定包含入口点的类型

将 java_opts 传递给 Kubernetes 中的 Spring Boot 应用程序