GLKit框架是苹果为快速实现OpenGL ES功能所提供的轻型框架
GLKit框架提供的函数和类减少了创建新的基于着色器的应用程序,减少了现有app所需的依赖于OpenGL ES或OpenGL早期版本提供的顶点着色器或者片元着色器程序的工作。
GLKit主要提供了一下四方面的功能:
1纹理加载让你很容易的从资源文件中加载纹理图片,仅通过几行简单的代码就能让你在后台线程一步加载纹理,更多详细内容请看GLKTextureLoader
2.数学库提供了常用的向量、四元数和矩阵运算。这些实现进行了优化,以提供出色的性能。
3Effects类提供了通用着色效果的标准实现。您可以配置效果和关联的顶点数据;effect将会自动创建并加载一个适当的着色器,GLKit包含三个效果:GLKBaseEffect类实现了OpenGL ES1.1中明暗模型的一个关键子集,GLKReflectionMapEffect类扩展了基本效果以包含反射映射支持,而GLKSkyboxEffect类提供了一个skybox效果的实现。
4.视图和视图控制器提供了OpenGL ES视图和相应视图控制器的标准实现。这减少了创建使用OpenGL ES的iOS应用程序所需的代码量。有关更多信息,请参见GLKView和GLKViewController。
下面介绍如何使用GLKit加载一张纹理图片
//
// ViewController.swift
// GLKitTest
//
// Created by dzq_mac on 2020/5/14.
// Copyright © 2020 dzq_mac. All rights reserved.
//
import UIKit
import GLKit
@available(*, deprecated, message: "ios13")//消除警告
class ViewController: UIViewController {
var glView:GLKView!
var context:EAGLContext?
var effect:GLKBaseEffect!
var bufferID : GLuint = GLuint()
override func viewDidLoad() {
super.viewDidLoad()
//1.初始化
config()
//2.顶点/纹理 坐标
setUpVertexData()
//设置纹理数据
setImage()
}
func config(){
//1创建GKLKView
glView = GLKView(frame: CGRect(x: 20, y: 100, width: UIScreen.main.bounds.width - 40, height: self.view.bounds.size.height - 200))
self.view.addSubview(glView)
//2.设置Context
context = EAGLContext(api: EAGLRenderingAPI.openGLES3)
if let con = context {
EAGLContext.setCurrent(con)
glView.context = con
}
/*3.配置视图创建的渲染缓存区.
(1). drawableColorFormat: 颜色缓存区格式.
简介: OpenGL ES 有一个缓存区,它用以存储将在屏幕中显示的颜色。你可以使用其属性来设置缓冲区中的每个像素的颜色格式。
GLKViewDrawableColorFormatRGBA8888 = 0,
默认.缓存区的每个像素的最小组成部分(RGBA)使用8个bit,(所以每个像素4个字节,4*8个bit)。
GLKViewDrawableColorFormatRGB565,
如果你的APP允许更小范围的颜色,即可设置这个。会让你的APP消耗更小的资源(内存和处理时间)
(2). drawableDepthFormat: 深度缓存区格式
GLKViewDrawableDepthFormatNone = 0,意味着完全没有深度缓冲区
GLKViewDrawableDepthFormat16,
GLKViewDrawableDepthFormat24,
如果你要使用这个属性(一般用于3D游戏),你应该选择GLKViewDrawableDepthFormat16
或GLKViewDrawableDepthFormat24。这里的差别是使用GLKViewDrawableDepthFormat16
将消耗更少的资源
*/
glView.drawableColorFormat = .RGBA8888
glView.drawableDepthFormat = .format16
glView.delegate = self
//4.设置背景颜色
glClearColor(1, 1, 0, 1)
}
func setUpVertexData(){
//1.设置顶点数组(顶点坐标,纹理坐标)
/*
纹理坐标系取值范围[0,1];原点是左下角(0,0);
故而(0,0)是纹理图像的左下角, 点(1,1)是右上角.
*/
let vertexs:[GLfloat] = [
0.5, -0.5, 0.0, 1.0, 0.0, //右下
0.5, 0.5, -0.0, 1.0, 1.0, //右上
-0.5, 0.5, 0.0, 0.0, 1.0, //左上
0.5, -0.5, 0.0, 1.0, 0.0, //右下
-0.5, 0.5, 0.0, 0.0, 1.0, //左上
-0.5, -0.5, 0.0, 0.0, 0.0, //左下
]
/*
顶点数组: 开发者可以选择设定函数指针,在调用绘制方法的时候,直接由内存传入顶点数据,也就是说这部分数据之前是存储在内存当中的,被称为顶点数组
顶点缓存区: 性能更高的做法是,提前分配一块显存,将顶点数据预先传入到显存当中。这部分的显存,就被称为顶点缓冲区
*/
////2.开辟顶点缓存区
//(1).创建顶点缓存区标识符ID
glGenBuffers(1, &bufferID)
//(2).绑定顶点缓存区.(明确作用)
glBindBuffer(GLenum(GL_ARRAY_BUFFER), bufferID)
let size = MemoryLayout<[GLfloat]>.size * 30
glBufferData(GLenum(GL_ARRAY_BUFFER), size, vertexs, GLenum(GL_STATIC_DRAW))
//3.打开读取通道.
/*
(1)在iOS中, 默认情况下,出于性能考虑,所有顶点着色器的属性(Attribute)变量都是关闭的.
意味着,顶点数据在着色器端(服务端)是不可用的. 即使你已经使用glBufferData方法,将顶点数据从内存拷贝到顶点缓存区中(GPU显存中).
所以, 必须由glEnableVertexAttribArray 方法打开通道.指定访问属性.才能让顶点着色器能够访问到从CPU复制到GPU的数据.
注意: 数据在GPU端是否可见,即,着色器能否读取到数据,由是否启用了对应的属性决定,这就是glEnableVertexAttribArray的功能,允许顶点着色器读取GPU(服务器端)数据。
(2)方法简介
glVertexAttribPointer (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr)
功能: 上传顶点数据到显存的方法(设置合适的方式从buffer里面读取数据)
参数列表:
index,指定要修改的顶点属性的索引值,例如
size, 每次读取数量。(如position是由3个(x,y,z)组成,而颜色是4个(r,g,b,a),纹理则是2个.)
type,指定数组中每个组件的数据类型。可用的符号常量有GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT,GL_UNSIGNED_SHORT, GL_FIXED, 和 GL_FLOAT,初始值为GL_FLOAT。
normalized,指定当被访问时,固定点数据值是否应该被归一化(GL_TRUE)或者直接转换为固定点值(GL_FALSE)
stride,指定连续顶点属性之间的偏移量。如果为0,那么顶点属性会被理解为:它们是紧密排列在一起的。初始值为0
ptr指定一个指针,指向数组中第一个顶点属性的第一个组件。初始值为0
*/
glEnableVertexAttribArray(GLuint(GLKVertexAttrib.position.rawValue))
glVertexAttribPointer(GLuint(GLKVertexAttrib.position.rawValue), 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), GLsizei(MemoryLayout<GLfloat>.size * 5), UnsafeRawPointer(bitPattern: 0))
//
glEnableVertexAttribArray(GLuint(GLKVertexAttrib.texCoord0.rawValue))
glVertexAttribPointer(GLuint(GLKVertexAttrib.texCoord0.rawValue), 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), GLsizei(MemoryLayout<GLfloat>.size * 5), UnsafeRawPointer(bitPattern: MemoryLayout<GLfloat>.size * 3))
}
func setImage() {
guard let imagePath = Bundle.main.path(forResource: "timg", ofType: "jpeg", inDirectory: nil),
let image = UIImage(contentsOfFile: imagePath)?.cgImage
else {
return
}
let texture = try? GLKTextureLoader.texture(with: image, options: [GLKTextureLoaderOriginBottomLeft:true,GLKTextureLoaderGrayscaleAsAlpha:true,GLKTextureLoaderApplyPremultiplication:true])
effect = GLKBaseEffect()
if let textureInfo = texture{
effect.texture2d0.enabled = GLboolean(GL_TRUE)
effect.texture2d0.name = textureInfo.name
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
//present(GLKViewController(), animated: true, completion: nil)
}
deinit {
glDeleteBuffers(1, &bufferID)
}
}
//绘制视图的内容
/*
GLKView对象使其OpenGL ES上下文成为当前上下文,并将其framebuffer绑定为OpenGL ES呈现命令的目标。然后,委托方法应该绘制视图的内容。
*/
@available(*, deprecated, message: "ios13")
extension ViewController :GLKViewDelegate{
func glkView(_ view: GLKView, drawIn rect: CGRect) {
glClear(GLbitfield(GL_COLOR_BUFFER_BIT))
effect.prepareToDraw()
glDrawArrays(GLenum(GL_TRIANGLES), 0, 6)
}
}