
override func viewDidLoad() {
super.viewDidLoad()
// 1. 设置上下文
setupContext()
// 2. 设置飞机的GLKBaseEffect
setupBaseEffect()
// 3. 将顶点,法线数据从cpu 复制到 gpu
setupBuffers()
// 4. 开启深度测试和剔除 不然会绘制背部,和出现闪烁效果
glEnable(GLenum(GL_CULL_FACE))
glEnable(GLenum(GL_DEPTH_TEST))
// 5. 加载天空盒子的天空纹理
setupSkyBoxTexture()
// 6. 设置视角
setMatrices()
}
设置上下文
创建EAGLContext对象,将该对象赋给GLKView对象,然后设置为当前上下文;
func setupContext() {
let view = self.view as! GLKView
view.context = context!
view.drawableColorFormat = .RGBA8888
view.drawableDepthFormat = .format24
EAGLContext.setCurrent(context!)
}
设置飞机的BaseEffect
主要为了设置绘制飞机模型的GLKBaseEffect,并设置一个光照信息;
// 设置飞机的BaseEffect
func setupBaseEffect() {
baseEffect.light0.enabled = GLboolean(GL_TRUE)
baseEffect.light0.position = GLKVector4Make(0.0, 0.0, 2.0, 1.0)
baseEffect.light0.specularColor = GLKVector4Make(0.25, 0.25, 0.25, 1.0)
baseEffect.light0.diffuseColor = GLKVector4Make(0.75, 0.75, 0.75, 1.0)
baseEffect.lightingType = .perPixel
}
设置缓存,将数据从cpu 复制到 gpu
创建两个缓存,用来存放飞机模型的顶点和法线数组,并使用glBufferData方法,将数据从cpu 复制到gpu;
func setupBuffers() {
// 1. 设置顶点缓存
var buffer:GLuint = 0
glGenBuffers(1, &buffer)
glBindBuffer(GLenum(GL_ARRAY_BUFFER), buffer)
var starshipPositions1 = starshipPositions
glBufferData(GLenum(GL_ARRAY_BUFFER), MemoryLayout.size(ofValue: starshipPositions1), &starshipPositions1 , GLenum(GL_STATIC_DRAW))
glEnableVertexAttribArray(GLuint(GLKVertexAttrib.position.rawValue)) // 设置顶点可见
glVertexAttribPointer(GLuint(GLKVertexAttrib.position.rawValue), 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), GLsizei(MemoryLayout<GLfloat>.stride*0), nil) // 设置顶点数据的读取方式
mPositionBuffer = buffer
// 2. 设置法线缓存
glGenBuffers(1, &buffer)
glBindBuffer(GLenum(GL_ARRAY_BUFFER), buffer)
starshipPositions1 = starshipNormals
glBufferData(GLenum(GL_ARRAY_BUFFER), MemoryLayout.size(ofValue: starshipPositions1), &starshipPositions1 , GLenum(GL_STATIC_DRAW))
glEnableVertexAttribArray(GLuint(GLKVertexAttrib.normal.rawValue)) // 设置法线可见
glVertexAttribPointer(GLuint(GLKVertexAttrib.normal.rawValue), 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), GLsizei(MemoryLayout<GLfloat>.stride*0), nil) // 设置发现数据的读取方式
mNormalBuffer = buffer
}
加载天空的纹理
使用苹果GLKit框架的GLKTextureLoader来加载纹理,更加方便;然后将纹理设置到天空盒子对象GLKSkyboxEffect;
func setupSkyBoxTexture() {
let path = Bundle.main.path(forResource: "image", ofType: "png")!
let url = URL.init(fileURLWithPath: path)
let textureInfo = try? GLKTextureLoader.cubeMap(withContentsOf: url, options: nil)
guard let textureI = textureInfo else {
return
}
skyboxEffect.textureCubeMap.name = textureI.name
skyboxEffect.textureCubeMap.target = GLKTextureTarget(rawValue: textureI.target)!
}
设置模型视图矩阵
由于在天空盒子中,每一帧都会改变观察者的位置,所以在准备刷新每一帧时,都将更新观察着的位置;观察者围绕着飞机做旋转,所以使用观察者位置(eyePosition)可以设置为圆形环绕;做半径为5的逆时针环绕;

等价于
eyePosition = GLKVector3Make(5.0 * sin(angle), -5.0, 5.0 * cos(angle)) // 眼睛位置
func setMatrices() {
let aspectRatio: GLfloat = GLfloat(self.view.bounds.width / self.view.bounds.height)
baseEffect.transform.projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(85.0), aspectRatio, 0.2, 23.0)
baseEffect.transform.modelviewMatrix = GLKMatrix4MakeLookAt(eyePosition.x, eyePosition.y, eyePosition.z, lookAtPosition.x, lookAtPosition.y, lookAtPosition.z, upVector.x, upVector.y, upVector.z)
skyboxEffect.center = self.eyePosition
skyboxEffect.transform.projectionMatrix = baseEffect.transform.projectionMatrix
skyboxEffect.transform.modelviewMatrix = baseEffect.transform.modelviewMatrix
angle += 0.01
eyePosition = GLKVector3Make(5.0 * sin(angle), -5.0, 5.0 * cos(angle)) // 眼睛位置
lookAtPosition = GLKVector3Make(0.0 , 1.5 + -5.0 * sin(0.3 * angle), 0.0) // 观察的位置, 为了效果更好上下浮动
}
绘制飞机和天空盒子
需要在每一帧刷新时的GLKViewDelegate代理方法中,来绘制模型;
override func glkView(_ view: GLKView, drawIn rect: CGRect) {
glClearColor(0.5, 0.1, 0.1, 1.0)
glClear(GLbitfield(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT))
// 1. 改变视角
setMatrices()
// 2. 绘制天空盒子
skyboxEffect.prepareToDraw()
glDepthMask(GLboolean(GL_FALSE))
skyboxEffect.draw()
glDepthMask(GLboolean(GL_TRUE))
//glBindBuffer(GLenum(GL_ELEMENT_ARRAY_BUFFER), 0)
//glBindTexture(GLenum(GL_TEXTURE_CUBE_MAP), 0)
// 3. 读取飞机模型数据
glBindBuffer(GLenum(GL_ARRAY_BUFFER), mPositionBuffer!);
glEnableVertexAttribArray(GLuint(GLKVertexAttrib.position.rawValue));
glVertexAttribPointer(GLuint(GLKVertexAttrib.position.rawValue), 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 0, nil);
glBindBuffer(GLenum(GL_ARRAY_BUFFER), mNormalBuffer!);
glEnableVertexAttribArray(GLuint(GLKVertexAttrib.normal.rawValue));
glVertexAttribPointer(GLuint(GLKVertexAttrib.normal.rawValue), 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 0, nil);
// 转换数据 将元组变为数组
let diffusesM = Mirror(reflecting: starshipDiffuses)
let specularsM = Mirror(reflecting: starshipSpeculars)
let firstsM = Mirror(reflecting: starshipFirsts)
let countsM = Mirror(reflecting: starshipCounts)
var diffusesArr:[ (GLfloat, GLfloat, GLfloat)] = []
var specularsArr:[ (GLfloat, GLfloat, GLfloat)] = []
var firstsArr:[ GLint] = []
var countsArr:[ GLint] = []
for diffuses in diffusesM.children {
let value = diffuses.value as! (GLfloat, GLfloat, GLfloat)
diffusesArr.append(value)
}
for speculars in specularsM.children {
let value = speculars.value as! (GLfloat, GLfloat, GLfloat)
specularsArr.append(value)
}
for firsts in firstsM.children {
let value = firsts.value as! (GLint)
firstsArr.append(value)
}
for counts in countsM.children {
let value = counts.value as! (GLint)
countsArr.append(value)
}
// 4. 绘制飞机 需要绘制不同的颜色,所以需要for循环
for i in 0..<diffusesArr.count {
let diffusesValue = diffusesArr[I]
let specularsValue = specularsArr[I]
let firstValue = firstsArr[I]
let countValue = countsArr[I]
self.baseEffect.material.diffuseColor = GLKVector4Make(diffusesValue.0, diffusesValue.1, diffusesValue.2, 1.0);
self.baseEffect.material.specularColor = GLKVector4Make(specularsValue.0, specularsValue.1, specularsValue.2, 1.0);
baseEffect.prepareToDraw()
glDrawArrays(GLenum(GL_TRIANGLES), firstValue, countValue);
}
// 如果不想使用多种颜色的飞机,可以这样写,比较简洁
// baseEffect.prepareToDraw()
// glDrawArrays(GLenum(GL_TRIANGLES), 0, 30+33+3); // 30+33+3 == starshipCounts
}
源代码demo
网友评论