OpenGL ES2.0 c++ 基礎(chǔ)
什么是OpenGL ES?OpenGL ES (為OpenGL for Embedded System的縮寫) 為適用于嵌入式系統(tǒng)的一個免費二維和三維圖形庫矾兜。為桌面版本OpenGL 的一個子集咱枉。OpenGL ES 定義了一個在移動平臺上能夠支持
hello world 開始吧
首先環(huán)境搭建引入必要庫窍荧。Linux windows ios android 嵌入式設(shè)備 自己的目標(biāo)設(shè)備支持什么庫。
基本庫
- EGL
- openGL ES2.0
以下例子程序均基于Linux平臺。
1. 保存全局變量的數(shù)據(jù)結(jié)構(gòu)
ESContext
typedef struct _escontext
{
void* userData; // Put your user data here...
GLint width; // Window width
GLint height; // Window height
EGLNativeWindowType hWnd; // Window handle
EGLDisplay eglDisplay; // EGL display
EGLContext eglContext; // EGL context
EGLSurface eglSurface; // EGL surface
// Callbacks
void (ESCALLBACK *drawFunc) ( struct _escontext * );
void (ESCALLBACK *keyFunc) ( struct _escontext *, unsigned char, int, int );
void (ESCALLBACK *updateFunc) ( struct _escontext *, float deltaTime );
}ESContext;
UserData
typedef struct
{
// Handle to a program object
GLuint programObject;
// Atrribute Location
GLint positionLoc;
GLint textureLoc;
// Uniform location
GLint matrixModeLoc;
GLint matrixViewLoc;
GLint matrixPerspectiveLoc;
// Sampler location
GLint samplerLoc;
// texture
GLuint texture;
} UserData;
2. 初始化EGL渲染環(huán)境和相關(guān)元素(第一步)
- 獲取 EGL Display 對象:eglGetDisplay()
- 初始化與 EGLDisplay 之間的連接:eglInitialize()
- 獲取 EGLConfig 全部列表對象 :eglGetConfigs()
- 獲取 EGLConfig 選擇EGL對象:eglChooseConfig()
- 創(chuàng)建 EGLContext 實例:eglCreateContext()
- 創(chuàng)建 EGLSurface 實例:eglCreateWindowSurface()
- 設(shè)置當(dāng)前渲染API : eglBindAPI();
- EGL_OPENGL_API
- EGL_OPENGL_ES_API
- EGL_OPENVG_API
- 連接 EGLContext 和 EGLSurface:eglMakeCurrent()
int InitEGL(ESContext * esContext)
{
NativeWindowType eglWindow = NULL;
EGLDisplay display;
EGLContext context;
EGLSurface surface;
EGLConfig configs[2];
EGLBoolean eRetStatus;
EGLint majorVer, minorVer;
EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
EGLint numConfigs;
EGLint cfg_attribs[] = {EGL_BUFFER_SIZE, EGL_DONT_CARE,
EGL_DEPTH_SIZE, 16,
EGL_RED_SIZE, 5,
EGL_GREEN_SIZE, 6,
EGL_BLUE_SIZE, 5,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE};
//my function
eglWindow = my_getEglNativeWindowType();
// Get default display connection
display = eglGetDisplay((EGLNativeDisplayType)EGL_DEFAULT_DISPLAY);
if ( display == EGL_NO_DISPLAY )
{
return EGL_FALSE;
}
// Initialize EGL display connection
eRetStatus = eglInitialize(display, &majorVer, &minorVer);
if( eRetStatus != EGL_TRUE )
{
return EGL_FALSE;
}
//Get a list of all EGL frame buffer configurations for a display
eRetStatus = eglGetConfigs (display, configs, 2, &numConfigs);
if( eRetStatus != EGL_TRUE )
{
return EGL_FALSE;
}
// Get a list of EGL frame buffer configurations that match specified attributes
eRetStatus = eglChooseConfig (display, cfg_attribs, configs, 2, &numConfigs);
if( eRetStatus != EGL_TRUE || !numConfigs)
{
return EGL_FALSE;
}
// Create a new EGL window surface
surface = eglCreateWindowSurface(display, configs[0], eglWindow, NULL);
if (surface == EGL_NO_SURFACE)
{
return EGL_FALSE;
}
// Set the current rendering API (EGL_OPENGL_API, EGL_OPENGL_ES_API,EGL_OPENVG_API)
eRetStatus = eglBindAPI(EGL_OPENGL_ES_API);
if (eRetStatus != EGL_TRUE)
{
return EGL_FALSE;
}
// Create a new EGL rendering context
context = eglCreateContext (display, configs[0], EGL_NO_CONTEXT, context_attribs);
if (context == EGL_NO_CONTEXT)
{
return EGL_FALSE;
}
// Attach an EGL rendering context to EGL surfaces
eRetStatus = eglMakeCurrent (display, surface, surface, context);
if( eRetStatus != EGL_TRUE )
{
return EGL_FALSE;
}
//If interval is set to a value of 0, buffer swaps are not synchronized to a video frame, and the swap happens as soon as the render is complete.
eglSwapInterval(display,0);
// Return the context elements
esContext->eglWindow = eglWindow;
esContext->eglDisplay = display;
esContext->eglSurface = surface;
esContext->eglContext = context;
return EGL_TRUE;
}
獲取 my_getEglNativeWindowType();
- NativeDisplayType 平臺顯示數(shù)據(jù)類型,標(biāo)識你所開發(fā)設(shè)備的物理屏幕
- NativeWindowType 平臺窗口數(shù)據(jù)類型,標(biāo)識系統(tǒng)窗口
下面的代碼是一個 NativeWindowType 獲取的例子叉讥。這只是一個例子,不同平臺之間的實現(xiàn)千差萬別饥追。使用 native 類型的關(guān)鍵作用在于為開發(fā)者抽象化這些細(xì)節(jié)图仓。
NativeWindowType getEglNativeWindowType(){
NativeDisplayType EGL_display = fbGetDisplayByIndex(0);
NativeDisplayType window;
unsigned lond physical;
int width, height, stride, bitsPerPixel;
fbgetDisplayInfo(egl_display, &width, &height, &physical, &stride, &bitsPerPixel);
window = fbCreateWindow(egl_display, 0, 0, width, height);
assert(window);
return window;
}
3. 生成Program (第二步)
3.1 LoadShader
實現(xiàn)shader的編譯
LoadShader主要實現(xiàn)以下功能:
- 創(chuàng)建Shader對象
- 裝載Shader源碼
- 編譯Shader
/* type specifies the Shader type: GL_VERTEX_SHADER or GL_FRAGMENT_SHADER */
GLuint LoadShader ( GLenum type, const char *shaderSrc )
{
GLuint shader;
GLint compiled;
// Create an empty shader object, which maintain the source code strings that define a shader
shader = glCreateShader ( type );
if ( shader == 0 )
return 0;
// Replaces the source code in a shader object
glShaderSource ( shader, 1, &shaderSrc, NULL );
// Compile the shader object
glCompileShader ( shader );
// Check the shader object 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;
}
3.1.1. 創(chuàng)建Shader對象 :glCreateShader()
它創(chuàng)建一個空的shader對象,它用于維護用來定義shader的源碼字符串但绕。支持以下兩種
- GL_VERTEX_SHADER:它運行在可編程的“頂點處理器”上救崔,用于代替固定功能的頂點處理
- GL_FRAGMENT_SHADER:它運行在可編程的“片斷處理器”上,用于代替固定功能的片段處理
3.1.2. 裝載Shader源碼:glShaderSource()
shader對象中原來的源碼全部被新的源碼所代替捏顺。
3.1.3. 編譯Shader:glCompileShader()
編譯存儲在shader對象中的源碼字符串六孵,編譯結(jié)果被當(dāng)作shader對象狀態(tài)的一部分被保存起來,可通過glGetShaderiv函數(shù)獲取編譯狀態(tài)幅骄。
3.1.4. 獲取shader對象參數(shù):glGetShaderiv()
獲取shader對象參數(shù)劫窒,參數(shù)包括:GL_SHADER_TYPE, GL_DELETE_STATUS, GL_COMPILE_STATUS, GL_INFO_LOG_LENGTH, GL_SHADER_SOURCE_LENGTH.
3.2 LoadProgram
實現(xiàn)了Program的鏈接
GLuint LoadProgram ( const char *vShaderStr, const char *fShaderStr )
{
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;
// Attaches a shader object to a program object
glAttachShader ( programObject, vertexShader );
glAttachShader ( programObject, fragmentShader );
// Bind vPosition to attribute 0
glBindAttribLocation ( programObject, 0, "vPosition" );
// Link the program object
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 GL_FALSE;
}
// Free no longer needed shader resources
glDeleteShader ( vertexShader );
glDeleteShader ( fragmentShader );
return programObject;
}
3.2.1 創(chuàng)建Program對象 :glCreateProgram()
建立一個空的program對象,shader對象可以被連接到program對像
3.2.2 glAttachShader
program對象提供了把需要做的事連接在一起的機制拆座。在一個program中主巍,在shader對象被連接在一起之前,必須先把shader連接到program上挪凑。
3.2.3 glBindAttribLocation
把program的頂點屬性索引與頂點shader中的變量名進行綁定孕索。
3.2.4 glLinkProgram
連接程序?qū)ο蟆H绻魏晤愋蜑镚L_VERTEX_SHADER的shader對象連接到program,它將產(chǎn)生在“可編程頂點處理器
”上可執(zhí)行的程 序躏碳;如果任何類型為GL_FRAGMENT_SHADER的shader對象連接到program,它將
產(chǎn)生在“可編程片斷處理器
”上可執(zhí)行的程序搞旭。
3.2.5 glGetProgramiv
獲取program對象的參數(shù)值,參數(shù)有:GL_DELETE_STATUS, GL_LINK_STATUS, GL_VALIDATE_STATUS, GL_INFO_LOG_LENGTH, GL_ATTACHED_SHADERS, GL_ACTIVE_ATTRIBUTES, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH,
GL_ACTIVE_UNIFORMS, GL_ACTIVE_UNIFORM_MAX_LENGTH.
3.3 CreateProgram
實現(xiàn)了Program的源碼
加載
在3.1中只實現(xiàn)了Shader的編譯菇绵,在3.2中只實現(xiàn)了Program的鏈接肄渗,現(xiàn)在還缺少真正供進行編譯和鏈接
的源碼
int CreateProgram(ESContext * esContext)
{
GLuint programObject;
GLbyte vShaderStr[] =
"attribute vec4 vPosition;"
"void main()"
"{"
" gl_Position = vPosition;"
"}";
GLbyte fShaderStr[] =
"precision mediump float;"
"void main()"
"{"
" gl_FragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 );"
"}";
// Create user data
esContext->userData = malloc(sizeof(UserData));
UserData *userData = esContext->userData;
// Load the shaders and get a linked program object
programObject = LoadProgram ( (const char*)vShaderStr, (const char*)fShaderStr );
if(programObject == 0)
{
return GL_FALSE;
}
// Store the program object
userData->programObject = programObject;
// Get the attribute locations
userData->positionLoc = glGetAttribLocation ( g_programObject, "v_position" );
glClearColor ( 0.0f, 0.0f, 0.0f, 1.0f );
return ELG_TRUE;
}
4. 安裝并執(zhí)行Program(第三步)
void Render ( ESContext *esContext )
{
UserData *userData = esContext->userData;
GLfloat vVertices[] = { 0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f };
// 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 );
eglSwapBuffers(esContext->eglDisplay, esContext->eglSurface);
4.1 glClear
清除指定的buffer到預(yù)設(shè)值×掣剩可清除以下四類buffer:
- GL_COLOR_BUFFER_BIT
- GL_DEPTH_BUFFER_BIT
- GL_ACCUM_BUFFER_BIT
- GL_STENCIL_BUFFER_BIT
預(yù)設(shè)值通過glClearColor, glClearIndex, glClearDepth, glClearStencil, 和glClearAccum來設(shè)置恳啥。
1)glClearColor( GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)
指定color buffer的清除值偏灿,當(dāng)調(diào)用glClear(GL_COLOR_BUFFER_BIT)時才真正用設(shè)定的顏色值清除color buffer丹诀。參數(shù)值的范圍為:0~1。
2)glClearIndex(GLfloat c)
指定color index buffer清除值。
3)glClearDepth(GLclampd depth)
指定depth buffer的清除值铆遭,取值范圍為:0~1硝桩,默認(rèn)值為1。
4)glClearStencil(GLint s)
指定stencil buffer清除值的索引枚荣,初始值為0碗脊。
5)glClearAccum( GLfloat red,GLfloat green,GLfloat blue,GLfloat alpha)
指定accumulation buffer的清除值,初始值為0橄妆,取值范圍為:-1~1
4.2 glUseProgram
安裝一個program object衙伶,并把它作為當(dāng)前rendering state的一部分。
1) 當(dāng)一個可執(zhí)行程序被安裝到vertex processor害碾,下列OpenGL固定功能將被disable:
- The modelview matrix is not applied to vertex coordinates.
- The projection matrix is not applied to vertex coordinates.
- The texture matrices are not applied to texture coordinates.
- Normals are not transformed to eye coordinates.
- Normals are not rescaled or normalized.
- Normalization of GL_AUTO_NORMAL evaluated normals is not performed.
- Texture coordinates are not generated automatically.
- Per-vertex lighting is not performed.
- Color material computations are not performed.
- Color index lighting is not performed.
- This list also applies when setting the current raster position.
2)當(dāng)一個可執(zhí)行程序被安裝到fragment processor矢劲,下列OpenGL固定功能將被disable:
- Texture environment and texture functions are not applied.
- Texture application is not applied.
- Color sum is not applied.
- Fog is not applied.
4.3 glVertexAttribPointer
定義一個通用頂點屬性數(shù)組。當(dāng)渲染時慌随,它指定了通用頂點屬性數(shù)組從索引index處開始的位置和數(shù)據(jù)格式芬沉。其定義如下:
void glVertexAttribPointer(
GLuint index, // 指示將被修改的通用頂點屬性的索引
GLint size, // 指點每個頂點元素個數(shù)(1~4)
GLenum type, // 數(shù)組中每個元素的數(shù)據(jù)類型
GLboolean normalized, //指示定點數(shù)據(jù)值是否被歸一化(歸一化<[-1,1]或[0,1]>:GL_TRUE,直接使用:GL_FALSE)
GLsizei stride, // 連續(xù)頂點屬性間的偏移量,如果為0阁猜,相鄰頂點屬性間緊緊相鄰
const GLvoid * pointer);//頂點數(shù)組:其index應(yīng)該小于#define GL_MAX_VERTEX_ATTRIBS 0x8869
)
4.4glEnableVertexAttribArray
Enable由索引index指定的通用頂點屬性數(shù)組丸逸。
void glEnableVertexAttribArray(GLuint index);
void glDisableVertexAttribArray(GLuint index);
默認(rèn)狀態(tài)下,所有客戶端的能力被disabled剃袍,包括所有通用頂點屬性數(shù)組黄刚。如果被Enable,通用頂點屬性數(shù)組中的值將被訪問并被用于rendering笛园,通過調(diào)用頂點數(shù)組命令:glDrawArrays, glDrawElements,
glDrawRangeElements, glArrayElement, glMultiDrawElements, or glMultiDrawArrays.
4.5 glDrawArrays
void glDrawArrays(GLenum mode, GLint first, GLsizei count);
- mode:指明render原語隘击,如:GL_POINTS, GL_LINE_STRIP, GL_LINE_LOOP, GL_LINES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_TRIANGLES, GL_QUAD_STRIP, GL_QUADS, 和 GL_POLYGON。
- first: 指明Enable數(shù)組中起始索引研铆。
- count: 指明被render的原語個數(shù)埋同。
可以預(yù)先使用單獨的數(shù)據(jù)定義vertex、normal和color棵红,然后通過一個簡單的glDrawArrays構(gòu)造一系列原語凶赁。當(dāng)調(diào)用 glDrawArrays時,它使用每個enable的數(shù)組中的count個連續(xù)的元素逆甜,來構(gòu)造一系列幾何原
語虱肄,從第first個元素開始。
4.6 eglSwapBuffers
把EGL surface中的color buffer提交到native window進行顯示交煞。
EGLBoolean eglSwapBuffers(EGLDisplay display,EGLSurface surface)
5. 協(xié)調(diào)組織
在前面的描述中咏窿,三步曲已經(jīng)完成了:
- 初始化EGL環(huán)境,為繪圖做好準(zhǔn)備
- 生成Program
- 安裝并執(zhí)行Program
只有這三個關(guān)鍵人物素征,還不能運行集嵌,還需要一個協(xié)調(diào)組織者萝挤。
int main(int argc, char** argv)
{
ESContext esContext;
UserData userData;
int iFrames;
unsigned long iStartTime,iEndTime;
int iDeltaTime;
memset( &esContext, 0, sizeof( ESContext) );
esContext.userData = &userData;
esContext.width = 1280;
esContext.height = 720;
// Init EGL display, surface and context
if(!InitEGL(&esContext))
{
printf("Init EGL fail\n");
return GL_FALSE;
}
// compile shader, link program
if(!CreateProgram(&esContext))
{
printf("Create Program fail\n");
return GL_FALSE;
}
iStartTime = GetCurTime();
iFrames = 0;
while(1) { // render a frame
Render();
iFrames++;
iEndTime = GetCurTime();
iDeltaTime = iEndTime - iStartTime;
if(iDeltaTime >= 5000)
{
iStartTime = iEndTime;
float fFrame = iFrames * 1000.0 / iDeltaTime;
iFrames = 0;
printf("Frame.: %f\n", fFrame);
}
}
glDeleteProgram (esContext.userData->programObject);
return GL_TRUE;
}
[TOC]
總結(jié)
- 初始化EGL渲染環(huán)境和相關(guān)元素 (第一步)
- 生成Program (第二步)
- shader的編譯
- Program的鏈接
- CreateProgram 程序創(chuàng)建
vertex(頂點)
和fragment(片元)
的shader(著色器)
源碼加載
- 安裝并執(zhí)行Program (第三步)
- 使用 OpenGL ES API 繪制圖形:gl_*()
- 協(xié)調(diào)組織
- EGL釋放資源