一银舱、OpenGL ES簡介
OpenGL(Open Graphics Library)是指定義了一個跨編程語言、跨平臺的編程接口規(guī)格的專業(yè)的圖形程序接口判耕。它用于三維圖像(二維的亦可)透绩,是一個功能強(qiáng)大,調(diào)用方便的底層圖形庫壁熄。
OpenGL ES (OpenGL for Embedded Systems) 是 OpenGL 三維圖形 API 的子集帚豪,針對手機(jī)、PDA和游戲主機(jī)等嵌入式設(shè)備而設(shè)計草丧。(OpenGL ES可以在iOS上實(shí)現(xiàn)2D和3D圖形編程狸臣。)
** OpenGL ES 1.0、2.0昌执、3.0的區(qū)別: **
OpenGL ES1.0:
針對固定管線硬件(fixed pipeline)烛亦,通過它內(nèi)建的functions來設(shè)置諸如燈光、vertexes(圖形的頂點(diǎn)數(shù))懂拾、顏色煤禽、camera等等的東西。
OpenGL ES2.0:
針對可編程管線硬件(programmable pipeline)岖赋,需要自己動手編寫任何功能檬果。與此同時,2.0相比于1.0更具靈活性唐断,功能也更強(qiáng)大选脊。可以自定義頂點(diǎn)和像素計算脸甘,可以讓表現(xiàn)方式更加準(zhǔn)確恳啥。
OpenGL ES3.0:
OpenGL ES3.0擴(kuò)展了OpenGL ES2.0,支持許多新的渲染技術(shù)斤程、優(yōu)化和顯示質(zhì)量改進(jìn)角寸,包括——引入了許多和紋理相關(guān)的新功能菩混,對著色語言進(jìn)行了重大更新和支持著色器新功能的API特性忿墅,引入了多種與幾何形狀規(guī)范和圖元渲染控制相關(guān)的新功能扁藕,引入了新的緩沖區(qū)對象,增添了許多與屏幕外渲染到幀緩沖區(qū)對象相關(guān)的新功能疚脐。具體功能在后邊的文章詳細(xì)說明亿柑。(可能:))
** OpenGL ES 3.0的向后兼容新 **
OpenGL ES 3.0向后兼容OpenGL ES 2.0,但由于3.0/2.0不支持1.x支持的固定功能管線棍弄,3.0/2.0不能向后兼容1.x望薄。
** EGL/EAGL **
EGL是Khronos渲染API(如OpenGL ES)和原生窗口系統(tǒng)之間的接口(在iOS上則是EAGL),任何OpenGL ES應(yīng)用程序都必須在開始渲染之前使用EGL執(zhí)行如下任務(wù):
- 查詢并初始化設(shè)備商可用的顯示器
- 創(chuàng)建渲染表面
- 創(chuàng)建渲染上下文
** OpenGL ES 3.0圖形管線的各個階段 **
二呼畸、Hello痕支,Triangle:第一個OpenGL ES 3.0程序
** 代碼地址如下 Hello_Triangle **
在此解釋一下** Hello_Triangle.c **的代碼
#include "esUtil.h"
typedef struct
{
// Handle to a program object
GLuint programObject;
} UserData;
///
// Create a shader object, load the shader source, and
// compile the shader.
//
GLuint LoadShader ( GLenum type, const char *shaderSrc )
{
GLuint shader;
GLint compiled;
// Create the shader object
shader = glCreateShader ( type );
if ( shader == 0 )
{
return 0;
}
// Load the shader source
glShaderSource ( shader, 1, &shaderSrc, NULL );
// Compile the shader
glCompileShader ( shader );
// Check the compile status
glGetShaderiv ( shader, GL_COMPILE_STATUS, &compiled );
if ( !compiled )
{
GLint infoLen = 0;
glGetShaderiv ( shader, GL_INFO_LOG_LENGTH, &infoLen );
if ( infoLen > 1 )
{
char *infoLog = malloc ( sizeof ( char ) * infoLen );
glGetShaderInfoLog ( shader, infoLen, NULL, infoLog );
esLogMessage ( "Error compiling shader:\n%s\n", infoLog );
free ( infoLog );
}
glDeleteShader ( shader );
return 0;
}
return shader;
}
///
// Initialize the shader and program object
//
int Init ( ESContext *esContext )
{
UserData *userData = esContext->userData;
char vShaderStr[] =
"#version 300 es \n"
"layout(location = 0) in vec4 vPosition; \n"
"void main() \n"
"{ \n"
" gl_Position = vPosition; \n"
"} \n";
char fShaderStr[] =
"#version 300 es \n"
"precision mediump float; \n"
"out vec4 fragColor; \n"
"void main() \n"
"{ \n"
" fragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 ); \n"
"} \n";
GLuint vertexShader;
GLuint fragmentShader;
GLuint programObject;
GLint linked;
// Load the vertex/fragment shaders
vertexShader = LoadShader ( GL_VERTEX_SHADER, vShaderStr );
fragmentShader = LoadShader ( GL_FRAGMENT_SHADER, fShaderStr );
// Create the program object
programObject = glCreateProgram ( );
if ( programObject == 0 )
{
return 0;
}
glAttachShader ( programObject, vertexShader );
glAttachShader ( programObject, fragmentShader );
// Link the program
glLinkProgram ( programObject );
// Check the link status
glGetProgramiv ( programObject, GL_LINK_STATUS, &linked );
if ( !linked )
{
GLint infoLen = 0;
glGetProgramiv ( programObject, GL_INFO_LOG_LENGTH, &infoLen );
if ( infoLen > 1 )
{
char *infoLog = malloc ( sizeof ( char ) * infoLen );
glGetProgramInfoLog ( programObject, infoLen, NULL, infoLog );
esLogMessage ( "Error linking program:\n%s\n", infoLog );
free ( infoLog );
}
glDeleteProgram ( programObject );
return FALSE;
}
// Store the program object
userData->programObject = programObject;
glClearColor ( 1.0f, 1.0f, 1.0f, 0.0f );
return TRUE;
}
///
// Draw a triangle using the shader pair created in Init()
//
void Draw ( ESContext *esContext )
{
UserData *userData = esContext->userData;
GLfloat vVertices[] = { 0.0f, 0.5f, 0.3f,
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, -0.4f
};
// Set the viewport
glViewport ( 0, 0, esContext->width, esContext->height );
// Clear the color buffer
glClear ( GL_COLOR_BUFFER_BIT );
// Use the program object
glUseProgram ( userData->programObject );
// Load the vertex data
glVertexAttribPointer ( 0, 3, GL_FLOAT, GL_FALSE, 0, vVertices );
glEnableVertexAttribArray ( 0 );
glDrawArrays ( GL_TRIANGLES, 0, 3 );
}
void Shutdown ( ESContext *esContext )
{
UserData *userData = esContext->userData;
glDeleteProgram ( userData->programObject );
}
int esMain ( ESContext *esContext )
{
esContext->userData = malloc ( sizeof ( UserData ) );
esCreateWindow ( esContext, "Hello Triangle", 375, 667, ES_WINDOW_RGB );
if ( !Init ( esContext ) )
{
return GL_FALSE;
}
esRegisterShutdownFunc ( esContext, Shutdown );
esRegisterDrawFunc ( esContext, Draw );
return GL_TRUE;
}
** 1.使用OpenGL ES 3.0 框架 **
** int esMain ( ESContext esContext ) **
esMain函數(shù)為應(yīng)用程序的主入口,參數(shù)是ESContext蛮原。ESContext有一個名為userData卧须、類型為void的成員變量,應(yīng)用程序所需的所有數(shù)據(jù)保存在userData中儒陨。ESContext結(jié)構(gòu)中的其他元素在頭文件中描述花嘶,供用戶的用用程序讀取。ESContext結(jié)構(gòu)中的其他數(shù)據(jù)包括窗口寬度和高度蹦漠、EGL上下文和回調(diào)函數(shù)指針等信息椭员。
在本程序的esMain中分配了userData、創(chuàng)建了窗口并初始化繪圖回調(diào)函數(shù)笛园。
** 2.創(chuàng)建頂點(diǎn)著色器和片斷著色器 **
** int Init ( ESContext *esContext ) **
要進(jìn)行任何渲染隘击,OpenGL ES 3.0程序必須至少有一個頂點(diǎn)著色器和一個片斷著色器。本程序中的Init函數(shù)最主要的任務(wù)是建在一個頂點(diǎn)著色器和一個片斷著色器研铆。
以頂點(diǎn)著色器為例說明:
char vShaderStr[] =
"#version 300 es \n"
"layout(location = 0) in vec4 vPosition; \n"
"void main() \n"
"{ \n"
" gl_Position = vPosition; \n"
"}
第一行聲明著色器版本闸度;
第二行聲明一個名為vPosition的4分量向量輸入屬性,layout(location=0)限定符表示這個變量的位置是頂點(diǎn)屬性0蚜印;
第三行開始聲明一個main函數(shù)莺禁,表示著色器執(zhí)行的開始,{}中是著色器主體窄赋,將vPosition輸入屬性拷貝到名為gl_Position的特殊輸出變量哟冬。每個頂點(diǎn)著色器必須在gl_Position變量中輸出一個位置,這個變量定義傳遞到管線下一個階段的位置忆绰。
** 3.編譯和加載著色器 **
** GLuint LoadShader ( GLenum type, const char *shaderSrc ) **
定義完著色器源代碼后浩峡,可以將著色器加載到OpenGL ES。LoadShader函數(shù)負(fù)責(zé)加載著色器源代碼错敢、編譯并檢查錯誤翰灾,它返回一個著色器對象缕粹,這是一個OpenGL ES 3.0對象,以后可用于連接到程序?qū)ο螅ê筮厱v到)纸淮。
以本程序的LoadShader為例:
GLuint shader;
GLint compiled;
// Create the shader object
shader = glCreateShader ( type );
if ( shader == 0 )
{
return 0;
}
glCreateShader創(chuàng)建指定類型的新著色器對象平斩。
// Load the shader source
glShaderSource ( shader, 1, &shaderSrc, NULL );
// Compile the shader
glCompileShader ( shader );
著色器源代碼本身用glShaderSource加載到著色器對象,著色器用glCompileShader函數(shù)編譯咽块。
// Check the compile status
glGetShaderiv ( shader, GL_COMPILE_STATUS, &compiled );
if ( !compiled )
{
GLint infoLen = 0;
glGetShaderiv ( shader, GL_INFO_LOG_LENGTH, &infoLen );
if ( infoLen > 1 )
{
char *infoLog = malloc ( sizeof ( char ) * infoLen );
glGetShaderInfoLog ( shader, infoLen, NULL, infoLog );
esLogMessage ( "Error compiling shader:\n%s\n", infoLog );
free ( infoLog );
}
glDeleteShader ( shader );
return 0;
}
return shader;
編譯著色器之后绘面,確定編譯的狀態(tài),打印輸出生成的錯誤侈沪。
如果著色器編譯成功揭璃,則返回一個新的著色器對象。
** 4.創(chuàng)建一個程序?qū)ο蟛⑦B接著色器 **
頂點(diǎn)和片段著色器需要連接到一個程序?qū)ο蟛拍芾L制圖形亭罪,可以說程序?qū)ο笫亲罱K連接的程序瘦馍。
// Create the program object
programObject = glCreateProgram ( );
if ( programObject == 0 )
{
return 0;
}
glAttachShader ( programObject, vertexShader );
glAttachShader ( programObject, fragmentShader );
用glCreateProgram創(chuàng)建程序?qū)ο螅⒂胓lAttachShader將頂點(diǎn)著色器和片斷著色器連接到對象上应役。
// Link the program
glLinkProgram ( programObject );
// Check the link status
glGetProgramiv ( programObject, GL_LINK_STATUS, &linked );
if ( !linked )
{
GLint infoLen = 0;
glGetProgramiv ( programObject, GL_INFO_LOG_LENGTH, &infoLen );
if ( infoLen > 1 )
{
char *infoLog = malloc ( sizeof ( char ) * infoLen );
glGetProgramInfoLog ( programObject, infoLen, NULL, infoLog );
esLogMessage ( "Error linking program:\n%s\n", infoLog );
free ( infoLog );
}
glDeleteProgram ( programObject );
return FALSE;
}
// Store the program object
userData->programObject = programObject;
連接程序情组,檢查錯誤。對象連接成功后扛吞,可以使用程序?qū)ο筮M(jìn)行渲染呻惕。
// Use the program object
glUseProgram ( userData->programObject );
調(diào)用glUseProgram來使用程序?qū)ο蟆?/p>
** 5.設(shè)置視口和清除顏色緩沖區(qū) **
** void Draw ( ESContext *esContext ) **
Draw回調(diào)函數(shù)用于繪制幀。
// Set the viewport
glViewport ( 0, 0, esContext->width, esContext->height );
設(shè)置視口的原點(diǎn)和寬高
// Clear the color buffer
glClear ( GL_COLOR_BUFFER_BIT );
繪圖中涉及多種緩沖區(qū)類型:顏色滥比、深度和模板亚脆。在本程序中只向顏色緩沖區(qū)中繪制圖形。在每一幀開始的時候盲泛,我們用glClear清除緩沖區(qū)濒持,清除顏色為glClearColor指定的顏色。在本程序中寺滚,清除顏色被設(shè)為(1.0柑营, 1.0, 1.0村视, 1.0)官套。
** 6.加載幾何形狀和繪制圖元 **
清除顏色緩沖區(qū)、設(shè)置視口和加載程序?qū)ο笾笠峡祝枰贫ㄈ切蔚膸缀涡螤睢?/p>
GLfloat vVertices[] = { 0.0f, 0.5f, 0.3f,
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, -0.4f
};
...
// Load the vertex data
glVertexAttribPointer ( 0, 3, GL_FLOAT, GL_FALSE, 0, vVertices );
glEnableVertexAttribArray ( 0 );
glDrawArrays ( GL_TRIANGLES, 0, 3 );
三角形的頂點(diǎn)由vVertices數(shù)組中的三個坐標(biāo)(x奶赔, y, z)指定杠氢。頂點(diǎn)位置使用glVertexAttribPointer加載到GL站刑,并連接到頂點(diǎn)著色器生命的vPosition屬性。
最后鼻百,使用glDrawArrays告訴OpenGL ES繪制圖元绞旅。
** 7.顯示后臺緩沖區(qū) **
屏幕上可見的幀緩沖區(qū)有一個像素數(shù)據(jù)的二維數(shù)組表示摆尝。但是如果我們直接繪制到緩沖區(qū),那么用戶在部分更新幀緩沖區(qū)的時候會看到偽像因悲。為了解決這個問題堕汞,我們使用雙緩沖區(qū)(前臺緩沖區(qū)和后臺緩沖區(qū))。所有渲染都發(fā)生在后臺緩沖區(qū)(不可見緩沖區(qū))囤捻,當(dāng)渲染完成之后臼朗,這個緩沖區(qū)被交換到前臺緩沖區(qū)(可見緩沖區(qū))邻寿。然后前臺緩沖區(qū)變成下一幀的后臺緩沖區(qū)蝎土。這種活動通過eglSwapBuffers控制(調(diào)用Draw毀掉函數(shù)之后調(diào)用該函數(shù))。