若是你看遍了網上那些只是在C++裏面輸出一個 ‘ helloWorld ’ 的NDK教程的話,能夠看看本系列的文章,本系列是經過NDK的運用的例子來學習NDK。這是本系列的第三篇,這是一個opengl的例子。html
本文的代碼在這裏!建議下載到本地閱讀。java
若是對這方面感興趣,能夠看看前兩篇。android
Android鬼點子-經過Google官方示例學NDK(1)——主要說的是如何在NDK使用多線程,還有就是基礎的java與c++的相互調用。c++
Android鬼點子-經過Google官方示例學NDK(2)——主要是說的不使用java代碼,用c++寫一個activity。git
Android鬼點子-經過Google官方示例學NDK(4)——主要是說的視頻解碼相關的內容。github
首先運行一下,效果是這樣的,一個「原諒色」的是三角形,背景色會在白色到黑色之間變換。:數組
代碼結構:緩存
Activity裏面放了一個GL2JNIView。GL2JNIView是GLSurfaceView的一個子類。GL2JNIView的重點是設置一個Renderer,在Renderer中調用了C++實現。bash
private static class Renderer implements GLSurfaceView.Renderer {
public void onDrawFrame(GL10 gl) {
GL2JNILib.step();
}
public void onSurfaceChanged(GL10 gl, int width, int height) {
GL2JNILib.init(width, height);
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// Do nothing.
}
}
複製代碼
onSurfaceCreated是在建立的時候調用,onSurfaceChanged是在尺寸變化的時候調用,onDrawFrame是在繪製每一幀的時候調用。多線程
GL2JNILib.step();
和GL2JNILib.init(width, height);
都是JNI實現的。直接進入gl_code.cpp中看實現。 首先看看如何初始化:
JNIEXPORT void JNICALL Java_com_android_gl2jni_GL2JNILib_init(JNIEnv * env, jobject obj, jint width, jint height) {
setupGraphics(width, height);
}
bool setupGraphics(int w, int h) {
printGLString("Version", GL_VERSION);
printGLString("Vendor", GL_VENDOR);
printGLString("Renderer", GL_RENDERER);
printGLString("Extensions", GL_EXTENSIONS);
LOGI("setupGraphics(%d, %d)", w, h);
gProgram = createProgram(gVertexShader, gFragmentShader);
if (!gProgram) {
LOGE("Could not create program.");
return false;
}
// 向着色器程序中傳遞數據
gvPositionHandle = glGetAttribLocation(gProgram, "vPosition");//獲取着色器程序中,指定爲attribute類型變量的id
checkGlError("glGetAttribLocation");
LOGI("glGetAttribLocation(\"vPosition\") = %d\n",
gvPositionHandle);
//設置可見區域:左下(0,0)開始 區域的 寬 高
glViewport(0, 0, w, h);
checkGlError("glViewport");
return true;
}
複製代碼
這裏主要是設置的要執行的OpenGLShading Language (GLSL)代碼createProgram(gVertexShader, gFragmentShader)
,和取到傳入參數的句柄glGetAttribLocation(gProgram, "vPosition")
。
先看看createProgram方法,關鍵的地方加了註釋:
GLuint createProgram(const char* pVertexSource, const char* pFragmentSource) {
GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource);
if (!vertexShader) {
return 0;
}
GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource);
if (!pixelShader) {
return 0;
}
// 建立着色器程序
GLuint program = glCreateProgram();
if (program) {// 若程序建立成功則向程序中加入頂點着色器與片元着色器
glAttachShader(program, vertexShader);
checkGlError("glAttachShader");
glAttachShader(program, pixelShader);
checkGlError("glAttachShader");
// 連接程序
glLinkProgram(program);
GLint linkStatus = GL_FALSE;
// 獲取program的連接狀況
glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
if (linkStatus != GL_TRUE) {
GLint bufLength = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
if (bufLength) {
char* buf = (char*) malloc(bufLength);
if (buf) {
//在鏈接階段使用glGetProgramInfoLog獲取鏈接錯誤
glGetProgramInfoLog(program, bufLength, NULL, buf);
LOGE("Could not link program:\n%s\n", buf);
free(buf);
}
}
glDeleteProgram(program);
program = 0;
}
}
return program;
}
複製代碼
這裏調用了一個工具方法loadShader
,用來載入代碼,加了註釋:
//工具方法
GLuint loadShader(GLenum shaderType, const char* pSource) {
// 建立一個vertex shader類型(GLES20.GL_VERTEX_SHADER,頂點shader)
// 或fragment shader類型(GLES20.GL_FRAGMENT_SHADER,片元shader)
GLuint shader = glCreateShader(shaderType);
if (shader) {
// 將源碼添加到shader並編譯之
glShaderSource(shader, 1, &pSource, NULL);
glCompileShader(shader);
GLint compiled = 0;
// 獲取Shader的編譯狀況
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
if (!compiled) {
GLint infoLen = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen) { //若是編譯失敗顯示錯誤日誌並刪除此shader
char* buf = (char*) malloc(infoLen);
if (buf) {
glGetShaderInfoLog(shader, infoLen, NULL, buf);
LOGE("Could not compile shader %d:\n%s\n",
shaderType, buf);
free(buf);
}
glDeleteShader(shader);
shader = 0;
}
}
}
return shader;
}
複製代碼
被載入的代碼:
//auto 自動推斷類型 OpenGLShading Language (GLSL)代碼,必須在使用前編譯
//vec4(四維向量) 座標點 P = (wx, wy, wz, w) 的 w值都是1,也必須是1
auto gVertexShader =
"attribute vec4 vPosition;\n"// 應用程序傳入頂點着色器的頂點位置
"void main() {\n"
" gl_Position = vPosition;\n"
"}\n";
auto gFragmentShader =
"precision mediump float;\n" // 設置工做精度
"void main() {\n"
" gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n" // 進行紋理採樣 r:0 g:1 b:0 a:1
"}\n";
複製代碼
這兩句就是設置了頂點的變量和顏色。頂點變量gl_Position
會被外部設置的vPosition
所賦值。而vPosition
又是經過gvPositionHandle =glGetAttribLocation(gProgram, "vPosition")
方法在外部取到句柄。
JNIEXPORT void JNICALL Java_com_android_gl2jni_GL2JNILib_step(JNIEnv * env, jobject obj) {
renderFrame();
}
void renderFrame() {
static float grey;
grey += 0.01f;
if (grey > 1.0f) {
grey = 0.0f;
}
//設置背景色 ,「底色」
//紅、綠、藍和 alpha 值,指定值範圍均爲[ 0.0f,1.0f ]
glClearColor(grey, grey, grey, 1.0f);
checkGlError("glClearColor");
//用來清除屏幕顏色,即將屏幕的全部像素點都還原爲 「底色」,但屏蔽參數的操做
//GL_COLOR_BUFFER_BIT 指定當前被激活爲寫操做的顏色緩存
//GL_DEPTH_BUFFER_BIT 指定深度緩存
glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
checkGlError("glClear");
//將program加入OpenGL ES環境中(使用shader程序)
glUseProgram(gProgram);
checkGlError("glUseProgram");
//指定要修改的頂點着色器中頂點變量id
//2:指定每一個頂點屬性的組件數量 2個一組
//GL_FLOAT:每一個組件的數據類型
//固定點數據值是否應該被歸一化(GL_TRUE)或者直接轉換爲固定點值(GL_FALSE)
//指定連續頂點屬性之間的偏移量。若是爲0,那麼頂點屬性會被理解爲:它們是緊密排列在一塊兒的。初始值爲0
//頂點的緩衝數據
glVertexAttribPointer(gvPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, gTriangleVertices); // 頂點座標傳遞到頂點着色器
checkGlError("glVertexAttribPointer");
// 容許使用頂點座標數組,這裏一共傳入6個數字,2個一組,一共3組
glEnableVertexAttribArray(gvPositionHandle);
checkGlError("glEnableVertexAttribArray");
// 圖形繪製 繪製三角形。第一個點的索引是0 ,共3個點 詳細:http://blog.sina.com.cn/s/blog_4119bd830100rvip.html
glDrawArrays(GL_TRIANGLES , 0, 3);
checkGlError("glDrawArrays");
}
複製代碼
因爲glClearColor(grey, grey, grey, 1.0f);
中grey
的值的變化,背景色會從白色到黑色漸變。
而後給初始化時取到的句柄賦值glVertexAttribPointer(gvPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, gTriangleVertices);
這裏的gTriangleVertices
就是頂點的座標:
//屏幕中心是(0,0)
const GLfloat gTriangleVertices[] = { 0.0f, 0.5f, -0.5f, -0.5f,
0.5f, -0.5f };
複製代碼
glEnableVertexAttribArray(gvPositionHandle);
容許使用頂點座標數組,這裏一共傳入6個數字,2個一組,一共3組。
glDrawArrays(GL_TRIANGLES , 0, 3);
圖形繪製,繪製三角形。第一個點的索引是0 ,共3個點
能夠修改auto gFragmentShader = "precision mediump float;\n" // 設置工做精度 "void main() {\n" " gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n" // 進行紋理採樣 r:0 g:1 b:0 a:1 "}\n";
中rgb的值,來修改三角形的顏色。