Jac*_*rse 5 shader opengl-es ipad-2 sprite-kit metal
我正在使用Sprite Kit开发游戏.由于iOS9 SpriteKit使用Metal作为着色器后端.SK和着色器效果很好.但如果我在iPad2上进行测试,它就不再适用了.我已经读过这个问题并且知道iPad2不支持Metal.现在我想回到开放GL以在这种情况下提供GLES着色器.
我可以通过编程方式测试"Metal"是否可用(Swift 2):
/**
Returns true if the executing device supports metal.
*/
var metalAvailable:Bool {
get {
struct Static {
static var metalAvailable : Bool = false
static var metalNeedsToBeTested : Bool = true
}
if Static.metalNeedsToBeTested {
let device = MTLCreateSystemDefaultDevice()
Static.metalAvailable = (device != nil)
}
return Static.metalAvailable
}
}
Run Code Online (Sandbox Code Playgroud)
我知道可以在应用程序的plist中设置兼容模式:
在这种情况下,SpriteKit总是使用openGL.这不是我想要使用的.我希望我的应用程序始终使用金属,如果没有检测到金属设备,则回退到openGL.
SpriteKit或UIKit中的任何选项,还是API中的某个地方,我可以通过编程方式切换到"PrefersOpenGL"选项?
提前致谢,
插口
我找到了解决方案.最后这是我的错.SpriteKit肯定会自动回退到openGL.GLES着色器语言比Metal更不宽容.这是问题的来源.在openGL着色器中,您必须在每个数字中设置小数点.不幸的是,着色器编译器在编译后没有告诉我.另一个问题是有时旧的着色器构建坚持捆绑.在测试着色器之前执行"清理".
这就是如何处理这两种着色器和检测Metal/openGL:
这个小助手可以放在代码中的任何位置.它可以帮助您在第一次使用时检测Metal,并让您有机会根据配置执行一次自定义代码.
头:
#import SpriteKit
#import Metal
Run Code Online (Sandbox Code Playgroud)
码:
/**
Detect Metal. Returns true if the device supports metal.
*/
var metalAvailable:Bool {
get {
struct Static {
static var metalAvailable : Bool = false
static var metalNeedsToBeTested : Bool = true
}
if Static.metalNeedsToBeTested {
Static.metalNeedsToBeTested = false
let device = MTLCreateSystemDefaultDevice()
Static.metalAvailable = (device != nil)
if Static.metalAvailable {
// Do sth. to init Metal code, if needed
} else {
// Do sth. to init openGL code, if needed
}
}
return Static.metalAvailable
}
}
Run Code Online (Sandbox Code Playgroud)
像往常一样使用sprite工具包创建着色器.
let shaderContainer = SKSpriteNode()
shaderContainer.position = CGPoint(x:self.frame.size.width/2, y:self.frame.size.height/2)
shaderContainer.size = CGSize(width:self.frame.size.width, height:self.frame.size.height)
self.backgroundNode.addChild(shaderContainer)
let bgShader:SKShader
// Test if metal is available
if self.metalAvailable {
bgShader = SKShader(fileNamed:"plasma.fsh")
} else {
NSLog("Falling back to openGL")
bgShader = SKShader(fileNamed:"plasmaGL.fsh")
}
// Add your uniforms. OpenGL needs the size of the frame to normalize
// The coordinates. This is why we always use the size uniform
bgShader.uniforms = [
SKUniform(name: "size", floatVector2:GLKVector2Make(1920.0, 1024.0))
]
shaderContainer.shader = bgShader
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,根据检测到的配置,正在加载另一个着色器文件.openGL着色器需要额外的大小统一,因为符号v_tex_coord在openGL中不可用.如果你没有在Metal中使用size uniform,你可以将uniforms语句移动到if块中或者忽略它.如果你不使用金属,金属不会抱怨.
#define M_PI 3.1415926535897932384626433832795
#define frequency 1 // Metal is less sensitive to number types.
#define colorDepth 2 // Numbers without decimal point make problems with openGL
void main(void) {
vec2 uv = v_tex_coord; // Normalized coordinates in Metal shaders
float red = ((sin((uv.x + u_time * 0.01) * M_PI * frequency) * cos((uv.y + u_time * 0.03) * M_PI * frequency) + 1) / colorDepth) + (colorDepth / 2.75) - (2 / 2.75);
gl_FragColor = vec4(red, uv.x, u_time, 1.0);
}
Run Code Online (Sandbox Code Playgroud)
在金属着色器中,您可以简单地读取标准化坐标.如果您愿意,可以使用尺寸重建图像坐标.然而,金属更加宽容小数点.如您所见,某些数字在此处没有小数点.
// OPEN GL shaders NEED the decimal point in numbers. so never use 1 but 1. or 1.0
#define M_PI 3.1415926535897932384626433832795
#define frequency 1.0 // This number must have a decimal point
#define colorDepth 2.0 // Same here.
void main(void) {
vec2 uv = gl_FragCoord.xy / size.xy; // Frame coordinates in openGL
// This formula is always using numbers with decimal points.
// Compare it to the metal shader. Two numbers of the metal
// have no decimal point. If you cut copy paste the metal shader
// formula to the GL shader it will not work!
float red = ((sin((uv.x + u_time * 0.01) * M_PI * frequency) * cos((uv.y + u_time * 0.03) * M_PI * frequency) + 1.0) / colorDepth) + (colorDepth / 2.75) - (2.0 / 2.75);
gl_FragColor = vec4(red, uv.x, u_time, 1.0);
}
Run Code Online (Sandbox Code Playgroud)
测试两个系统和创建两个着色器是更多的工作.但只要我们从GL转换为Metal,这就是测试应该使用哪种着色器的好方法.iOS模拟器也不支持Metal.这意味着您可以使用iOS和tvOS模拟器测试openGL行为.
如果您为AppleTV开发,那么这种方法非常方便,因为openGL着色器始终与Metal配合使用.你只需要更换gl_FragCoord.xy/size.xy与v_tex_coord.如果您在模拟器上运行代码,您将看到openGL代码,如果您在AppleTV目标上运行它,您将看到平滑的金属着色器.
所有快速开发人员的另一个暗示:永远不要忘记着色器末尾的分号;-)
另一个陷阱是铸造.
Metal:int intVal =(int)uv.x; float a =(float)intVal;
Open GL:int intVal = int(uv.x); float a = float(intVal);
我希望我可以帮助任何人.
干杯,
插口
| 归档时间: |
|
| 查看次数: |
1093 次 |
| 最近记录: |