上個教程瞭解了蘋果官方爲減輕OpenGL ES開發提供的一個簡單庫GLKit。咱們也知道GLKit能提供的紋理通道只有兩個,光照效果三個,在須要更多的紋理以及自定義效果只能經過GLSL(Open GL Shader Language)來實現。git
也許不少學習的人會糾結OpenGL ES用啥版本,當真正使用的時候就不會有這種疑問。會發現各個版本有個版本的針對狀況,實際使用能夠根據需求來進行一個選擇。github
OpenGL ES 版本 | 描述 |
---|---|
1.x | 針對固定管線硬件 |
2.x | 針對可編程管線硬件 |
3.0 | 對2.0版本的擴充 |
3.1 | 在3.0的基礎上增長了幾何着色器 |
GLSL就是爲了開發者編寫頂點着色器和片元着色器的語言。在以前的OpenGL中咱們知道可使用固定管線進行編程,也就是使用默認的頂點着色器和片元着色器,因此實現自定義的效果必須使用可編程管線。GLSL是一門C風格的語言,也有着控制結構(if、for、switch等),其實咱們瞭解了GLSL的規範後開發仍是很容易的,可是由於編譯器不會提示,只會在一些關鍵字寫對後會進行一個色彩顯示,也就意味着咱們在寫着色器的時候必須很仔細,否則會出現編譯着色器失敗。objective-c
類型 | 描述 |
---|---|
vec2 vec3 vec4 | 二維、三維、四維浮點向量 |
ivec2 ivec3 ivec4 | 二維、三維、四維整形向量 |
uvec2 uvec3 uvec4 | 二維、三維、四維無符號整形向量 |
bvec2 bvec3 bvec4 | 二維、三維、四維布爾型向量 |
類型 | 描述 |
---|---|
mat2 mat2x2 | 兩行兩列 |
mat3 mat3x3 | 三行三列 |
mat4 mat4x4 | 四行四列 |
mat2x3 | 三行兩列 |
mat2x4 | 四行兩列 |
mat3x2 | 兩行三列 |
mat3x4 | 四行三列 |
mat4x2 | 兩行四列 |
mat4x3 | 三行四列 |
限定符 | 描述 |
---|---|
<none> | 普通的本地變量,外部不可見並訪問 |
const | 一個編譯常量,只讀屬性 |
in/varying | 從之前階段傳遞過來的變量 |
in/vartying centroid | 一個從之前處理階段傳遞過來的變量,使用質心插值 |
out/attribute | 傳遞下一處理階段或者在一個函數中指定一個返回值 |
out/attribute centroid | 傳遞到下一個處理階段,質心插值 |
uniform | 一個從客戶端傳遞過來的變量,在頂點之間不作改變 |
#version 300 es // 一般會寫這個版本號,假如OpenGL ES 3.3.0 則爲330
attribute vec4 position; // 頂點數據都是用attribute修飾,用attribute通道進行傳值
attribute vec2 textureCootdinate; // 紋理頂點數據
varying lowp vec2 textureCoord; // 須要傳遞給片元着色器的紋理頂點,且須要精度修飾符
void main(){
textureCoord = textureCootdinate;
gl_Position = position;
}
複製代碼
咱們須要注意的點有如下這些:編程
①#version 是爲了告訴OpenGL ES版本號,必須處於空行外的第一行。書寫GLSL時每行結束須要添加分號和換行。數組
②頂點數據都須要使用attribute修飾,用於attribute通道進行傳值。並且頂點數據須要用vec4修飾,由於頂點是RGBA四個數據,不然編譯着色器將會報錯。bash
③傳遞給片元着色器的紋理頂點須要使用varying修飾,只有使用了varying修飾的屬性才能傳遞到片元着色器;其次還需使用精度修飾符,精度修飾符有lowp、mediump、highp低中高三種精度。函數
④着色器源碼中只能有一個main函數。性能
⑤在main函數中把須要傳遞給片元着色器的值進行賦值。例如紋理屬性值學習
⑥必須對內建變量gl_Position進行賦值spa
#version 300 es
varying lowp vec2 textureCoord;
uniform sampler2D textureSampler;
void main(){
gl_FragColor = texture2D(textureSampler, textureCoord);
}
複製代碼
代碼和頂點着色器差很少,由於片斷着色器是處理紋理數據的,因此一些貼圖、濾鏡大部分都在這裏進行。片元着色器的內建變量是gl_FragColor。
咱們知道在OpenGL ES開發中會經過glGetError函數來獲取錯誤代碼,來排查錯誤緣由,其中在開發中若是粗心或者一不留神寫錯了值就容易形成1280和1286錯誤代碼。
錯誤 | 代碼 | 緣由 |
---|---|---|
GL_NO_ERROR | 0(0x0) | 無 |
GL_INVALID_ENUM | 1280(0x0500) | 使用了不合法的枚舉 |
GL_INVALID_VALUE | 1281(0x0501) | 數值參數超出範圍 |
GL_INVALID_OPERATION | 1282(0x0502) | 操做在當前狀態非法 |
GL_OUT_OF_MEMORY | 1285(0x0505) | 內存不足以執行命令 |
GL_INVALID_FRAMEBUFFER_OPERATION | 1286(0x0506) | 幀緩衝區不完整 |
**在應用調用任何OpenGL ES命令以前,須要首先建立一個渲染上下文和繪圖表面,並使之成爲現行的上下文和表面。**渲染上下文和繪圖表面一般由原生窗口系統經過EGL等API提供,咱們知道在iOS中是EAGL。由原生系統提供的繪圖表面能夠是屏幕上顯示的表面(稱爲系統提供的幀緩衝區);也能夠是屏幕外的表面(稱爲pbuffer)。建立CAEAGLLayer繪圖表面的咱們能夠指定寬度、高度以及是否使用顏色、深度和模板緩衝區以及這些緩衝區的位深。
在默認狀況,在iOS中OpenGL ES使用CAEAGLayer做爲繪圖表面。若是應用程序只在屏幕上的表面繪圖,則窗口系統提供的幀緩衝區童話村那個很高效。可是,由於許多應用程序須要渲染到紋理,爲此,使用系統提供的幀緩衝區做爲繪圖表面一般不是理想的選擇。渲染到紋理的實例由陰影貼圖、動態反射和環境貼圖、多道景深技術、運動模糊效果和處理後特效。
應用程序能夠經過如下兩種方式渲染到紋理:
①經過繪製窗口系統提供的緩衝區,而後將緩衝區的對應區域複製到紋理來實現渲染到紋理。這能夠用glCopyTexImage2D和glCopyTexSubImage2D API實現。這些API執行從幀緩衝區到紋理緩衝區的複製,之一複製操做每每對性能有不利影響。此方法只適用於紋理的尺寸小於或者等於幀緩衝區尺寸的時候纔有效。
②經過使用鏈接到紋理的pbuffer來實現渲染到紋理。咱們知道,窗口系統提供的表面必須鏈接到一個渲染上下文。這在某些對每一個pbuffer和窗口表面須要不一樣的上下文實現中可能效率低下。可繪製表面切換有時候須要OpenGL ES清除全部切換以前渲染的圖像。
可是以上兩種方式對於渲染到紋理或者其餘屏幕外表面來講都不理想。做爲替代,咱們須要容許程序直接渲染到紋理的API,或者在OpenGL ES API中具有建立屏幕外表面的能力,並將它做爲渲染目標。幀緩衝區對象和渲染緩衝區對象容許應用程序完成這些操做,不須要額外建立渲染上下文。由於咱們在使用窗口系統提供了渲染到紋理或者屏幕外表面的更好、更有效的方法。
①僅使用OpenGL ES命令建立幀緩衝區對象。
②在單一EAGLContext中建立和使用多個幀緩衝區對象。也就是說不須要每一個幀緩衝區都有一個渲染上下文。
③建立屏幕外顏色、深度、或者模板渲染緩衝區和紋理,並將它們鏈接到幀緩衝區對象。
④在每一個幀緩衝區之間共享顏色、深度或者模板緩衝區。
⑤將紋理直接鏈接到幀緩衝區做爲顏色或深度,從而避免了進行復制操做的必要操做。
⑥在幀緩衝區之間複製並使用幀緩衝區內容失效
幀緩衝區對象(FBO)是一組顏色、深度、模板紋理或者渲染目標對象。各類2D圖像能夠鏈接到幀緩衝區對象中的顏色附着點。這些附着點包括一個渲染緩衝區對象,他保存顏色值、2D紋理或者立方圖面的mip級別、2D數組紋理的層次甚至3D紋理中一個2D切片的mip級別;一樣的包含深度值得各類2D圖像能夠鏈接到FBO的深度附着點;能夠鏈接到FBO模板附着點的惟一2D圖像是保存模板值得渲染緩衝區對象。並且一個幀緩衝區只有一個顏色、深度、模板附着點。
渲染緩衝區對象(RBO)是一個有應用程序分配的2D圖像緩衝區。渲染緩衝區能夠用於分配和存儲顏色、深度或者模板值,能夠用做幀緩衝區對象中的顏色、深度或者模板附着。渲染緩衝區相似於屏幕外的窗口系統提供的可繪製表面,例如pbuffer。可是渲染緩衝區不能直接做爲GL的紋理。
根據上面的流程圖咱們知道和GLKit不一樣的是使用自定義GLSL代替了GLKBaseEffect部份內容。值得注意的是咱們程序正常的加載紋理是倒像,咱們能夠經過處理頂點數據或者處理紋理映射方式來處理,可是都不是很好的處理辦法。較好的作法是在加載紋理的時候重繪位圖時進行一個轉換操做:
CGRect rect = CGRectMake(0, 0, imageWidth, imageHeight);
CGContextTranslateCTM(context, 0, imageHeight);
CGContextScaleCTM(context, 1.0f, -1.0f);
複製代碼
實現的效果圖: