内容简介:前面对 OpenGL ES3.0做了个基本的介绍,这节我们尝试着来写一个OpenGL ES 的 “hello word”程序 — 画一个三角形输入三角形顶点和色值 –> openGL 处理 –> CAEAGLLayer 显示
前面对 OpenGL ES3.0做了个基本的介绍,这节我们尝试着来写一个OpenGL ES 的 “hello word”程序 — 画一个三角形
效果
EAGLContext 和 CAEAGLLayer
- EAGLContext对象是管理OpenGL ES渲染上下文,若想使用OpenGL ES 进行绘制工作,则必须一个上下文对象。该对象管理者openGL ES 绘制使用的上下文对象
-
CAEAGLLayer 将OpenGL ES 渲染结果显示的屏幕上的layer (TODO:开一篇单独介绍)
流程
输入三角形顶点和色值 –> openGL 处理 –> CAEAGLLayer 显示
初始化 OpenGL 环境
var context: EAGLContext? = EAGLContext(api: .openGLES3)
var glLayer: CAEAGLLayer { return layer as! CAEAGLLayer }
open override class var layerClass: Swift.AnyClass {
return CAEAGLLayer.self
}
private func setupLlayer() {
glLayer.opacity = 1.0
glLayer.drawableProperties = [
kEAGLDrawablePropertyRetainedBacking: true,
kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8,
]
}
private func setupContext() {
if context == nil {
context = EAGLContext(api: .openGLES2)
}
assert(context != nil && EAGLContext.setCurrent(context), "初始化环境失败")
}
初始化 frameBuffer 和 renderBuffer
为什么要用两个buffer,这个跟OpenGL 内部优化有关系(TODO:另外开一篇博客,写好了贴链接)
frameBuffer 负责管理buffer缓冲区的和显示buffer切换。
renderBuffer 负责将渲染结果推到CAEAGLLayer显示。
//初始化 frameBuff
private func setupFrameBuffer() {
deleteFrameBuff()
glGenFramebuffers(1, &frameBuffer)
glBindFramebuffer(GLenum(GL_FRAMEBUFFER), frameBuffer)
glFramebufferRenderbuffer(
GLenum(GL_FRAMEBUFFER),
GLenum(GL_COLOR_ATTACHMENT0),
GLenum(GL_RENDERBUFFER),
renderBuffer)
}
private func deleteFrameBuff() {
if frameBuffer != 0 {
glDeleteBuffers(1, &frameBuffer)
frameBuffer = 0
}
}
//初始化 renderBuffer
private func setupRenderBuffer() {
deleteRenderBuffer()
glGenRenderbuffers(1, &renderBuffer)
glBindRenderbuffer(GLenum(GL_RENDERBUFFER), renderBuffer)
context?.renderbufferStorage(Int(GL_RENDERBUFFER), from: glLayer)
}
private func deleteRenderBuffer() {
if renderBuffer != 0 {
glDeleteBuffers(1, &renderBuffer)
renderBuffer = 0
}
}
- glGenFramebuffers(_ n: GLsizei, _ framebuffers: UnsafeMutablePointer
!) 申请 n 个 frameBuffer 并返回申请的 frameBuffer的id列表 - glBindRenderbuffer(_ target: GLenum, _ renderbuffer: GLuint) 将申请到的 frameBuffer 绑定到 OpenGL ES 中
- glFramebufferRenderbuffer 将frameBuffer 和 renderbuffer关联
- glGenRenderbuffers:申请n个renderBuffer
- glBindRenderbuffer:将申请到的 renderBuffer 绑定到 OpenGL ES 中
- glDeleteBuffers: 删除指定的buffer
检查 frameBuffer 状态
private func checkFrameBuffer() throws -> Bool {
let status: GLenum = glCheckFramebufferStatus(GLenum(GL_FRAMEBUFFER))
var result: Bool = false
var errorMessage: String = ""
print("\(GLenum(GL_FRAMEBUFFER_UNSUPPORTED))," + "\(GLenum(GL_FRAMEBUFFER_COMPLETE))," + "\(GLenum(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT))," + "\(GLenum(GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS)),")
switch status {
case GLenum(GL_FRAMEBUFFER_UNSUPPORTED):
errorMessage = "framebuffer不支持该格式"
result = false
case GLenum(GL_FRAMEBUFFER_COMPLETE):
print("frame buffer 创建成功")
result = true
case GLenum(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT):
errorMessage = "Framebuffer不完整 缺失组件"
result = false
case GLenum(GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS):
errorMessage = "Framebuffer 不完整, 附加图片必须要指定大小"
result = false
default:
errorMessage = "未知错误"
result = false
}
if errorMessage.isEmpty == false {
let error: NSError = NSError(domain: "com.colin.error", code: Int(status), userInfo: ["errorMSG": errorMessage])
throw error
}
return result
}
编写shader脚本
- 顶点着色器脚本
attribute vec4 position;
attribute vec4 color;
varying vec4 colorVarying;
void main(void) {
colorVarying = color;
gl_Position = position;
}
- 片段着色器脚本
varying lowp vec4 colorVarying;
void main(void) {
gl_FragColor = colorVarying;
}
编译、链接shader脚本
open class ShaderCompiler{
open class func loadShader(_ vertPath:String,fragPath:String)throws -> GLuint {
var verShader:GLuint = 0,fragShader:GLuint = 0
do {
verShader = try self.compileShader(vertPath, type:GLenum(GL_VERTEX_SHADER))
fragShader = try self.compileShader(fragPath, type: GLenum(GL_FRAGMENT_SHADER))
//创建着色程序
let program:GLuint = glCreateProgram()
//装配 着色程序
glAttachShader(program, verShader)
glAttachShader(program, fragShader)
glLinkProgram(program)
programLog(GLenum(GL_LINK_STATUS), program: program)
return program
} catch let error {
print(error)
throw error
}
}
open class func compileShader(_ shaderPath:String,type:GLenum)throws -> GLuint{
do {
let shaderStr:String = try String.init(contentsOfFile: shaderPath)
let shaderStringUTF8 = shaderStr.cString(using:.utf8)
var shaderStringUTF8Pointer = UnsafePointer<GLchar>(shaderStringUTF8)
var shaderStringLength: GLint = GLint(Int32(shaderStr.count))
let shaderHandle:GLuint = glCreateShader(type)
glShaderSource(shaderHandle, 1,&shaderStringUTF8Pointer, &shaderStringLength)
glCompileShader(shaderHandle)
shaderLog(GLenum(GL_COMPILE_STATUS), shader: shaderHandle)
return shaderHandle
} catch let error {
print(error)
throw error
}
}
open class func programLog(_ statue:GLenum,program:GLuint) {
var linkSucc:GLint = 0
glGetProgramiv(program, GLenum(statue), &linkSucc)
if linkSucc == GL_FALSE
{
var message:Array<GLchar> = Array<GLchar>.init(repeating: 0, count: 256)
glGetProgramInfoLog(program, GLsizei(MemoryLayout<GLchar>.stride * message.count), nil, &message[0])
let msg = String.init(cString: message)
print("gl link program fail msg = \(msg)")
exit(1)
} else {
print("link ok")
}
}
open class func shaderLog(_ statue:GLenum,shader:GLuint){
var status:GLint = 0
glGetShaderiv(shader, statue, &status)
if status == GL_FALSE {
var message:Array<GLchar> = Array<GLchar>.init(repeating: 0, count: 256)
glGetShaderInfoLog(shader, GLsizei(MemoryLayout<GLchar>.stride * message.count), nil, &message[0])
let msg = String.init(cString: message)
print("glGetShaderiv shaderIngoLog type = \(statue) shader = \(shader) "+" \(msg)")
exit(1)
} else {
print("\(statue) \(shader) shader 操作成功")
}
}
}
初始化顶点坐标
//前三位为顶点坐标 后四位为顶点色值rgba var vertices: [GLfloat] = [ -0.5, 0.5, 1.0, 1, 0, 0, 1, -0.5, -0.5, 1.0, 0, 1, 0, 1, 0.5, -0.5, 1.0, 0, 0, 1, 1, ]
将顶点坐标推给OpenGL
private func setupVBOs() {
var vertexBuffer: GLuint = 0
glGenBuffers(1, &vertexBuffer)
glBindBuffer(GLenum(GL_ARRAY_BUFFER), vertexBuffer)
glBufferData(GLenum(GL_ARRAY_BUFFER),
MemoryLayout<GLfloat>.size * vertices.count,
vertices,
GLenum(GL_STATIC_DRAW))
}
渲染
private func render() {
glBindFramebuffer(GLenum(GL_FRAMEBUFFER), frameBuffer)
glBindRenderbuffer(GLenum(GL_RENDERBUFFER), renderBuffer)
glClearColor(0, 1, 1, 1)
glClear(GLbitfield(GL_COLOR_BUFFER_BIT))
glViewport(0, 0, GLsizei(frame.width),GLsizei(frame.height))
glVertexAttribPointer(GLuint(self.positon),
3,
GLenum(GL_FLOAT),
GLboolean(GL_FALSE),
GLsizei(MemoryLayout<GLfloat>.size * 7),
nil)
let offset: GLsizeiptr = MemoryLayout<GLfloat>.size * 3
glVertexAttribPointer(GLuint(self.color),
4,
GLenum(GL_FLOAT),
GLboolean(GL_FALSE),
GLsizei(MemoryLayout<GLfloat>.size * 7),
UnsafeRawPointer(bitPattern: offset))
glDrawArrays(GLenum(GL_TRIANGLES), 0, 3)
context?.presentRenderbuffer(Int(GL_RENDERBUFFER))
}
写在最后
写这篇博客的时候刚刚开始学 openGLES,在写这个博客的demo时候,各种查资料零零散散 费半日之力才成。在写的过程中出现了许多问题主要还是openGL es api方面的熟悉,api中各种参数代表的含义还是有多多的不了解。通过写这个demo我主要学习到一下几点
* 所有的buffer 都需要有 申请–>绑定–>使用的一个过程
* shader的主要流程有, 创建–>加载–>编译–>检测编译状态–>装配到着色程序–>着色程序进行链接–>使用着色程序的过程
* 顶点buffer必须有glGenBuffers –> glBindBuffer –> glBufferData –> glEnableVertexAttribArray –> glVertexAttribPointer 流程。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Hit Refresh
Satya Nadella、Greg Shaw / HarperBusiness / 2017-9-26 / USD 20.37
Hit Refresh is about individual change, about the transformation happening inside of Microsoft and the technology that will soon impact all of our lives—the arrival of the most exciting and disruptive......一起来看看 《Hit Refresh》 这本书的介绍吧!