几何入口点的 SceneKit 着色器修改器适用于 iOS 但不适用于 OS X
SceneKit shader modifier for geometry entry points works in iOS but not OS X
我正处于制作 SceneKit 着色器修改器(用于几何体入口点)的早期阶段,该修改器根据高度贴图纹理置换平面的几何体。计划是用它来创建地形。
在 iOS(编辑:iOS 模拟器)中,着色器正常工作,但会向控制台打印此警告:
SceneKit: error, modifier without code is invalid
然而,当为 OS X 构建时,着色器出现致命错误,地形几何图形仅显示为粉红色矩形。
这是几何着色器修改器:
uniform sampler2D displacementMap;
const float intensity = 7.5;
# pragma body
vec4 displace = texture2D(displacementMap, _geometry.texcoords[0]);
_geometry.position.z += displace.r * intensity;
这就是着色器连接到几何体的方式。 terrainScene
是高度贴图,它被放置在漫反射内容和着色器修改器中的自定义 displacementMap
采样器值中:
//displacement shader
let mat = SCNMaterial()
mat.diffuse.contents = terrainScene
var displacementShader: String?
guard let path = NSBundle.mainBundle().pathForResource("DisplaceGeometry", ofType: "shader") else {return}
do {
displacementShader = try String(contentsOfFile: path, encoding: NSUTF8StringEncoding)
} catch {
return
}
mat.shaderModifiers = [SCNShaderModifierEntryPointGeometry: displacementShader!]
let noiseProperty = SCNMaterialProperty(contents: terrainScene!)
mat.setValue(noiseProperty, forKey: "displacementMap")
//geometry
let plane = SCNPlane(width: 80, height: 80)
plane.widthSegmentCount = 40
plane.heightSegmentCount = 40
plane.materials = [mat]
这是 OS X 错误消息的开头:
SceneKit: error, modifier without code is invalid
2016-06-11 10:58:32.054 EndlessTerrainOSX[16923:5292387] SceneKit: error, Invalid shader modifier : no code provided
2016-06-11 10:58:32.640 EndlessTerrainOSX[16923:5292387] FATAL ERROR : failed loading compiling shader:
错误结束:
UserInfo={NSLocalizedDescription=Compilation failed:
<program source>:343:8: error: global variables must have a constant address space qualifier
float4 displace = displacementMap.sample(displacementMapSampler, _geometry.texcoords[0]);
^
<program source>:343:19: error: use of undeclared identifier 'displacementMap'
float4 displace = displacementMap.sample(displacementMapSampler, _geometry.texcoords[0]);
^
<program source>:343:42: error: use of undeclared identifier 'displacementMapSampler'
float4 displace = displacementMap.sample(displacementMapSampler, _geometry.texcoords[0]);
^
<program source>:344:1: error: unknown type name '_geometry'
_geometry.position.z += displace.r * intensity;
^
<program source>:344:10: error: cannot use dot operator on a type
_geometry.position.z += displace.r * intensity;
^
}
2016-06-11 10:58:32.646 EndlessTerrainOSX[16923:5292387] Shaders without a vertex function are not allowed
有谁知道为什么它在 iOS 上显示,但在 OS X 上显示失败?
我认为这可能不是 iOS 与 OSX 的问题,而是与一个使用 OpenGL 的设备和另一个使用 Metal 的设备有关。
错误消息中的代码是Metal,说明SceneKit已经将你的代码从GLSL翻译成Metal。根据我的经验,这在 100% 的时间都不起作用。在您的情况下,最简单的解决方案可能是通过 passing in options when it's created 强制 SCNView
使用 OpenGL。我相信 Interface Builder 中也有一个下拉菜单。
如果您希望继续使用 Metal,并且有您这样做的原因,请继续阅读。
当着色器修改器无法编译时,将着色器的全部内容转储到标准输出,这确实有助于调试过程。如果向上滚动到第 343 行,您会发现 SceneKit 在顶点着色器 之前包含了 float4 displace = displacementMap.sample(...);
方法 。在函数外部定义意味着它是一个全局变量,因此需要 constant
地址 space 修饰符,但这根本不是你想要的......在这种情况下,从 GLSL 到Metal 没有像您预期的那样工作。
我注意到你遗漏了 #pragma arguments
指令,这应该包含在统一定义之上(参考 "Writing a Shader Modifier Snippet"),它可能会有所帮助。以我的经验,我认为您将很难将纹理传递到基于金属的着色器修改器中。我尝试过,失败了,然后写了一个 SCNProgram
来代替。建议强制使用 OpenGL。
我已经弄清楚为什么它在 Metal 中不起作用
以及为什么,正如@lock 非常有帮助地指出的那样,float4 displace = displacementMap.sample(...)
行被放置在顶点着色器之外的全局 space 中,即使它位于 [=] 行下方15=]。
因为多了一个space.
应该是#pragma body
(hash后没有space)。
如果我删除那个 space,那么它在 metal 和 GLES 中编译都没有错误。自定义纹理也有效(我没有提到的一件事是纹理实际上是一个 SpriteKit 场景,因为它需要动态更改)。
我不敢相信我已经花了 24 小时盯着这段应该可以工作的代码,这一切都是因为我写了 # pragma
而不是 #pragma
。感谢@lock 发现正文中的行实际上是在全局范围内声明的。
编辑:
由于这个问题的解决方案围绕着 SceneKit 将我的 GLES 着色器片段转换为 Metal 的能力,因此我注意到了一些其他事情:
将自定义纹理传递到着色器片段可以很好地转换为 Metal。 Swift 我使用的代码:
let terrainGenomes = SCNMaterialProperty(contents: "art.scnassets/TerrainGenomes.jpg")
mat.setValue(terrainGenomes, forKey: "terrainGenomes")
(其中 mat
是 SCNMaterial)
使用 #pragma arguments
会使 GLES 着色器失败。这似乎是可选的,所以我只是删除它。
我无法让 Metal 翻译识别自定义全局函数
当我尝试这样做时,Metal 翻译总是失败并显示 no previous prototype for function
,即使 SCNShadable class reference 在其示例片段中有自定义全局函数。因此,为了保持兼容性,我不得不 "unwind" 代码段中的函数。例如,这是一个计算法线的表面着色器修改器(点法线看起来不太好,所以我在表面着色器修改器中切换到像素法线)。这在 GLES 中有效,但无法转换为 Metal:
uniform sampler2D displacementMap;
uniform float intensity;
vec3 getNewPos(vec2 tc) {
vec4 displace = texture2D(displacementMap, tc);
return vec3((tc.x - 0.5) * 80., (tc.y - 0.5) * -80., displace.r * intensity);
}
#pragma body
vec3 newPos = getNewPos(_surface.diffuseTexcoord);
vec3 neighbour1 = getNewPos(_surface.diffuseTexcoord + vec2(0.015, 0.));
vec3 neighbour2 = getNewPos(_surface.diffuseTexcoord + vec2(0., 0.015));
vec4 norm = u_modelViewTransform * vec4(normalize(cross(neighbour2 - newPos, neighbour1 - newPos)), 0.);
_surface.normal = norm.xyz;
在 Metal 翻译中,这失败了:
<program source>:344:8: warning: no previous prototype for function 'getNewPos'
float3 getNewPos(float2 tc) {
^
这是未缠绕的版本。删除该函数使代码不那么枯燥,也不那么漂亮,但它在 GLES 和 Metal 中都有效:
uniform sampler2D displacementMap;
uniform float intensity;
#pragma body
vec4 pixel = texture2D(displacementMap, _surface.diffuseTexcoord);
vec3 newPos = vec3((_surface.diffuseTexcoord.x - 0.5) * 80., (_surface.diffuseTexcoord.y - 0.5) * -80., pixel.r * intensity);
vec2 tc1 = _surface.diffuseTexcoord + vec2(0.015, 0.);
vec2 tc2 = _surface.diffuseTexcoord + vec2(0., 0.015);
vec3 neighbour1 = vec3((tc1.x - 0.5) * 80., (tc1.y - 0.5) * -80., texture2D(displacementMap, tc1).r * intensity);
vec3 neighbour2 = vec3((tc2.x - 0.5) * 80., (tc2.y - 0.5) * -80., texture2D(displacementMap, tc2).r * intensity);
vec4 norm = u_modelViewTransform * vec4(normalize(cross(neighbour2 - newPos, neighbour1 - newPos)), 0.);
_surface.normal = norm.xyz;
- SceneKit 提供的某些制服,例如
u_diffuseTexture
在着色器片段的 Metal 翻译中无法访问
我想最终,将 GLES 着色器修改器自动转换为 Metal 只会让你走到这一步,要使用自定义着色器修改器正确地进行多目标开发,你应该提供完整的 Metal 着色器片段除了 GLES 之外(并使用预编译器 #if
块在它们之间切换?)。我对 GLSL 很了解,我还没有抽出时间学习 Metal SL。
我正处于制作 SceneKit 着色器修改器(用于几何体入口点)的早期阶段,该修改器根据高度贴图纹理置换平面的几何体。计划是用它来创建地形。
在 iOS(编辑:iOS 模拟器)中,着色器正常工作,但会向控制台打印此警告:
SceneKit: error, modifier without code is invalid
然而,当为 OS X 构建时,着色器出现致命错误,地形几何图形仅显示为粉红色矩形。
这是几何着色器修改器:
uniform sampler2D displacementMap;
const float intensity = 7.5;
# pragma body
vec4 displace = texture2D(displacementMap, _geometry.texcoords[0]);
_geometry.position.z += displace.r * intensity;
这就是着色器连接到几何体的方式。 terrainScene
是高度贴图,它被放置在漫反射内容和着色器修改器中的自定义 displacementMap
采样器值中:
//displacement shader
let mat = SCNMaterial()
mat.diffuse.contents = terrainScene
var displacementShader: String?
guard let path = NSBundle.mainBundle().pathForResource("DisplaceGeometry", ofType: "shader") else {return}
do {
displacementShader = try String(contentsOfFile: path, encoding: NSUTF8StringEncoding)
} catch {
return
}
mat.shaderModifiers = [SCNShaderModifierEntryPointGeometry: displacementShader!]
let noiseProperty = SCNMaterialProperty(contents: terrainScene!)
mat.setValue(noiseProperty, forKey: "displacementMap")
//geometry
let plane = SCNPlane(width: 80, height: 80)
plane.widthSegmentCount = 40
plane.heightSegmentCount = 40
plane.materials = [mat]
这是 OS X 错误消息的开头:
SceneKit: error, modifier without code is invalid
2016-06-11 10:58:32.054 EndlessTerrainOSX[16923:5292387] SceneKit: error, Invalid shader modifier : no code provided
2016-06-11 10:58:32.640 EndlessTerrainOSX[16923:5292387] FATAL ERROR : failed loading compiling shader:
错误结束:
UserInfo={NSLocalizedDescription=Compilation failed:
<program source>:343:8: error: global variables must have a constant address space qualifier
float4 displace = displacementMap.sample(displacementMapSampler, _geometry.texcoords[0]);
^
<program source>:343:19: error: use of undeclared identifier 'displacementMap'
float4 displace = displacementMap.sample(displacementMapSampler, _geometry.texcoords[0]);
^
<program source>:343:42: error: use of undeclared identifier 'displacementMapSampler'
float4 displace = displacementMap.sample(displacementMapSampler, _geometry.texcoords[0]);
^
<program source>:344:1: error: unknown type name '_geometry'
_geometry.position.z += displace.r * intensity;
^
<program source>:344:10: error: cannot use dot operator on a type
_geometry.position.z += displace.r * intensity;
^
}
2016-06-11 10:58:32.646 EndlessTerrainOSX[16923:5292387] Shaders without a vertex function are not allowed
有谁知道为什么它在 iOS 上显示,但在 OS X 上显示失败?
我认为这可能不是 iOS 与 OSX 的问题,而是与一个使用 OpenGL 的设备和另一个使用 Metal 的设备有关。
错误消息中的代码是Metal,说明SceneKit已经将你的代码从GLSL翻译成Metal。根据我的经验,这在 100% 的时间都不起作用。在您的情况下,最简单的解决方案可能是通过 passing in options when it's created 强制 SCNView
使用 OpenGL。我相信 Interface Builder 中也有一个下拉菜单。
如果您希望继续使用 Metal,并且有您这样做的原因,请继续阅读。
当着色器修改器无法编译时,将着色器的全部内容转储到标准输出,这确实有助于调试过程。如果向上滚动到第 343 行,您会发现 SceneKit 在顶点着色器 之前包含了 float4 displace = displacementMap.sample(...);
方法 。在函数外部定义意味着它是一个全局变量,因此需要 constant
地址 space 修饰符,但这根本不是你想要的......在这种情况下,从 GLSL 到Metal 没有像您预期的那样工作。
我注意到你遗漏了 #pragma arguments
指令,这应该包含在统一定义之上(参考 "Writing a Shader Modifier Snippet"),它可能会有所帮助。以我的经验,我认为您将很难将纹理传递到基于金属的着色器修改器中。我尝试过,失败了,然后写了一个 SCNProgram
来代替。建议强制使用 OpenGL。
我已经弄清楚为什么它在 Metal 中不起作用
以及为什么,正如@lock 非常有帮助地指出的那样,float4 displace = displacementMap.sample(...)
行被放置在顶点着色器之外的全局 space 中,即使它位于 [=] 行下方15=]。
因为多了一个space.
应该是#pragma body
(hash后没有space)。
如果我删除那个 space,那么它在 metal 和 GLES 中编译都没有错误。自定义纹理也有效(我没有提到的一件事是纹理实际上是一个 SpriteKit 场景,因为它需要动态更改)。
我不敢相信我已经花了 24 小时盯着这段应该可以工作的代码,这一切都是因为我写了 # pragma
而不是 #pragma
。感谢@lock 发现正文中的行实际上是在全局范围内声明的。
编辑:
由于这个问题的解决方案围绕着 SceneKit 将我的 GLES 着色器片段转换为 Metal 的能力,因此我注意到了一些其他事情:
将自定义纹理传递到着色器片段可以很好地转换为 Metal。 Swift 我使用的代码:
let terrainGenomes = SCNMaterialProperty(contents: "art.scnassets/TerrainGenomes.jpg") mat.setValue(terrainGenomes, forKey: "terrainGenomes")
(其中 mat
是 SCNMaterial)
使用
#pragma arguments
会使 GLES 着色器失败。这似乎是可选的,所以我只是删除它。我无法让 Metal 翻译识别自定义全局函数
当我尝试这样做时,Metal 翻译总是失败并显示 no previous prototype for function
,即使 SCNShadable class reference 在其示例片段中有自定义全局函数。因此,为了保持兼容性,我不得不 "unwind" 代码段中的函数。例如,这是一个计算法线的表面着色器修改器(点法线看起来不太好,所以我在表面着色器修改器中切换到像素法线)。这在 GLES 中有效,但无法转换为 Metal:
uniform sampler2D displacementMap;
uniform float intensity;
vec3 getNewPos(vec2 tc) {
vec4 displace = texture2D(displacementMap, tc);
return vec3((tc.x - 0.5) * 80., (tc.y - 0.5) * -80., displace.r * intensity);
}
#pragma body
vec3 newPos = getNewPos(_surface.diffuseTexcoord);
vec3 neighbour1 = getNewPos(_surface.diffuseTexcoord + vec2(0.015, 0.));
vec3 neighbour2 = getNewPos(_surface.diffuseTexcoord + vec2(0., 0.015));
vec4 norm = u_modelViewTransform * vec4(normalize(cross(neighbour2 - newPos, neighbour1 - newPos)), 0.);
_surface.normal = norm.xyz;
在 Metal 翻译中,这失败了:
<program source>:344:8: warning: no previous prototype for function 'getNewPos'
float3 getNewPos(float2 tc) {
^
这是未缠绕的版本。删除该函数使代码不那么枯燥,也不那么漂亮,但它在 GLES 和 Metal 中都有效:
uniform sampler2D displacementMap;
uniform float intensity;
#pragma body
vec4 pixel = texture2D(displacementMap, _surface.diffuseTexcoord);
vec3 newPos = vec3((_surface.diffuseTexcoord.x - 0.5) * 80., (_surface.diffuseTexcoord.y - 0.5) * -80., pixel.r * intensity);
vec2 tc1 = _surface.diffuseTexcoord + vec2(0.015, 0.);
vec2 tc2 = _surface.diffuseTexcoord + vec2(0., 0.015);
vec3 neighbour1 = vec3((tc1.x - 0.5) * 80., (tc1.y - 0.5) * -80., texture2D(displacementMap, tc1).r * intensity);
vec3 neighbour2 = vec3((tc2.x - 0.5) * 80., (tc2.y - 0.5) * -80., texture2D(displacementMap, tc2).r * intensity);
vec4 norm = u_modelViewTransform * vec4(normalize(cross(neighbour2 - newPos, neighbour1 - newPos)), 0.);
_surface.normal = norm.xyz;
- SceneKit 提供的某些制服,例如
u_diffuseTexture
在着色器片段的 Metal 翻译中无法访问
我想最终,将 GLES 着色器修改器自动转换为 Metal 只会让你走到这一步,要使用自定义着色器修改器正确地进行多目标开发,你应该提供完整的 Metal 着色器片段除了 GLES 之外(并使用预编译器 #if
块在它们之间切换?)。我对 GLSL 很了解,我还没有抽出时间学习 Metal SL。