[英]SceneKit shader modifier for geometry entry points works in iOS but not OS X
我處於制作SceneKit着色器修改器(用於幾何學入口點)的早期階段,該修改器根據高度圖紋理來置換平面的幾何體。 計划將其用於創建地形。
在iOS中(編輯器:iOS Simulator),着色器可以正常工作,但會在控制台上顯示以下警告:
SceneKit:錯誤,沒有代碼的修飾符無效
但是,在為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%地起作用。 在您的情況下,最簡單的解決方案可能是通過在創建 SCNView
時傳入選項來強制SCNView
使用OpenGL。 我相信Interface Builder也為此提供了一個下拉菜單。
如果您希望繼續使用Metal,並且出於某些原因,請繼續閱讀。
當着色器修改器無法編譯着色器的全部內容時,會將其全部轉儲到stdout,這確實有助於調試過程。 如果您向上滾動到第343行,您會發現SceneKit包含了float4 displace = displacementMap.sample(...);
Map.sample float4 displace = displacementMap.sample(...);
頂點着色器之前的方法。 在函數外部定義意味着它是一個全局變量,因此需要constant
地址空間修飾符,但這根本不是您想要的...在這種情況下,從GLSL到Metal的翻譯沒有按預期工作。
我注意到您缺少#pragma arguments
指令,該指令應包含在統一定義上方(請參閱“編寫着色器修改器代碼段” ),這可能會有所幫助。 根據我的經驗,我認為將紋理傳遞到基於金屬的着色器修改器中會很困難。 我嘗試了失敗,然后編寫了SCNProgram
來代替它。 建議強制使用OpenGL。
我已經弄清楚了為什么它在Metal中不起作用
以及為什么@lock非常有幫助地指出,即使將float4 displace = displacementMap.sample(...)
映射.sample float4 displace = displacementMap.sample(...)
放置在頂點着色器外部的全局空間中,即使它位於# pragma body
行之下。
這是因為有額外的空間。
它應該是#pragma body
(哈希后沒有空格)。
如果刪除該空間,那么它將在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類引用在其示例代碼段中具有自定義全局函數也是如此。 因此,為了保持兼容性,我不得不“放松”代碼段中的功能。 例如,這是一個用於計算法線的表面着色器修改器(點法線看起來不太好,因此我在表面着色器修改器中切換為像素法線)。 這在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;
在《金屬版》翻譯中,此操作失敗,原因是:
<program source>:344:8: warning: no previous prototype for function 'getNewPos'
float3 getNewPos(float2 tc) {
^
這是展開的版本。 刪除該函數會使代碼的DRY和漂亮程度大大降低,但它同時適用於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;
u_diffuseTexture
在着色器片段的Metal轉換中無法訪問 我想,最終,將GLES着色器修改器自動轉換為Metal只會使您受益匪淺,並且要使用自定義着色器修改器正確地進行多目標開發,除了GLES之外,還應提供完整的Metal着色器代碼段一個(並使用預編譯器#if
塊在它們之間切換?)。 我非常了解GLSL,但我還沒有全面了解Metal SL的知識。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.