和ES1.0不一樣,ES2.0引入了可編程管線,以下圖中,可編程階段爲VertexShader,FragmentShader頂點和片斷着色器階段。android
頂點着色器被使用在傳統的基於頂點的操做,例如位移矩陣、計算光照方程、產生貼圖
座標。頂點着色器被應用指定,應用於客戶端的頂點轉化。頂點着色器須要一個位置和顏色數據做爲輸入屬性,輸入位置數據是 4×4 的矩陣,輸出是變換後的位置和顏色。
片斷着色器不需定義輸出,這是由於片斷着色器僅僅的輸出是gl_FragColor。git
如今咱們用上一節的Android入口渲染一個三角形。這裏借用NDK包裏的例子hello-gl2。項目代碼看裏面的lesson2的taggithub
#ifndef _APPMACROS_H__ #define _APPMACROS_H__ #include <android/log.h> #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> #include <stdio.h> #include <stdlib.h> #define LOG_TAG "GLES-Tutorial" #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE,LOG_TAG,__VA_ARGS__) #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) static void printGLString(const char *name, GLenum s) { const char *v = (const char *) glGetString(s); LOGI("GL %s = %s\n", name, v); } static void checkGlError(const char* op) { for (GLint error = glGetError(); error; error = glGetError()) { LOGI("after %s() glError (0x%x)\n", op, error); } } #endif
這裏除了android裏的Log外添加了gl2.h, gl2ext.h頭文件,這兩個文件能夠在NDK包(platforms/android-19/)中找到,這裏使用了android裏的libGLESv2.so庫。printGLString能夠獲取到OpenGL屬性的狀態,checkGlError能夠檢測OpenGL狀態是否有錯誤。編程
先給Director添加成員變量:less
GLProgram *_glProgram; // opengl狀態機 GLuint _vPositionHandle; // 獲取到的頂點位置屬性
#include "Director.h" #include "AppMacros.h" // 頂點着色器 static const char gVertexShader[] = "attribute vec4 vPosition;\n" "void main() {\n" " gl_Position = vPosition;\n" "}\n"; // 片斷着色器 static const char gFragmentShader[] = "precision mediump float;\n" "void main() {\n" " gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n" "}\n"; // 三角形頂點數據 const GLfloat gTriangleVertices[] = { 0.0f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f }; // ... 省略掉一些 void Director::setFrameSize(float width, float height) { LOGI("Director::setFrameSize(%lf, %lf)", width, height); _fFrameWidth = width; _fFrameHeight = height; // 建立一個空源的OpenGL狀態機 _glProgram = new GLProgram(); // 將着色器裝載和編譯 _glProgram->initWithVertexShaderByteArray(gVertexShader, gFragmentShader); // 連接着色器 _glProgram->link(); // 使用此狀態機,着色器將能運用上 _glProgram->use(); // 獲取到位置屬性,以便用來繪製圖形;頂點着色器裏的vPosition屬性 _vPositionHandle = _glProgram->getAttribLocation("vPosition"); // 設置OpenGL視口,一個2D的長方形區域(Android裏GLSurfaceView窗體的大小) glViewport(0, 0, width, height); checkGlError("glViewport"); } void Director::mainLoop() { // 每一幀都讓灰度值疊加 static float grey; grey += 0.01f; if (grey > 1.0f) { grey = 0.0f; } // 用顏色來填充清除深度和顏色緩衝區 glClearColor(grey, grey, grey, 1.0f); glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); // 將頂點位置屬性獲取到 glVertexAttribPointer(_vPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, gTriangleVertices); checkGlError("glVertexAttribPointer"); glEnableVertexAttribArray(_vPositionHandle); // 並將三角形頂點設置進頂點矩陣 checkGlError("glEnableVertexAttribArray"); glDrawArrays(GL_TRIANGLES, 0, 3); // 繪製三角形圖元 checkGlError("glDrawArrays"); }
GLProgram是我包裝了OpenGL的一些操做,便於使用:oop
class GLProgram { public: GLProgram(); ~GLProgram(); /* * init GLProgram with vertex shader array data and fragment shader array data */ bool initWithVertexShaderByteArray(const GLchar* vShaderByteArray, const GLchar* fShaderByteArray); /* * link shader */ bool link(); /* * use this opengl program */ void use(); /* * get location attrib */ GLuint getAttribLocation(const GLchar* attrib); private: GLuint loadShader(GLenum shaderType, const GLchar* shaderSrc); GLuint _uProgram; GLuint _uVertShader; GLuint _uFragShader; };
bool GLProgram::initWithVertexShaderByteArray(const GLchar* vShaderByteArray, const GLchar* fShaderByteArray) { _uVertShader = loadShader(GL_VERTEX_SHADER, vShaderByteArray); if (!_uVertShader) { return false; } _uFragShader = loadShader(GL_FRAGMENT_SHADER, fShaderByteArray); if (!_uFragShader) { return false; } _uProgram = glCreateProgram(); if (!_uProgram) { return false; } glAttachShader(_uProgram, _uVertShader); checkGlError("glAttachShader"); glAttachShader(_uProgram, _uFragShader); checkGlError("glAttachShader"); return true; } bool GLProgram::link() { glLinkProgram(_uProgram); GLint linkStatus = GL_FALSE; glGetProgramiv(_uProgram, GL_LINK_STATUS, &linkStatus); if (linkStatus != GL_TRUE) { GLint bufLength = 0; glGetProgramiv(_uProgram, GL_INFO_LOG_LENGTH, &bufLength); if (bufLength) { char* buf = (char*) malloc(bufLength); if (buf) { glGetProgramInfoLog(_uProgram, bufLength, NULL, buf); LOGE("Could not link _uProgram:\n%s\n", buf); free(buf); } } glDeleteProgram(_uProgram); _uProgram = 0; return false; } return true; } void GLProgram::use() { glUseProgram(_uProgram); checkGlError("glUseProgram"); } GLuint GLProgram::loadShader(GLenum shaderType, const char* shaderSrc) { GLuint shader = glCreateShader(shaderType); if (shader) { glShaderSource(shader, 1, &shaderSrc, NULL); glCompileShader(shader); GLint compiled = 0; glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); // check compile information if (!compiled) { GLint infoLen = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); if (infoLen) { char* infoLog = (char*) malloc(infoLen); if (infoLog) { glGetShaderInfoLog(shader, infoLen, NULL, infoLog); // seek infor log LOGE("Could not compile shader %d:\n%s\n", shaderType, infoLog); free(infoLog); } glDeleteShader(shader); shader = 0; } } } return shader; } GLuint GLProgram::getAttribLocation(const GLchar* attrib) { GLuint retAtt = glGetAttribLocation(_uProgram, attrib); checkGlError("glGetAttribLocation"); LOGI("glGetAttribLocation(\"%s\") = %d\n", attrib, retAtt); return retAtt; }
OpenGL ES提供了一套運行期動態編譯的流程:ui
1.建立着色器:glCreateShaderthis
2.指定着色器源代碼字符串:glShaderSourcespa
3.編譯着色器:glCompileShaderdebug
4.建立着色器可執行程序:glCompileShader
5.向可執行程序中添加着色器:glAttachShader
6.連接可執行程序:glLinkProgram
編譯以前,須要修改下Android.mk
LOCAL_SRC_FILES := \ com_richard_glestutorial_GLRenderer.cpp \ ../core/Director.cpp \ ../core/GLProgram.cpp LOCAL_C_INCLUDES := \ $(LOCAL_PATH)
$ndk-build && ant debug && adb install -r bin/GlesTutorial-debug.apk
運行腳本,將能夠看一個綠色的三角形,而且背景在不斷變化。