OpenGL ES (OpenGL for Embedded Systems) 是以手持和嵌入式爲目標的高級3D圖形應 用程序編程接口(API). OpenGL ES 是⽬前智能手機中佔據統治地位的圖形API.⽀持的平 臺: iOS, Andriod , BlackBerry ,bada ,Linux ,Windows。iOS 容許OpenGL ES經過底層圖形處理的強大功能,能夠繪製複雜的2D、3D圖形,進行復雜着色的計算。html
其中安卓主要仍是採用OpenGL ES,而iOS在iOS 12開始被廢棄,可是仍然可用,官方主推Metal,可是有不少應用仍是基於OpenGL ES寫的,還沒那麼快直接遷移至Metal,就像Swift和OC同樣。並且Metal的也是採起了OpenGL ES的思想和作法本身開發的一個圖形接口集合,由於高度封裝因此使用很方便,可是想要熟悉瞭解還得深刻底層,那麼瞭解和學習OpenGL ES是頗有必要的,畢竟這也是iOS12以前的作法。git
着色器程序是執行頂點操做的頂點着色器程序源代碼/可執行文件。github
可接收數據有:編程
Attribute:接收頂點數組數據swift
Uniform:接收頂點/片元着色器使用的不變化的數據設計模式
採樣器:接收紋理的特殊統一變量api
頂點着色器的主要任務是:①計算矩形變換位置 ②根據光照公式計算顏色 ③ 生成/變換紋理。頂點着色器能夠執行自定義計算進行變換,以及實現光照效果等傳統不能實現的功能。數組
根據圖元類型和頂點數據計算生成一個個的圖元,裁剪、透視分割和視口變換操做都是在這個階段進行,以後進入光柵化階段。緩存
在這個階段將會把圖元裝配後的圖元(點、線、三角形等)轉化成一組二維片斷的過程。二維片斷有屏幕座標、顏色屬性、紋理座標等數據,也就是屏幕上可繪製的像素,像素含有以上屬性、數據,轉化的片斷將有片元着色器進行處理。bash
可接受的數據:
圖元變量:接收光柵化後的片元
Uniform:接收頂點/片元着色器使用的不變化的數據
採樣器:接收紋理的特殊統一變量
片元着色器的主要任務:①計算顏色 ②獲取紋理值 ③往像素填充顏色(顏色值/紋理值)。能夠將圖片/視頻中的每幀的每像素中顏色進行修改,常見的有添加濾鏡、美化圖片等操做。
OpenGL ES規範沒有定義窗口層,託管操做系統必須提供函數來建立一個OpenGL ES 渲染上下文和一個幀緩衝區,寫入任何繪圖命令的結果 。 OpenGL ES 命令須要渲染上下文和繪製表面才能完成圖形圖像的繪製。可是OpenGL ES API並無提供如何渲染上下文或者上下文如何鏈接到原生窗口系統,EGL是Khronos渲染API(OpenGL ES)和原生窗口的之間的接口,iOS是惟一支持OpenGL ES卻不支持EGL的平臺,由於Apple提供本身的EGL API實現—— EAGL。
EAGL的主要功能:
1.和本地窗口系統通信
2.查詢可用的配置
3.建立OpenGL ES可用的繪製表面(drawing surface)。用於繪製圖元的表面,指定渲染所需的緩存區類型,例如顏色緩存區、深度緩存區和模板緩衝區。
4.同步不一樣類別之間的API,例如OpenGL ES 和OpenVG,或者本地OpenGL ES在和本地繪圖命令之間。
5.管理渲染資源,例如紋理映射(rendering map)。
EGLDisplay:由於每一個窗口系統都有不一樣的定義,因此EGL提供基本不透明的類型:EGLDisplay,這我的類型封裝了全部的系統相關性,用於和原生窗口系統接口。
GLKit 框架的設計目標是爲了簡化基於OpenGL / OpenGL ES 的應⽤用開發。它的出現加快OpenGL ES或OpenGL應用程序開發。 使⽤數學庫,背景紋理加載,預先建立的着色器器效果,以及標準視圖和視圖控制器來實現渲染循環。
GLKit框架提供了功能和類,能夠減小建立新的基於着⾊器的應用程序所需的工做量,或者⽀持依賴早期版本的OpenGL ES或OpenGL提供的固定函數頂點或⽚片斷處理理的現有 應⽤用程序
GLKView提供繪製場所,GLKViewController擴展於標準的UIKit設計模式,用於繪製視圖內容的管理與呈現。 可參考官方文檔說明。
GLKit主要的功能是:①加載紋理 ②提供高性能的數學運算 ③提供常見的着色器 ④提供視圖以及視圖控制器。
EAGLContext對象管理OpenGL ES渲染上下文 - 使用OpenGL ES繪製所需的狀態信息,命令和資源。要執行OpenGL ES命令,就須要一個當前的渲染上下文。
一個繼承自UIView並且默認使用OpenGL ES渲染的視圖。GLKView類經過直接表明管理幀緩衝對象,簡化了建立OpenGL ES應用程序所需的工做量;當須要更新內容時,您的應用程序只須要繪製到幀緩衝區中。
EAGLSharegroup對象是管理一個或多個EAGLContext對象關聯的OpenGL ES資源,它是在初始化EAGLContext對象時建立的,並在釋放引用它的最後一個EAGLContext對象時進行處理。改對象。該對象沒有提供任何接口給開發者。
GLKTextureLoader類能夠加載Image I/O框架支持的大多數圖像格式的二維或立方體貼圖紋理。在iOS中,它還能夠加載以PVRTC格式壓縮的紋理。它能夠同步或異步加載數據。
當您的應用使用GLKTextureLoader類加載紋理時,紋理加載器會使用GLKTextureInfo對象返回有關紋理的信息。您的應用永遠不會直接建立GLKTextureInfo對象。
GLKBaseEffect類提供的着色器模仿OpenGL ES 1.1照明和着色模型提供的許多行爲,包括材質,光照和紋理。基本效果容許將最多三個燈光和兩個紋理應用於場景。
咱們知道使用UIImageView加載圖片很簡單,可是加載圖片的底層用到了OpenGL ES,而GLKView也是封裝在OpenGL ES之上的,能夠看看如何使用GLKView加載一張圖片。
// 經過指定OpenGL ES版本初始化
public convenience init?(api: EAGLRenderingAPI)
// 經過指定OpenGL ES版本、OpenGL ES管理對象進行初始化
public init?(api: EAGLRenderingAPI, sharegroup: EAGLSharegroup)
// 設置當前的上下文
open class func setCurrent(_ context: EAGLContext?) -> Bool // 得到當前的上下文 open class func current() -> EAGLContext? // 獲取當前上下文的OpenGL ES版本 open var api: EAGLRenderingAPI { get }
// 獲取OpenGL ES管理對象
open var sharegroup: EAGLSharegroup { get }
// 標籤說明上下文的用途
open var debugLabel: String?
// 是否開啓多線程
open var isMultiThreaded: Bool
複製代碼
//經過frame和上下文來進行初始化
public init(frame: CGRect, context: EAGLContext)
// 代理
@IBOutlet unowned(unsafe) open var delegate: GLKViewDelegate?
// 上下文
open var context: EAGLContext
// 獲取幀緩衝的寬、高
open var drawableWidth: Int { get }
open var drawableHeight: Int { get }
//渲染顏色緩衝區格式
open var drawableColorFormat: GLKViewDrawableColorFormat
//渲染深度緩衝區格式
open var drawableDepthFormat: GLKViewDrawableDepthFormat
//渲染模板緩衝區格式
open var drawableStencilFormat: GLKViewDrawableStencilFormat
//多重採樣格式
open var drawableMultisample: GLKViewDrawableMultisample
// 將幀緩衝區對象綁定到OpenGL ES
open func bindDrawable()
// 刪除幀緩衝區對象
open func deleteDrawable()
// 得到繪製的一張快照,不該該在繪製時獲取
open var snapshot: UIImage { get }
//控制視圖是否響應setNeedsDisplay。若是爲true,則視圖與UIView相似。當視圖已標記爲響應時,將在下一個繪製週期中調用draw方法。若是是不響應時,在下一個繪圖週期中永遠不會調用視圖的繪製方法。默認爲true,可是在GLKViewController默認爲false。
open var enableSetNeedsDisplay: Bool
//當enableSetNeedsDisplay值爲false時,則須要使用此方法進行更新繪製內容
open func display()
// 代理方法。全部的繪製都須要在這裏進行
protocol func glkView(_ view: GLKView, drawIn rect: CGRect) 複製代碼
// 經過管理對象來進行初始化
public init(sharegroup: EAGLSharegroup)
/*******如下紋理加載方法爲類方法都爲同步、實例方法都爲異步加載*******************/
// 同步從本地文件路徑加載紋理
open class func texture(withContentsOfFile path: String, options: [String : NSNumber]? = nil) throws -> GLKTextureInfo // 同步從指定URL加載紋理 open class func texture(withContentsOf url: URL, options: [String : NSNumber]? = nil) throws -> GLKTextureInfo // 同步從指定Assets下的圖片名稱來加載紋理 open class func texture(withName name: String, scaleFactor: CGFloat, bundle: Bundle?, options: [String : NSNumber]? = nil) throws -> GLKTextureInfo // 同步從數據中加載紋理 open class func texture(withContentsOf data: Data, options: [String : NSNumber]? = nil) throws -> GLKTextureInfo // 同步從位圖中加載紋理 open class func texture(with cgImage: CGImage, options: [String : NSNumber]? = nil) throws -> GLKTextureInfo // 同步從本地路徑加載六張圖片做爲立方體的紋理 右、左、上、下、前、後的順序加載 open class func cubeMap(withContentsOfFiles paths: [Any], options: [String : NSNumber]? = nil) throws -> GLKTextureInfo // 同步從本地路徑加載一張圖片寬高均乘以6後做爲立方體六個面的紋理 open class func cubeMap(withContentsOfFile path: String, options: [String : NSNumber]? = nil) throws -> GLKTextureInfo // 同步從指定URL加載一張圖片寬高均乘以6後做爲立方體六個面的紋理 open class func cubeMap(withContentsOf url: URL, options: [String : NSNumber]? = nil) throws -> GLKTextureInfo 複製代碼
// 紋理的名稱
open var name: GLuint { get }
// 紋理的對象
open var target: GLenum { get }
// 紋理的寬、高
open var width: GLuint { get }
open var height: GLuint { get }
// 紋理的深度
open var depth: GLuint { get }
// 紋理的透明度狀態
open var alphaState: GLKTextureInfoAlphaState { get }
// 紋理的原點
open var textureOrigin: GLKTextureInfoOrigin { get }
// 是否包含mip貼圖
open var containsMipmaps: Bool { get }
open var mimapLevelCount: GLuint { get }
open var arrayLength: GLuint { get }
複製代碼
// 三個光照。默認是關閉的,須要手動開啓
open var light0: GLKEffectPropertyLight { get }
open var light1: GLKEffectPropertyLight { get }
open var light2: GLKEffectPropertyLight { get }
// 光源類型從
open var lightingType: GLKLightingType // GLKLightingTypePerVertex
// 環境顏色
open var lightModelAmbientColor: GLKVector4 // { 0.2, 0.2, 0.2, 1.0 }
// 圖元材質屬性
open var material: GLKEffectPropertyMaterial { get } // Default material state
// 兩個紋理,默認是關閉,須要手動開啓
open var texture2d0: GLKEffectPropertyTexture { get } // Disabled
open var texture2d1: GLKEffectPropertyTexture { get }
// 紋理順序
open var textureOrder: [GLKEffectPropertyTexture]? // texture2d0, texture2d1
// 不提供頂點顏色時使用這個常量顏色
open var constantColor: GLKVector4 // { 1.0, 1.0, 1.0, 1.0 }
//霧化效果
open var fog: GLKEffectPropertyFog { get } // Disabled
// 標籤
open var label: String? // @"GLKBaseEffect"
// 是否使用計算燈光與材質後的顏色
open var colorMaterialEnabled: GLboolean // GL_FALSE
// 是不是兩面光照
open var lightModelTwoSided: GLboolean // GL_FALSE
// 是否使用常量顏色
open var useConstantColor: GLboolean // GL_TRUE
//準備渲染效果
open func prepareToDraw()
複製代碼
// 參數api是個枚舉值,有openGLES一、openGLES二、openGLES3
let context = EAGLContext.init(api: .openGLES3)
// 還須要設置EAGLContext的上下文
EAGLContext.setCurrent(context)
複製代碼
// 初始化GLKView
glKitView = GLKView.init(frame: CGRect.init(x: 0, y: 200, width:self.view.frame.width, height: self.view.frame.width))
// 設置view的代理
glKitView.delegate = self
// 設置view的上下文
glKitView.context = context
// 設置顏色緩衝區格式
glKitView.drawableColorFormat = .RGBA8888
// 設置深度緩衝區格式
glKitView.drawableDepthFormat = .format24
// 設置多重採用格式
glKitView.drawableMultisample = .multisample4X
// 設置模板緩衝區格式
glKitView.drawableStencilFormat = .format8
self.view.addSubview(glKitView)
複製代碼
var bufferID: GLuint = 0
glGenBuffers(1, &bufferID);
複製代碼
glBindBuffer(GLenum(GL_ARRAY_BUFFER), bufferID);
複製代碼
glBufferData(GLenum(GL_ARRAY_BUFFER), GLsizeiptr(MemoryLayout<VertexBuffer>.size * vertexData.count), vertexData, GLenum(GL_STATIC_DRAW));
複製代碼
參數一:目標
參數二:座標數據的大小
參數三:座標數據
參數四:用途
在iOS中,蘋果爲了提升性能全部的通道都是默認關閉的,如需使用須要手動開啓。
glEnableVertexAttribArray(GLuint(GLKVertexAttrib.position.rawValue))
複製代碼
在OC中獲取佔用字節數大小是使用函數sizeof來獲取,在swift中使用MemoryLayout<GLfloat>.size
。
let pointerPtr = UnsafeRawPointer.init(bitPattern: MemoryLayout<GLfloat>.size * 0)
glVertexAttribPointer(GLuint(GLKVertexAttrib.position.rawValue), 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), GLsizei(MemoryLayout<VertexBuffer>.size), pointerPtr)
複製代碼
glVertexAttribPointer方法參數說明
參數一:傳遞頂點座標的類型有五種類型:position[頂點]、normal[法線]、color[顏色]、texCoord0[紋理一]、texCoord1[紋理二],這裏用的是頂點類型。
參數二:每次從數據取多少個頂點或其餘類型數據
參數三:取值得類型是啥。GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT,GL_UNSIGNED_SHORT, GL_FIXED, 和 GL_FLOAT,初始值爲GL_FLOAT。
參數四:是否須要歸一化(NDC)
參數五:步長,取完一次數據須要跨越多少步長去讀取下一個數據,單位是字節數。
參數六:偏移量。每次取得數據須要偏移多少位置開始讀取數據,單位是字節數。
glEnableVertexAttribArray(GLuint(GLKVertexAttrib.texCoord0.rawValue))
let texturePtr = UnsafeRawPointer.init(bitPattern: MemoryLayout<GLfloat>.size * 3)
glVertexAttribPointer(GLuint(GLKVertexAttrib.texCoord0.rawValue), 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), GLsizei(MemoryLayout<VertexBuffer>.size), texturePtr)
複製代碼
咱們都知道iOS的座標計算是從左上角[0, 0]開始,到右下角[1, 1]。可是在紋理中的原點不是左上角而是左下角,右上角爲[1, 1],因此若是須要圖片被正確方向的加載那麼須要設置紋理的原點爲左下角,不然獲得的圖片是一張倒立的圖片。咱們知道GLKit只有兩個紋理通道,須要三種及以上的紋理只能經過GLSL來實現。
/// 加載紋理的可選項
let options = [GLKTextureLoaderOriginBottomLeft: NSNumber.init(value: 1)];
let textureInfo = try? GLKTextureLoader.texture(withContentsOfFile: texturePath, options: options)
複製代碼
咱們知道GLKView就是爲了開發者更好的完成OpenGL ES的開發,因此GLKView的的着色器工做是由GLKBaseEffect來完成的。
// 初始化
let glkEffect = ELKBaseEffect()
// 設置紋理通道1可用
glEffect.texture2d0.enabled = 1
// 設置紋理名稱,獲取紋理名稱可經過glGenTextures()
glEffect.texture2d0.name = textureInfo.name
// 設置紋理通道的目標
glEffect.texture2d0.target = GLKTextureTarget(rawValue: textureInfo.target)!
複製代碼
執行繪製是在GLKView的代理方法**glkView(_ view: GLKView, drawIn rect: CGRect)**中
// 清除顏色緩衝區
glClear(GLbitfield(GL_COLOR_BUFFER_BIT))
// 準備開始繪製
glEffect.prepareToDraw()
/** 開始繪製 參數一:繪製的類型 參數二:從那個點開始繪製 參數三:總共有幾個點 */
glDrawArrays(GLenum(GL_TRIANGLES), 0, 6);
複製代碼
參考文章:sizeof與MemoryLayout