天天看點

OpenGL ES入門詳解

        1.決定你要支援的OpenGL ES的版本。目前,OpenGL ES包含1.1和2.0兩個版本,iPhone 3G+和iPad開始支援OpenGL ES2.0。而且這兩個版本之間的差異非常大,不僅僅在程式設計思想上,API之間的差距也很大。是以,如果你想使用OpenGL ES開發3D程式或遊戲,那麼首先就要決定使用哪個版本,還是說兩個版本都支援。OpenGL ES定義了代表不同版本的宏:

                     enum

                    {

                        kEAGLRenderingAPIOpenGLES1 = 1,     //1.1版

                        kEAGLRenderingAPIOpenGLES2 = 2      //2.0版

                    }typedef NSUInteger EAGLRenderingAPI;

以iPhone代碼為例,你可以通過以下方式判斷使用者裝置所支援的OpenGL ES版本,如果支援2.0,就使用2.0進行渲染;如果僅支援1.1,則使用1.1版進行渲染:

                   EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES2;    //預設優先使用2.0版

                  m_context = [[EAGLContext alloc] initWithAPI:api];     //使用2.0版初始化EAGLContext

                  if (!m_context ) {            //使用2.0版初始化EAGLContext失敗

                      api = kEAGLRenderingAPIOpenGLES1;        //将API設為1.1版

                      m_context = [[EAGLContext alloc] initWithAPI:api];//使用1.1版初始化EAGLContext

                  }

                  if (!m_context || ![EAGLContext setCurrentContext:m_context]) {     //1.1版初始化失敗,則釋放記憶體

                      [self release];

                      return nil;

                  }

                  if (api == kEAGLRenderingAPIOpenGLES1) {

                      //使用1.1版開始渲染

                  }

                  else {

                      //使用2.0版開始渲染

                  }

         2.你想讓你的OpenGL ES程式跨手機平台運作麼?Android、iPhone、windows phone手機系統是目前最主流的手機系統,如何才能讓我們編寫出來的程式可以跨平台運作呢?好消息是,這三個平台都支援OpenGL ES,而且都支援C++語言。也許你明白了,方法就是通過C++來封裝OpenGL ES,盡量少用特定平台的耦合代碼。這樣我們就可以将封裝的OpenGL ES引擎在不同的手機平台的使用,而且僅需要修改特定平台連接配接OpenGL ES的代碼即可。

        3.入門常見代碼解析

3.1建立渲染緩沖區

    GLuint m_renderbuffer;//建立一個渲染緩沖區對象

    glGenRenderbuffers(1, &m_renderbuffer);//建立一個渲染緩沖區對象

    glBindRenderbuffer(GL_RENDERBUFFER, m_renderbuffer);//将該渲染緩沖區對象綁定到管線上

3.2建立幀緩沖區

   GLuint m_framebuffer;//建立一個幀緩沖區對象

    glGenFramebuffers(1, &m_framebuffer);//建立一個幀染緩沖區對象

    glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);//将該幀染緩沖區對象綁定到管線上

    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_renderbuffer);//将建立的渲染緩沖區綁定到幀緩沖區上,并使用顔色填充

3.3設定視口

glViewport(0, 0, width, height);//定義視口大小,說白了就是OpenGL ES視窗大小

3.4建立着色器

3.4.1建立一個空着色器

GLuint shaderHandle = glCreateShader(shaderType);//shaderType代表着色器的類型,可以是GL_VERTEX_SHADER(頂點着色器)或GL_FRAGMENT_SHADER(片元着色器)

3.4.2指定着色器源代碼

    glShaderSource(shaderHandle, 1, &source, 0);// source代表要執行的源代碼字元串數組,1表示源代碼字元串數組的字元串個數是一個,0表示源代碼字元串長度數組的個數為0個

3.4.3編譯着色器

    glCompileShader(shaderHandle);//編譯着色器

3.4.4檢查編譯是否成功

    GLint compileSuccess;

    glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &compileSuccess);//檢視編譯着色器是否成功,可選的查詢狀态有L_DELETE_STATUS,GL_COMPILE_STATUS, GL_INFO_LOG_LENGTH, GL_SHADER_SOURCE_LENGTH

//如果編譯出錯,則記錄出錯資訊

    if (compileSuccess == GL_FALSE) {

        GLchar messages[256];

        glGetShaderInfoLog(shaderHandle, sizeof(messages), 0, &messages[0]);

        std::cout << messages;

        exit(1);

    }

3.5建立渲染源程式

3.5.1建立一個空源程式

GLuint programHandle = glCreateProgram();//建立一個渲染程式

3.5.2向源程式中添加着色器

    glAttachShader(programHandle, shaderHandle);//将着色器添加到程式中

3.5.3連結源程式

    glLinkProgram(programHandle);//你可能添加了多個着色器,連結程式

3.5.4檢查連結程式是否成功

    GLint linkSuccess;

    glGetProgramiv(programHandle, GL_LINK_STATUS, &linkSuccess);//檢視連接配接是否成功

//連結失敗記錄失敗資訊

    if (linkSuccess == GL_FALSE) {

        GLchar message[256];

        glGetProgramInfoLog(programHandle, sizeof(message), 0, &message[0]);

        std::cout << message;

        exit(1);

    }

3.6頂點結構體

//此處定義了一個代表頂點的結構體,内部包含一個有兩個點(x,y)組成的頂點的位置資訊,和一個四個值(r, g, b, a)表示的顔色資訊

struct Vertex{

    float Position[2];

    float Color[4];

};

//建立頂點數組,裡面有6個頂點資訊

const Vertex vertices[] = {

    {{-0.5, -0.866}, {0.5, 1, 0.5, 1}},

    {{0.5, -0.866}, {0.2, 0.6, 0.5, 1}},

    {{0, 1}, {0.6, 0.1, 0.8, 1}},

    {{0.5, -0.866}, {0.5, 0.5, 0.5, 1}},

    {{1.5, -0.866}, {0.5, 0.5, 0.5, 1}},

    {{1, 0.4}, {0.5, 0.5, 0.5, 1}}

};

3.7着色器

3.7.1頂點着色器

#define STRINGIFY(A) #A

const char *SimpleVertexShader = STRINGIFY(

attribute vec4 Position;//位置,vec4說明有4個點組成,attribute表示屬性,由程式提供輸入值

attribute vec4 SourceColor;//源顔色,RGBA

varying vec4 DestinationColor;//目标顔色,輸出值,用來傳遞到片元着色器,vary表示可變傳出變量

uniform mat4 Projection;//投影矩陣,mat4表示一個4*4的矩陣,uniform表示統一的,不變的,每個頂點都是固定的這個值

uniform mat4 Modelview;//模型矩陣

void main(void){

    DestinationColor = SourceColor;//簡單的将源顔色賦給目标顔色

    gl_Position = Projection * Modelview * Position;//通過矩陣乘法獲得目标位置,gl_Position是OpenGL ES内定的值,必須指定

}

);

3.7.2片元着色器

const char *SimpleFragmentShader = STRINGIFY(

varying lowp vec4 DestinationColor;//由頂點着色器傳入,lowp表示低精度

void main(void){

    gl_FragColor = DestinationColor;//片元顔色,gl_FragColor也是OpenGL ES内定的,必須指定

}

);

3.8開始渲染

3.8.1填充(清理)螢幕

glClearColor(0.1f, 0.9f, 0.5f, 1);//指定填充螢幕的RGBA值

glClear(GL_COLOR_BUFFER_BIT);//指定要清除哪些緩沖區,GL_COLOR_BUFFER_BIT表示顔色緩沖區,GL_DEPTH_BUFFER_BIT表示深度緩沖區,GL_STENCIL_BUFFER_BIT表示模闆緩沖區

3.8.2從着色器代碼中擷取屬性資訊

    GLuint m_simpleProgram = programHandle;   

    GLuint positionSlot = glGetAttribLocation(m_simpleProgram, "Position");//從着色器源程式中的頂點着色器中擷取Position屬性

    GLuint colorSlot = glGetAttribLocation(m_simpleProgram, "SourceColor");//從着色器源程式中的頂點着色器中擷取SourceColor屬性

3.8.3開啟頂點屬性數組

    glEnableVertexAttribArray(positionSlot);

    glEnableVertexAttribArray(colorSlot);

3.8.4為着色器屬性指派

    GLsizei stride = sizeof(Vertex);//單個頂點的資料長度

    const GLvoid *pCoords = &vertices[0].Position[0];//頂點數組中的位置數組首位址

    const GLvoid *pColors = &vertices[0].Color[0];/頂點數組中的顔色數組首位址

    glVertexAttribPointer(positionSlot, 2, GL_FLOAT, GL_FALSE, stride, pCoords);//為頂點着色器位置資訊指派,positionSlot表示頂點着色器位置屬性(即,Position);2表示每一個頂點資訊由幾個值組成,這個值必須位1,2,3或4;GL_FLOAT表示頂點資訊的資料類型;GL_FALSE表示不要将資料類型标準化(即fixed-point);stride表示數組中每個元素的長度;pCoords表示數組的首位址

    glVertexAttribPointer(colorSlot, 4, GL_FLOAT, GL_FALSE, stride, pColors);//同上

3.8.5渲染頂點

    GLsizei vertexCount = sizeof(vertices)/sizeof(Vertex);//頂點個數

    glDrawArrays(GL_TRIANGLES, 0, vertexCount);//将頂點數組使用三角形渲染,GL_TRIANGLES表示三角形, 0表示數組第一個值的位置,vertexCount表示數組長度

3.8.6渲染完畢,關閉頂點屬性數組

    glDisableVertexAttribArray(positionSlot);

    glDisableVertexAttribArray(colorSlot);

4.OpenGL 1.1和2.0在程式設計上的差別

4.1函數命名上的差別

1.1版API函數和宏末尾通常都為加上OES(即OpenglES的縮寫),2.0版本基本上都把這個字尾名給去掉了,如:

1.1API函數和宏:glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_renderbuffer);

2.0API函數和宏:glBindRenderbuffer(GL_RENDERBUFFER, m_renderbuffer);

4.2渲染方式不同

1.1版是基于不可程式設計管線,2.0版是基于可程式設計管線的,明顯的差别是1.1不支援着色器而2.0支援着色器,如下:

在1.1版将頂點渲染到螢幕上一般這樣寫:

    glMatrixMode(GL_PROJECTION);

    const float maxX = 2;

    const float maxY = 3;

    glOrthof(-maxX, maxX, -maxY, maxY, -1, 1);

    glMatrixMode(GL_MODELVIEW);

2.0版支援着色器,就不再這麼渲染頂點了。将頂點屬性和變換方式全部放在頂點着色器和片元着色器中,然後程式中通過着色器來渲染

4.3編寫同樣的程式API不同

如,同樣是激活和關閉頂點數組,在1.1中是glEnableClientState(GL_VERTEX_ARRAY);和glDisableClientState(GL_VERTEX_ARRAY);

但在2.0中,就變成了:glEnableVertexAttribArray(*);和glDisableVertexAttribArray(*);

差别的來源就在于1.1不使用着色器,而2.0使用着色器。

4.4程式設計難易程度不同。

1.1是基于不可程式設計管線的,是以,管線的各個元件都是寫好的,我們僅需要調用即可。而2.0是基于可程式設計管線的,靈活性大大增加了,但是編寫的難度和複雜度也增加了,因為什麼功能都得自己寫了。

5.着色器的使用流程

剛開始學習OpenGL ES2.0,對其着色器十分不感冒,什麼是着色器,着色器該怎麼使用,着色器包含哪些内容呢?

着色器就是一段包含着色資訊的源代碼字元串。通常着色器分為頂點着色器(Vertex Shader)和片元着色器(Fragment Shader),兩個着色器分别寫在不同的檔案中,檔案沒有固定的字尾名,可以根據你自己的愛好寫,但是最好能差別檔案中寫的是頂點着色器還是片元着色器,不然時間長了自己都不知道哪個檔案中寫的是什麼資訊了。如你可以給你的頂點着色器字尾名命名為:vert, ver, v, vsh等,給你的片元着色器字尾名命名為:frag, fra, f, fsh等。

着色器源代碼和OpenGL源代碼不是一起編譯的,是以要特别強調我剛才說的“着色器是一段包含着色資訊的源代碼字元串”。是以,OpenGL源代碼肯定是和工程一起編譯的,但是着色器源代碼是在運作期編譯的。你可能會問,着色器的源代碼是一個字元串怎麼編譯呢?是以OpenGL ES提供了一套運作期動态編譯的流程:

(1)建立着色器:glCreateShader

(2)指定着色器源代碼字元串:glShaderSource

(3)編譯着色器:glCompileShader

(4)建立着色器可執行程式:glCompileShader

(5)向可執行程式中添加着色器:glAttachShader

(6)連結可執行程式:glLinkProgram