概述
-
GLKit
框架是為了簡化基于OpenGL
/OpenGL ES
的應(yīng)用開發(fā)诸衔。 - 它內(nèi)置數(shù)學(xué)方法和各種功能,其中的
Effect
有一點點固定管線的感覺岂丘。 - 使用體感是面向?qū)ο缶翰讓尤匀皇敲嫦蜻^程。
案例一:繪制一張圖片
繪制一張圖片
導(dǎo)頭
import UIKit
import GLKit
import OpenGLES.ES3.gl
import OpenGLES.ES3.glext
// 忽略警告
@available(iOS,deprecated:1.0)
UIViewController 改為 GLKViewController
// 使用 GLKViewController
class ViewController: GLKViewController {
View 改為 GLKView
[圖片上傳失敗...(image-de7578-1596009288359)]
定義 EAGLContext 和 GLKBaseEffect
// 上下文
var context: EAGLContext?
// 類似 OpenGL里的固定管線
var effect: GLKBaseEffect?
viewDidLoad() 3個準(zhǔn)備方法
override func viewDidLoad() {
super.viewDidLoad()
// 1. 準(zhǔn)備好 context 和 glkView
setupConfig()
// 2. 頂點數(shù)據(jù) => 頂點緩沖區(qū) => 頂點著色器
setupVertexData()
// 3. 圖片文件 => 紋理 => effect
setupTexture()
// (4.) 代理方法里 設(shè)置繪制
}
setupConfig()
func setupConfig() {
// 1. 初始化上下文
context = EAGLContext(api: .openGLES3)
guard let _context = context else {
print("Initial ES context Failed!!")
return
}
// 2. 可以有多個上下文硫椰,但只能有一個當(dāng)前上下文
EAGLContext.setCurrent(_context)
// 3. 獲取 GLKView
guard let glkView = view as? GLKView else {
print("Convert UIView to GLKView Failed!!")
return
}
// view 綁定上下文
glkView.context = _context
// 顏色緩沖區(qū)格式
glkView.drawableColorFormat = .RGBA8888
// 深度緩沖區(qū)格式 (可以選16位)
glkView.drawableDepthFormat = .format24
// 4. 設(shè)置背景顏色 橘色繁调,這里也能用 glkView.backgroundColor
glClearColor(1.0, 0.4, 0.0, 1.0)
}
setupVertexData()
func setupVertexData() {
// 1. 頂點數(shù)據(jù),包含 頂點坐標(biāo)(xyz) 和 紋理坐標(biāo)(st)
// 之后載入照片用到2個三角形靶草,共6個頂點
let vertextData: [GLfloat] = [
0.8, -0.2, 0.0, 1.0, 0.0,
0.8, 0.2, 0.0, 1.0, 1.0,
-0.8, 0.2, 0.0, 0.0, 1.0,
// x y z s t
0.8, -0.2, 0.0, 1.0, 0.0,
-0.8, 0.2, 0.0, 0.0, 1.0,
-0.8, -0.2, 0.0, 0.0, 0.0,
]
// 2. 創(chuàng)建 緩沖區(qū)(拿房間號)
var bufferID = GLuint()
glGenBuffers(1, &bufferID)
// 3. 綁定 頂點緩沖區(qū)(指定用途蹄胰,還能指定為 深度緩沖區(qū)等)
glBindBuffer(GLenum(GL_ARRAY_BUFFER), bufferID)
// 4. copy 內(nèi)存 => 顯存(頂點緩沖區(qū))
glBufferData(
GLenum(GL_ARRAY_BUFFER), //copy到哪:頂點緩沖區(qū)
MemoryLayout<GLfloat>.stride * vertextData.count, //空間大小
vertextData, //內(nèi)容
GLenum(GL_STATIC_DRAW)) //用途:繪制
// 5.1. 打開通道 以使用緩沖區(qū)數(shù)據(jù)
glEnableVertexAttribArray(GLuint(GLKVertexAttrib.position.rawValue))
// 5.2. 頂點著色器(頂點坐標(biāo)) 讀取 <= 頂點緩沖區(qū)
glVertexAttribPointer(
GLuint(GLKVertexAttrib.position.rawValue), //傳給誰:頂點position
3, //position 由 xyz 組成
GLenum(GL_FLOAT), //數(shù)據(jù)類型
GLboolean(GL_FALSE), //是否需要歸一化
GLsizei(MemoryLayout<GLfloat>.stride * 5), //每次讀取的步長
UnsafeRawPointer(bitPattern: MemoryLayout<GLfloat>.stride * 0)) //指針起始偏移
// 6.1. 打開通道 以使用緩沖區(qū)數(shù)據(jù)
glEnableVertexAttribArray(GLuint(GLKVertexAttrib.texCoord0.rawValue))
// 6.2. 頂點著色器(紋理坐標(biāo)) 讀取 <= 頂點緩沖區(qū)
glVertexAttribPointer(
GLuint(GLKVertexAttrib.texCoord0.rawValue), //傳給誰:頂點position
2, //tex 由 st 組成
GLenum(GL_FLOAT), //數(shù)據(jù)類型
GLboolean(GL_FALSE), //是否需要歸一化
GLsizei(MemoryLayout<GLfloat>.stride * 5), //每次讀取的步長
UnsafeRawPointer(bitPattern: MemoryLayout<GLfloat>.stride * 3)) //指針起始偏移
}
setupTexture()
func setupTexture() {
// 1. 圖片路徑
guard let filePath = Bundle.main.path(forResource: "pcr", ofType: "jpg") else {
print("load image failed!!")
return
}
// 2.1. 由于紋理坐標(biāo)原點為左下角,圖片顯示原點為左上角奕翔,需要設(shè)置 (否則是倒的)
let options = [GLKTextureLoaderOriginBottomLeft : NSNumber(booleanLiteral: true)]
// 2.2. 圖片 轉(zhuǎn) 紋理
guard let textureInfo = try? GLKTextureLoader.texture(withContentsOfFile: filePath, options: options) else {
print("convert image to texture failed!!")
return
}
// 3.1. 初始化 GLKBaseEffect
effect = GLKBaseEffect()
guard let _effect = effect else {
print("Initial GLKBaseEffect failed!!")
return
}
// 3.2. 打開 effect 的紋理通道
_effect.texture2d0.enabled = GLboolean(GL_TRUE)
// 3.3. 綁定 紋理
_effect.texture2d0.name = textureInfo.name
}
代理方法 glkView(_: , drawIn: ) 設(shè)置繪制
- 相當(dāng)于
OpenGL
的RenderScene()
// MARK:- GLKViewDelegate
override func glkView(_ view: GLKView, drawIn rect: CGRect) {
// 1. 清空 顏色緩沖區(qū)
glClear(GLbitfield(GL_COLOR_BUFFER_BIT))
guard let _effect = effect else {
print("Initial GLKBaseEffect failed!!")
return
}
// 2. 準(zhǔn)備繪制
_effect.prepareToDraw()
// 3. 繪制:三角形裕寨,從第0個頂點開始,共6個頂點
glDrawArrays(GLenum(GL_TRIANGLES), 0, 6)
}
案例二:立方體旋轉(zhuǎn)
立方體旋轉(zhuǎn)
導(dǎo)頭派继、改
GLKView
和GLKViewController
與案例一是一樣的宾袜,不再贅述。關(guān)注注釋:基本上只注釋案例一里沒有的代碼驾窟。
基本的聲明定義
// 頂點數(shù)據(jù) 結(jié)構(gòu)體
struct MDVertex {
let positionCoord: GLKVector3
let textureCoord: GLKVector2
let normalVec: GLKVector3
}
@available(iOS,deprecated: 12.0)
class ViewController: GLKViewController {
var context: EAGLContext!
var effect: GLKBaseEffect!
// 頂點數(shù)據(jù) 數(shù)組
var vertexes: [MDVertex]!
// 頂點緩沖區(qū)ID
var vertexBufferID: GLuint!
// 記錄旋轉(zhuǎn)角度
var angle: Int = 0
// 計時器
var displayLink: CADisplayLink!
deinit 釋放
deinit {
if EAGLContext.current() == context {
EAGLContext.setCurrent(nil)
}
if let _ = vertexBufferID {
glDeleteBuffers(1, &vertexBufferID)
vertexBufferID = 0
}
displayLink.invalidate()
}
viewDidLoad() 2個準(zhǔn)備方法
override func viewDidLoad() {
super.viewDidLoad()
// 1.1. 準(zhǔn)備好 context 和 glkView
// 1.2. 頂點數(shù)據(jù) => 頂點緩沖區(qū) => 頂點著色器
// 1.3. 圖片文件 => 紋理 => effect
commonInit()
// 2. 計時器庆猫,做旋轉(zhuǎn)動畫:調(diào)用的 update()方法 改變effect的 MV矩陣并重繪
addCADisplayLink()
// (3.) 代理方法里 設(shè)置繪制
}
commonInit()
- 相當(dāng)于案例一的3個準(zhǔn)備方法。
func commonInit() {
// context / glkView 的一系列操作
context = EAGLContext(api: .openGLES3)
assert(context != nil, "Initialize context failed!!")
EAGLContext.setCurrent(context)
guard let glkView = view as? GLKView else { return }
glkView.context = context
glkView.drawableColorFormat = .RGBA8888
// 啟用 深度緩沖區(qū)
glkView.drawableDepthFormat = .format24
glkView.backgroundColor = .clear
// 深度緩沖區(qū) 默認(rèn)(0, 1)绅络,這里相當(dāng)于翻轉(zhuǎn)Z軸月培,使正方形朝屏幕外
glDepthRangef(1, 0)
// 加載紋理的一系列操作
guard let filePath = Bundle.main.path(forResource: "pcr", ofType: "jpg") else { return }
guard let cgImage = UIImage(contentsOfFile: filePath)?.cgImage else { return }
let options = [GLKTextureLoaderOriginBottomLeft : NSNumber(booleanLiteral: true)]
guard let textureInfo = try? GLKTextureLoader.texture(with: cgImage, options: options) else { return }
// effect
effect = GLKBaseEffect()
// effect 設(shè)置紋理
effect.texture2d0.name = textureInfo.name
// effect 設(shè)置光照
effect.light0.enabled = GLboolean(GL_TRUE)
effect.light0.diffuseColor = GLKVector4Make(1, 1, 1, 1)
effect.light0.position = GLKVector4Make(0.5, 0, 5, 1)
// 頂點坐標(biāo)
let ver1 = GLKVector3Make(-0.5, 0.5, 0.5)
let ver2 = GLKVector3Make(-0.5, -0.5, 0.5)
let ver3 = GLKVector3Make( 0.5, 0.5, 0.5)
let ver4 = GLKVector3Make( 0.5, -0.5, 0.5)
let ver5 = GLKVector3Make( 0.5, 0.5, -0.5)
let ver6 = GLKVector3Make(-0.5, 0.5, -0.5)
let ver7 = GLKVector3Make( 0.5, -0.5, -0.5)
let ver8 = GLKVector3Make(-0.5, -0.5, -0.5)
// 紋理坐標(biāo)
let tex1 = GLKVector2Make(0, 1)
let tex2 = GLKVector2Make(0, 0)
let tex3 = GLKVector2Make(1, 1)
let tex4 = GLKVector2Make(1, 0)
// 法線向量
let normal1 = GLKVector3Make( 0, 0, 1)
let normal2 = GLKVector3Make( 0, 1, 0)
let normal3 = GLKVector3Make( 0,-1, 0)
let normal4 = GLKVector3Make(-1, 0, 0)
let normal5 = GLKVector3Make( 1, 0, 0)
let normal6 = GLKVector3Make( 0, 0,-1)
// 用于初始化的隨便取值的頂點數(shù)據(jù)
let meanlessVertex =
MDVertex(positionCoord: ver1, textureCoord: tex1, normalVec: normal1)
// 先用初始化來開辟內(nèi)存空間
vertexes = [MDVertex].init(repeating: meanlessVertex, count: 36)
// 初始化36個頂點數(shù)據(jù):每個三角形3個頂點嘁字,每個面2個三角形,一個立方體6個面杉畜,3x2x6 = 36
vertexes[00] = MDVertex(positionCoord: ver1, textureCoord: tex1, normalVec: normal1)
vertexes[01] = MDVertex(positionCoord: ver2, textureCoord: tex2, normalVec: normal1)
vertexes[02] = MDVertex(positionCoord: ver3, textureCoord: tex3, normalVec: normal1)
vertexes[03] = MDVertex(positionCoord: ver2, textureCoord: tex2, normalVec: normal1)
vertexes[04] = MDVertex(positionCoord: ver3, textureCoord: tex3, normalVec: normal1)
vertexes[05] = MDVertex(positionCoord: ver4, textureCoord: tex4, normalVec: normal1)
vertexes[06] = MDVertex(positionCoord: ver3, textureCoord: tex3, normalVec: normal2)
vertexes[07] = MDVertex(positionCoord: ver1, textureCoord: tex1, normalVec: normal2)
vertexes[08] = MDVertex(positionCoord: ver5, textureCoord: tex4, normalVec: normal2)
vertexes[09] = MDVertex(positionCoord: ver1, textureCoord: tex1, normalVec: normal2)
vertexes[10] = MDVertex(positionCoord: ver5, textureCoord: tex4, normalVec: normal2)
vertexes[11] = MDVertex(positionCoord: ver6, textureCoord: tex2, normalVec: normal2)
vertexes[12] = MDVertex(positionCoord: ver4, textureCoord: tex3, normalVec: normal3)
vertexes[13] = MDVertex(positionCoord: ver2, textureCoord: tex1, normalVec: normal3)
vertexes[14] = MDVertex(positionCoord: ver7, textureCoord: tex4, normalVec: normal3)
vertexes[15] = MDVertex(positionCoord: ver2, textureCoord: tex1, normalVec: normal3)
vertexes[16] = MDVertex(positionCoord: ver7, textureCoord: tex4, normalVec: normal3)
vertexes[17] = MDVertex(positionCoord: ver8, textureCoord: tex2, normalVec: normal3)
vertexes[18] = MDVertex(positionCoord: ver1, textureCoord: tex3, normalVec: normal4)
vertexes[19] = MDVertex(positionCoord: ver2, textureCoord: tex1, normalVec: normal4)
vertexes[20] = MDVertex(positionCoord: ver6, textureCoord: tex4, normalVec: normal4)
vertexes[21] = MDVertex(positionCoord: ver2, textureCoord: tex1, normalVec: normal4)
vertexes[22] = MDVertex(positionCoord: ver6, textureCoord: tex4, normalVec: normal4)
vertexes[23] = MDVertex(positionCoord: ver8, textureCoord: tex2, normalVec: normal4)
vertexes[24] = MDVertex(positionCoord: ver3, textureCoord: tex3, normalVec: normal5)
vertexes[25] = MDVertex(positionCoord: ver4, textureCoord: tex1, normalVec: normal5)
vertexes[26] = MDVertex(positionCoord: ver5, textureCoord: tex4, normalVec: normal5)
vertexes[27] = MDVertex(positionCoord: ver4, textureCoord: tex1, normalVec: normal5)
vertexes[28] = MDVertex(positionCoord: ver5, textureCoord: tex4, normalVec: normal5)
vertexes[29] = MDVertex(positionCoord: ver7, textureCoord: tex2, normalVec: normal5)
vertexes[30] = MDVertex(positionCoord: ver6, textureCoord: tex1, normalVec: normal6)
vertexes[31] = MDVertex(positionCoord: ver8, textureCoord: tex2, normalVec: normal6)
vertexes[32] = MDVertex(positionCoord: ver5, textureCoord: tex3, normalVec: normal6)
vertexes[33] = MDVertex(positionCoord: ver8, textureCoord: tex2, normalVec: normal6)
vertexes[34] = MDVertex(positionCoord: ver5, textureCoord: tex3, normalVec: normal6)
vertexes[35] = MDVertex(positionCoord: ver7, textureCoord: tex4, normalVec: normal6)
// 頂點緩沖區(qū)的一系列操作
vertexBufferID = GLuint()
glGenBuffers(1, &vertexBufferID)
glBindBuffer(GLenum(GL_ARRAY_BUFFER), vertexBufferID)
// 頂點數(shù)據(jù)數(shù)組的大小 = 40x36 = 1440
let bufferSize: GLsizeiptr = MemoryLayout<MDVertex>.stride * vertexes.count
// copy頂點數(shù)組 內(nèi)存 => 顯存
glBufferData(
GLenum(GL_ARRAY_BUFFER),
bufferSize,
vertexes,
GLenum(GL_STATIC_DRAW))
/*
glVertexAttribPointer函數(shù)的最后一個參數(shù)纪蜒,偏移值,要注意下此叠!
MDVertex結(jié)構(gòu)體 內(nèi)含 GLKVector3,GLKVector2,GLKVector3
獨立使用時:內(nèi)存占用大小 GLKVector3 = 12纯续,GLKVector2 = 8
但在結(jié)構(gòu)體:內(nèi)存占用大小 GLKVector3 = 16,GLKVector2 = 8
詳情查閱 "字節(jié)對齊"
所以 MDVertex結(jié)構(gòu)體 內(nèi)存占用大小 = 16+8+16 = 40
*/
// 頂點
glEnableVertexAttribArray(GLuint(GLKVertexAttrib.position.rawValue))
glVertexAttribPointer(
GLuint(GLKVertexAttrib.position.rawValue),
3,
GLenum(GL_FLOAT),
GLboolean(GL_FALSE),
GLsizei(MemoryLayout<MDVertex>.stride),
UnsafeRawPointer(bitPattern: 0))
// 紋理
glEnableVertexAttribArray(GLuint(GLKVertexAttrib.texCoord0.rawValue))
glVertexAttribPointer(
GLuint(GLKVertexAttrib.texCoord0.rawValue),
2,
GLenum(GL_FLOAT),
GLboolean(GL_FALSE),
GLsizei(MemoryLayout<MDVertex>.stride),
UnsafeRawPointer(bitPattern: 16))
// 法線
glEnableVertexAttribArray(GLuint(GLKVertexAttrib.normal.rawValue))
glVertexAttribPointer(
GLuint(GLKVertexAttrib.normal.rawValue),
3,
GLenum(GL_FLOAT),
GLboolean(GL_FALSE),
GLsizei(MemoryLayout<MDVertex>.stride),
UnsafeRawPointer(bitPattern: 16 + 8))
}
addCADisplayLink()
func addCADisplayLink() {
// 記錄旋轉(zhuǎn)角度
angle = 0
// 定時器調(diào)用方法 update
displayLink = CADisplayLink(target: self, selector: #selector(update))
// 加入到 Runloop
displayLink.add(to: RunLoop.main, forMode: RunLoop.Mode.common)
//CADisplayLink 類似定時器,提供一個周期性調(diào)用.屬于QuartzCore.framework中.
//具體可以參考該博客 https://www.cnblogs.com/panyangjun/p/4421904.html
}
update()
@objc
func update() {
// 1. 改變旋轉(zhuǎn)角度
angle = (angle + 2) % 360
// 2. MV矩陣 = 繞 (0.3, 1, 0.7)軸線 旋轉(zhuǎn) angle角度(轉(zhuǎn)弧度)
effect.transform.modelviewMatrix = GLKMatrix4MakeRotation(
GLKMathDegreesToRadians(Float(angle)),
0.3,
1,
0.7)
// 3. 重新渲染
if let glkView = view as? GLKView {
glkView.display()
}
}
代理方法 glkView(_: , drawIn: ) 設(shè)置繪制
// MARK:- GLKViewDelegate
override func glkView(_ view: GLKView, drawIn rect: CGRect) {
glEnable(GLenum(GL_DEPTH_TEST))
glClear(GLbitfield(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT))
effect.prepareToDraw()
glClearColor(0.8, 0.8, 0.8, 1.0)
glDrawArrays(GLenum(GL_TRIANGLES), 0, GLsizei(vertexes.count))
}