gpt4 book ai didi

opengl-es - 如何以编程方式切换到 Sprite Kit 中的 openGL 以适应 iPad2 等旧设备

转载 作者:行者123 更新时间:2023-12-03 06:50:15 26 4
gpt4 key购买 nike

我正在使用 Sprite Kit 开发游戏。自 iOS9 SpriteKit 使用 Metal 作为着色器后端以来。 SK 和着色器工作得很好。但如果我在 iPad2 上测试它,它就不再工作了。我已经阅读过有关此问题的信息,并且知道 iPad2 不支持 Metal。现在,在这种情况下,我想回退到 open 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
}
}

我知道可以在应用程序的 plist 中设置兼容模式:

  • 编辑应用的 Info.plist
  • 添加 bool 值为 YES 的 PrefersOpenGL

在这种情况下,SpriteKit 始终使用 openGL。这不是我想用的。我希望我的应用程序始终使用 Metal,如果未检测到 Metal 设备,则回退到 openGL。

SpriteKit 或 UIKit 或 API 中的某个位置是否有任何选项可以让我以编程方式切换到“PrefersOpenGL”选项?

提前致谢,

jack

最佳答案

摘要

我找到了解决方案。最后是我的错误。 SpriteKit 肯定会自动回退到 openGL。 GLES 着色器语言的宽容度不如 Metal。这就是问题的根源。在 openGL 着色器中,您必须设置每个数字的小数点。不幸的是,着色器编译器在编译后没有告诉我这一点。另一个问题是,有时旧的着色器构建会与 bundle 一起使用。在测试着色器之前执行“clean”。

这就是处理两种着色器并检测 Metal/openGL 的方法:

检测 Metal 是否可用

这个小助手可以放置在代码中的任何位置。它可以帮助您在第一次使用时检测 Metal,并让您有机会根据配置执行一次自定义代码。

标题:

#import SpriteKit
#import Metal

代码:

/**
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
}
}

在 Sprite Kit 中创建着色器

像往常一样使用 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

正如您所看到的,根据检测到的配置正在加载另一个着色器文件。 openGL 着色器需要额外的尺寸统一,因为符号 v_tex_coord 在 openGL 中不可用。如果您在 Metal 中不使用尺寸统一,则可以将统一语句移至 if block 中或忽略它。如果您不使用 Metal,它不会提示。

Metal 着色器:plasma.fsh

#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);
}

在 Metal 着色器中,您可以简单地读取标准化坐标。如果您愿意,可以使用大小来重建图像坐标。然而, Metal 对小数点的处理更为宽容。正如您所看到的,有些数字没有小数点。

Open GL 着色器:plasmaGL.fsh

// 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);
}

展望

测试两个系统并创建两个着色器需要更多工作。但只要我们从 GL 过渡到 Metal,这就是测试应使用哪种着色器的好方法。 iOS 模拟器也不支持 Metal。这意味着您可以使用 iOS 和 tvOS 模拟器测试 openGL 行为。

如果您为 AppleTV 进行开发,那么这种方法非常方便,因为 openGL 着色器始终与 Metal 配合使用。您只需将 gl_FragCoord.xy/size.xy 替换为 v_tex_coord 即可。如果您在模拟器上运行代码,您将看到 openGL 代码,如果您在 AppleTV 目标上运行代码,您将看到平滑的 Metal 着色器。

对所有 swift 开发人员的另一个提示:永远不要忘记着色器行末尾的分号;-)

另一个陷阱是类型转换。

Metal :int intVal = (int) uv.x;浮点 a = (浮点) intVal;

开放式GL:int intVal = int(uv.x); float a = float (intVal);

我希望我能帮助任何人。

干杯,

jack

关于opengl-es - 如何以编程方式切换到 Sprite Kit 中的 openGL 以适应 iPad2 等旧设备,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35699824/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com