翻过山后,将GLK值/操作SIMD,利用稍后显示的翻译功能。
我怎么强调都不为过——每次更改一些操作并测试它们时都要提交!你肯定会破坏东西,这样你就可以引用早期的工作代码。
这测试线束将被证明是有用的,因为您可能会发现时序更改会导致不良行为。在我的例子中,我创建了两个线束,第二个线束中包含更多应用程序代码,因此我可以更好地调试实际使用情况。
项目
我 fork 了一个开源项目Panorama。这掌握分支包含金属/simd代码,以及Swift-OpenGL分支包含原始目标C代码连同迅速转换。这让读者可以并排比较两者。但是,您不需要参考它来了解如何转换OpenGL代码输入目标C到迅速, 或转换GL套件向量和矩阵SIMD, 如下。
目标 C 到 Swift
这OpenGL代码大量使用指针,而这些在迅速.例如:
GLfloat *m_TexCoordsData; // treated as an array of pairs of floats
glTexCoordPointer(2, GL_FLOAT, 0, m_TexCoordsData);
成为
struct Pointer2 {
private var array: [SIMD2<Float>]
init(size: Int) {
let n: SIMD2<Float> = [Float.nan, Float.nan]
array = Array<SIMD2<Float>>(repeating: n, count: size)
}
subscript(index: Int) -> SIMD2<Float>{
get { return array[index] }
set(newValue) { array[index] = newValue }
}
mutating func usingRawPointer(block: WithRawPtr) {
array.withUnsafeBytes { (bufPtr) -> Void in
block(bufPtr.baseAddress!)
}
}
}
private var tPtr: Pointer2 // m_TexCoordsData
tPtr.usingRawPointer(block: { (ptr) in
glTexCoordPointer(2, UInt32(GL_FLOAT), 0, ptr)
})
这一切都在决赛中消失了金属代码。
进行转换也会出现潜在的错误!
此外,我必须将许多值转换(转换)为更严格的 Swift 类型:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
成为
glTexParameteri(UInt32(GL_TEXTURE_2D), UInt32(GL_TEXTURE_WRAP_S), GLint(GL_REPEAT))
这个过程很乏味。但是,那迅速恕我直言,代码更精简且更易于阅读。
几个功能很容易翻译:
GLKQuaternion GLKQuaternionFromTwoVectors(GLKVector3 u, GLKVector3 v) {
GLKVector3 w = GLKVector3CrossProduct(u, v);
GLKQuaternion q = GLKQuaternionMake(w.x, w.y, w.z, GLKVector3DotProduct(u, v));
q.w += GLKQuaternionLength(q);
return GLKQuaternionNormalize(q);
}
成为
func GLKQuaternionFromTwoVectors(_ u: GLKVector3, _ v: GLKVector3) -> GLKQuaternion {
let w = GLKVector3CrossProduct(u, v)
var q = GLKQuaternionMake(w.x, w.y, w.z, GLKVector3DotProduct(u, v))
q.w += GLKQuaternionLength(q)
return GLKQuaternionNormalize(q)
}
稍后你会看到翻译成SIMD没那么容易。
OpenGL 到金属
不幸的是,这里没有魔法棒。 Apple 在这方面有一些 WWDC 会议,但他们并没有真正启发我。金属使用两种类型的内核:计算和着色器,计算更容易。然而,在我的例子中,我不得不使用着色器,这对我来说更难掌握。
金属资源
如果您一无所知,这是一个很好的起点金属这是 Ray Wenderlich 网站上的 Metal Tutorial 吗?第二篇文章更有帮助:Ray Wenderlich 网站上的Moving From OpenGL to Metal。两者都有大量参考更多金属材料。
我发现有帮助的另外两篇优秀文章:Donald Pinckney's Blog (Older)。另一位乐于助人的作者:Alex Barbulescu
写这本关于 Metal 的书的人是Warren Moore。他的书和文章是无价的!
要记住的事情
OpenGL使用一个剪辑空间-1 到 1 (z值)。您需要在着色器中考虑到这一点。 Warren Moore 个人建议我确保我的着色器不会返回负数z使用此代码的值:
v.z = v.z * 0.5 + v.w * 0.5;
这避免了完全重做可能使用负数的 OpenGL 代码的需要z值。
这背景颜色的MTL视图不是通过使用该属性设置的,而是设置清除颜色.
从 App 空间到着色器空间的通信是使用结构完成的,必须分别在每个结构中单独定义。例如,在我的应用程序中这个结构:
private struct Uniforms {
let projectionMatrix: simd_float4x4
let attitudeMatrix: simd_float4x4
}
在着色器中定义为:
struct Uniforms {
float4x4 projectionMatrix;
float4x4 attitudeMatrix;
};
这些结构是应用程序定义的。
纹理
如果您使用图像来创建纹理,则有点不同金属.这
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], GLKTextureLoaderOriginBottomLeft, nil];
GLKTextureInfo *info=[GLKTextureLoader textureWithContentsOfFile:path options:options error:&error];
glBindTexture(GL_TEXTURE_2D, info.name);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, IMAGE_SCALING);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, IMAGE_SCALING);
成为
let loader: MTKTextureLoader = MTKTextureLoader(device: mtlDevice)
do {
let texture = try loader.newTexture(cgImage: cgImage, options: [
MTKTextureLoader.Option.origin: MTKTextureLoader.Origin.bottomLeft,
MTKTextureLoader.Option.SRGB: false // yes and image washed out
])
return texture
} catch {
请注意,您需要将原点设置为 bottomLeft。
最后评论
除非你打算真正学习金属深入地,您将进行大量实验并提出问题。有一个测试线束当您花费数小时尝试让您的代码执行您想要的操作时,应用程序将被证明是无价的。
将 GLK 翻译成 simd
任何 Apple 代码肯定都充满了 GLKVectors、GLKMatrices 和相关函数。遗憾的是,没有工具可以转换它们——您必须逐行手工完成,而且有时没有SIMD相等的。有时我使用 Xcode 的搜索和替换,但不经常使用。
GLK -> SIMD
首先,要获取 simd 宏,请将其添加到您的源文件中:导入simd
- GLfloat -> 浮动
- GLint -> Int
- GLKMatrix4 -> simd_float4(SIMD4 的类型别名)
- GLKMatrix4Identity -> matrix_identity_float4x4(不好找)
- GLKMatrix4Invert -> simd_inverse(simd_float4x4)
- GLKMatrix4Make -> simd_float4x4(simd_float4,simd_float4,simd_float4,simd_float4)
- GLKMatrix4MakeFrustum -> 无替换,下面提供的功能
- GLKMatrix4MakeLookAt -> 无替换,下面提供的功能
- GLKMatrix4MakeWithQuaternion -> simd_matrix4x4(simd_quatf)
- GLKMatrix4Multiply -> simd_float4x4 * simd_float4x4
- GLKMatrix4MultiplyVector3 -> 无替换,下面提供的函数
- GLKMatrix4MultiplyVector4 ->simd_float4x4 * simd_float4
- GLK四元数 -> simd_quatf
- GLKQuaternionLength -> simd_quatf.length
- GLKQuaternionMake -> simd_quaternion(_ x: Float, _y: Float, _ z: Float, _ w: Float)
- GLKQuaternionNormalize -> simd_quatf.normalized
- GLKTextureInfo -> MTLTexture
- GLKVector3 -> simd_float3
- GLKVector3CrossProduct -> simd_cross(simd_float3, simd_float3)
- GLKVector3DotProduct -> simd_dot(simd_float3, simd_float3)
- GLKVector3Make -> simd_make_float3(_ x:浮点数,_y:浮点数,_ z:浮点数)
- GLKVector3Normalize -> simd_normalize(simd_float3)
- GLKVector4 -> simd_float4
- GLKVector4Make -> simd_make_float4(_ x:浮点数,_y:浮点数,_ z:浮点数,_ w:浮点数)
我应该注意到 Dash 在挖掘SIMD功能。
上面提到的两个函数:
func simd_make_look_at_float4x4(
eyeX: Float,
eyeY: Float,
eyeZ: Float,
centerX: Float,
centerY: Float,
centerZ: Float,
upX: Float,
upY: Float,
upZ: Float
) -> simd_float4x4 {
// https://stackoverflow.com/questions/9053377/ios-questions-about-camera-information-within-glkmatrix4makelookat-result
let ev = simd_float3(eyeX, eyeY, eyeZ)
let cv = simd_float3(centerX, centerY, centerZ)
let uv = simd_float3(upX, upY, upZ)
let subbed = ev - cv
let n = simd_normalize(subbed)
let cross_p = simd_cross(uv, n)
let u = simd_normalize(cross_p)
let v = simd_cross(n, u)
let c0: simd_float4 = [u[0], v[0], n[0], 0]
let c1: simd_float4 = [u[1], v[1], n[1], 0]
let c2: simd_float4 = [u[2], v[2], n[2], 0]
let v0 = simd_dot(-1*u, ev)
let v1 = simd_dot(-1*v, ev)
let v2 = simd_dot(-1*n, ev)
let c3: simd_float4 = [v0, v1, v2, 1]
let m: simd_float4x4 = simd_float4x4(columns: (c0, c1, c2, c3))
return m
}
func simd_make_frustum_float4x4(frustum: Float, aspectRatio: Float) -> simd_float4x4 {
// https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glFrustum.xml
let left = -frustum
let right = frustum
let bottom = -frustum/aspectRatio
let top = frustum/aspectRatio
let near = PanoramaView.Z_NEAR
let far = PanoramaView.Z_FAR
let m00 = (2.0 * near) / (right - left)
let m11 = (2.0 * near) / (top - bottom)
let m20 = (right + left) / (right - left)
let m21 = (top + bottom) / (top - bottom)
let m22 = -1 * (far + near) / (far - near)
let m23 = Float(-1)
let m32 = -1 * (2 * far * near) / (far - near)
let c0: simd_float4 = [m00, 0, 0, 0]
let c1: simd_float4 = [0, m11, 0, 0]
let c2: simd_float4 = [m20, m21, m22, m23]
let c3: simd_float4 = [0, 0, m32, 0]
let m = simd_float4x4(columns: (c0, c1, c2, c3))
return m
}
// Translated from the original Panorama code
func simd_make_quaternion_from_two_vectors(_ u: simd_float3, _ v: simd_float3) -> simd_quatf {
let w: simd_float3 = simd_cross(u, v)
var q: simd_quatf = simd_quaternion(w.x, w.y, w.z, simd_dot(u, v))
q.real += q.length
return q.normalized
}
在 GLK 和 simd 之间来回翻译
这些函数可以在前面提到的 Panorama 存储库中找到,在一个文件中GLK-金属-Tools.swift.如果按照建议您在控制器单独运行后来回翻译SIMD,您可以在慢慢移除GLK代码。
func glkV3_to_simd(_ v3: GLKVector3) -> simd_float3 {
let v: simd_float3 = simd_make_float3(v3.x, v3.y, v3.z)
return v
}
func simd3_to_glk(_ v3: simd_float3) -> GLKVector3 {
let v = GLKVector3Make(v3[0], v3[1], v3[2])
return v
}
func glkV4_to_simd(_ v3: GLKVector4) -> simd_float4 {
let v: simd_float4 = simd_make_float4(v3.x, v3.y, v3.z, v3.w)
return v
}
func simd4x4_to_glk(_ m: simd_float4x4) -> GLKMatrix4 {
var array: [GLKVector4] = []
for i in 0..<4 {
let fv: simd_float4 = m[i]
let v: GLKVector4 = GLKVector4Make(fv[0], fv[1], fv[2], fv[3]);
array.append(v)
}
let mg: GLKMatrix4 = GLKMatrix4MakeWithColumns(array[0], array[1], array[2], array[3]);
return mg;
}
func glkm4_to_simd(_ m: GLKMatrix4) -> simd_float4x4 {
var array: [simd_float4] = []
for i in 0..<4 {
let fv: GLKVector4 = GLKMatrix4GetColumn(m, Int32(i))
let v: simd_float4 = simd_make_float4(fv[0], fv[1], fv[2], fv[3]);
array.append(v)
}
let ms: simd_float4x4 = simd_matrix(array[0], array[1], array[2], array[3]);
return ms;
}
我在开发过程中使用这些打印例程来检查各种值,您可能也会发现它们有用:
func print4x4SIMD(
msg: String,
m: simd_float4x4
) {
var s = ""
s += "---COL: (msg)
"
let (c0, c1, c2, c3) = m.columns
s += String(format: "[%.2d] %10.4lf %10.4lf %10.4lf %10.4lf
", 0, c0[0], c0[1], c0[2], c0[3])
s += String(format: "[%.2d] %10.4lf %10.4lf %10.4lf %10.4lf
", 1, c1[0], c1[1], c1[2], c1[3])
s += String(format: "[%.2d] %10.4lf %10.4lf %10.4lf %10.4lf
", 2, c2[0], c2[1], c2[2], c2[3])
s += String(format: "[%.2d] %10.4lf %10.4lf %10.4lf %10.4lf
", 3, c3[0], c3[1], c3[2], c3[3])
print("
(s)
")
}
func print4x4GLK(
msg: String,
m: GLKMatrix4
) {
var s = ""
s += "---COL: (msg)
"
s += String(format: "[%.2d] %10.4lf %10.4lf %10.4lf %10.4lf
", 0, m.m00, m.m01, m.m02, m.m03)
s += String(format: "[%.2d] %10.4lf %10.4lf %10.4lf %10.4lf
", 1, m.m10, m.m11, m.m12, m.m13)
s += String(format: "[%.2d] %10.4lf %10.4lf %10.4lf %10.4lf
", 2, m.m20, m.m21, m.m22, m.m23)
s += String(format: "[%.2d] %10.4lf %10.4lf %10.4lf %10.4lf
", 3, m.m30, m.m31, m.m32, m.m33)
print("
(s)
")
}
simd_float4x4 旋转
虽然我还没有使用它,但有一天我可能会需要它(未经测试):
func matrix_from_rotation(radians: Float, v _v: simd_float3) -> simd_float4x4 {
// https://www.haroldserrano.com/blog/rotating-a-2d-object-using-metal
let v: simd_float3 = simd_normalize(_v)
let cos: Float = cos(radians)
let cosp: Float = 1 - cos
let sin: Float = sin(radians)
let col0 = simd_float4(
cos + cosp * v.x * v.x,
cosp * v.x * v.y + v.z * sin,
cosp * v.x * v.z - v.y * sin,
0
)
let col1 = simd_float4(
cosp * v.x * v.y - v.z * sin,
cos + cosp * v.y * v.y,
cosp * v.y * v.z + v.x * sin,
0.0
)
let col2 = simd_float4(
cosp * v.x * v.z + v.y * sin,
cosp * v.y * v.z - v.x * sin,
cos + cosp * v.z * v.z,
0.0
)
let col3 = simd_float4(0, 0, 0, 1)
let m: simd_float4x4 = simd_float4x4(columns: (col0, col1, col2, col3))
return m
}
结论
这个项目花了我大约半年的时间,每周工作一个周末,但它跨越了 18 个月。原因:我花了这么多天没有任何进展,得到奇怪的损坏输出,或者没有输出,当我终于得到主要视图显示金属正如它所做的那样,我把这个项目搁置了。我太累了,无法继续下去。
也就是说,iOS 的末日即将结束,随着岁月的流逝,那个末日越来越近了。
我本来打算停下来的金属使用 GLK 向量和矩阵,但被敦促转换为SIMD现在由沃伦摩尔。
当我终于构建了我公司的应用程序并且没有一个编译器警告与GL套件!