@cxm-2016
2016-11-21T04:40:38.000000Z
字数 7486
阅读 8588
OpenGL-ES 0
版本:1
作者:陈小默
版权声明:禁止商用,禁止转载
上一篇:OpenGL-ES 3.0学习指南(五)——EGL基础
在开始书写程序之前,我们先看一下目录结构是什么样的(如下图):

该文件是Java文件,其内容如下,不再详细解释。
/*** 陈小默 16/10/24.*/public class TriangleLib {static {System.loadLibrary("triangle-lib");}//初始化本地GLESpublic static native boolean init();//为本地GLES设置宽和高public static native void resize(int width, int height);//用来绘制图形public static native void step();}
这里是一个Kotlin文件,我们在Android Studio右键New中可以找到。
/*** 陈小默 16/10/24.*/class TriangleView(context: Context):GLSurfaceView(context) {init {setEGLConfigChooser(8,8,8,0,16,0)setEGLContextClientVersion(3)setRenderer(TriangleRender())}class TriangleRender:GLSurfaceView.Renderer{override fun onDrawFrame(gl: GL10?) {TriangleLib.step()}override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {TriangleLib.resize(width, height)}override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {TriangleLib.init()}}}
在这个文件中,我们创建一个View继承GLSurfaceView,然后调用其三个方法用来启用OpenGL,
如果没有调用setEGLConfigChooser方法,则默认设置颜色模式为RGB_888,色彩深度16位。setEGLContextClientVersion方法指定了EL的版本,这里指定为了3,如果你的手机不支持OpenGL ES 3.0 则需要设置为2。接下来,我们创建了一个内部类TriangleRender,该类继承自GLSurfaceView.Renderer,这是Android SDK 中实现 OpenGL ES的方式,但是这里我们需要借助它的着色器进入本地环境。我们可以从实现中看到,我们调用的TriangleLib中的方法并没有跟这个Render产生任何交互,那么我们为什么还要通过Render去启动我们的本地方法呢?这涉及到了OpenGL ES的实现其实是基于管线的,整个操作过程就像水流一样分为上游、下游等,Render中的各个方法就是水流的不同阶段,我们的native方法是需要在特定的阶段调用的,所以才需要借助Render来标志当前进行到哪一个阶段了。但实际上,我们也可以在本地方法自己判断水流并进行相应的操作。在这里我们尽量不要使用Java中面向对象的思想去思考这些问题,应该将思想转变为面向过程。具体详细我们将会在后续章节中介绍。
Kotlin小贴士:
- 跟随类名的括号表示主构造器,如果不提供默认以外的构造方法可以省略括号
- 冒号
:表示继承关系- init为对象创建时的初始化函数,在构造器之后被调用
这个Activity没有别的功能,不解释
class TriangleActivity : AppCompatActivity() {lateinit var triangleView :TriangleViewoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)triangleView = TriangleView(this)setContentView(triangleView)}override fun onPause() {super.onPause()triangleView.onPause()}override fun onResume() {super.onResume()triangleView.onResume()}}
Kotlin小贴士:
- var 关键字表示这是一个可读可写的方法集(如果不好理解可以当做Java中实现了Getter/Setter方法的属性),当然还有一个val关键字表示一个可读不可写的方法集(Java中只实现了Getter的属性)。在Kotlin中域被封装到了val/var的内部,对外是不可见的(包括类本身)。
- lateinit 由于Kotlin中的安全检查机制,当你声明一个属性时,必须对其进行初始化以保证在任何位置访问其值的操作都是安全的,但是这回带来一些不便,于是其提供了此关键字用来说明我现在不想初始化。注意:访问任何未初始化的属性都是非法的。
我们在MainActivity的布局文件中增加一个按钮
<Buttonandroid:text="绘制三角形"android:layout_width="match_parent"android:layout_height="wrap_content"android:id="@+id/btn_drawTriangle" />
然后在MainActivity中给按钮添加事件监听,使其能够打开TriangleActivity。
class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)btn_drawTriangle.setOnClickListener {val intent = Intent(this,TriangleActivity::class.java)startActivity(intent)}}}
Kotlin小贴士:
如果你按照上一章的教程进行过配置的话,是可以在Activity中使用布局ID的。另外这里使用了Lambda语法,如果你对Lambda表达式不了解的话,可以查阅Kotlin Lambda 说明和Kotlin 函数式接口。
cmake_minimum_required(VERSION 3.4.1)#设置编译指令set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-rtti -fno-exceptions -Wall")#如果当前系统版本过低设置拒绝编译if (${ANDROID_PLATFORM_LEVEL} LESS 11)message(FATAL_ERROR "OpenGL 2 is not supported before API level 11 (currently using ${ANDROID_PLATFORM_LEVEL}).")return()elseif (${ANDROID_PLATFORM_LEVEL} LESS 18)add_definitions("-DDYNAMIC_ES3")set(OPENGL_LIB GLESv2)else ()set(OPENGL_LIB GLESv3)endif (${ANDROID_PLATFORM_LEVEL} LESS 11)# Include libraries needed for Triangle libadd_library( triangle-libSHAREDsrc/main/cpp/triangle-lib.cpp)target_link_libraries(triangle-lib${OPENGL_LIB}androidEGLlogm)
这里才是重中之重的重点,接下来我们会使用C代码实现一个在屏幕上绘制三角形的程序。在开始之前我们先进行一些说明。首先是着色器,这里的着色器跟Android中的不一样,这里的着色器是一段符合GLSL规范的字符串,其会在程序运行时被编译为GPU(图形处理器-显卡)能够识别的代码,也可以说这是一种独立的编程语言,这些着色器的目标是GPU而不是CPU。这是符合设计规范的,首先GPU是专为图像处理而设计的,其次如果将所有处理交给CPU后果可想而知。所以我们在编写着色器代码的时候可能会对其语法感到困惑,这是正常的,毕竟我们之前写的代码都是面向内存和CPU的。如果你对其中的流程和各种着色器仍有疑问,不用担心,我们会在后续章节中继续学习。接下来是我们的代码部分:
#include <GLES3/gl3.h>#include <android/log.h>#define LOG_TAG "TRIANGLE-LIB"#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)#include <jni.h>#include <stdlib.h>static const char VERTEX_SHADER[]="#version 300 es\n""layout(location = 0) in vec4 vPosition;\n""void main(){\n""gl_Position = vPosition;\n""}\n";static const char FRAGMENT_SHADER[]="#version 300 es\n""precision mediump float;\n""out vec4 fragColor;\n""void main(){\n""fragColor = vec4(1.0,0.0,0.0,1.0);\n""}\n";static const GLfloat VERTEX[]={0.0f,0.5f,0.0f,-0.5f,-0.5f,0.0f,0.5f,-0.5f,0.0f};bool checkGlError(const char* funcName) {GLint err = glGetError();if (err != GL_NO_ERROR) {ALOGE("GL error after %s(): 0x%08x\n", funcName, err);return true;}return false;}GLuint createShader(GLenum shaderType, const char* src) {GLuint shader = glCreateShader(shaderType);if (!shader) {checkGlError("glCreateShader");return 0;}glShaderSource(shader, 1, &src, NULL);GLint compiled = GL_FALSE;glCompileShader(shader);glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);if (!compiled) {GLint infoLogLen = 0;glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLen);if (infoLogLen > 0) {GLchar* infoLog = (GLchar*)malloc(infoLogLen);if (infoLog) {glGetShaderInfoLog(shader, infoLogLen, NULL, infoLog);ALOGE("Could not compile %s shader:\n%s\n",shaderType == GL_VERTEX_SHADER ? "vertex" : "fragment",infoLog);free(infoLog);}}glDeleteShader(shader);return 0;}return shader;}GLuint createProgram(const char* vtxSrc, const char* fragSrc) {GLuint vtxShader = 0;GLuint fragShader = 0;GLuint program = 0;GLint linked = GL_FALSE;vtxShader = createShader(GL_VERTEX_SHADER, vtxSrc);if (!vtxShader)goto exit;fragShader = createShader(GL_FRAGMENT_SHADER, fragSrc);if (!fragShader)goto exit;program = glCreateProgram();if (!program) {checkGlError("glCreateProgram");goto exit;}glAttachShader(program, vtxShader);glAttachShader(program, fragShader);glLinkProgram(program);glGetProgramiv(program, GL_LINK_STATUS, &linked);if (!linked) {ALOGE("Could not link program");GLint infoLogLen = 0;glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLen);if (infoLogLen) {GLchar* infoLog = (GLchar*)malloc(infoLogLen);if (infoLog) {glGetProgramInfoLog(program, infoLogLen, NULL, infoLog);ALOGE("Could not link program:\n%s\n", infoLog);free(infoLog);}}glDeleteProgram(program);program = 0;}exit:glDeleteShader(vtxShader);glDeleteShader(fragShader);return program;}GLuint program;extern "C"{JNIEXPORT jboolean JNICALL Java_com_github_cccxm_gles_model_TriangleLib_init(JNIEnv* env, jobject obj);JNIEXPORT void JNICALL Java_com_github_cccxm_gles_model_TriangleLib_resize(JNIEnv* env, jobject obj, jint width, jint height);JNIEXPORT void JNICALL Java_com_github_cccxm_gles_model_TriangleLib_step(JNIEnv* env, jobject obj);}JNIEXPORT jboolean JNICALL Java_com_github_cccxm_gles_model_TriangleLib_init(JNIEnv* env, jobject obj){program = createProgram(VERTEX_SHADER, FRAGMENT_SHADER);if (!program){ALOGE("程序创建失败");return JNI_FALSE;}glClearColor(0,0,0,0);return JNI_TRUE;}JNIEXPORT void JNICALL Java_com_github_cccxm_gles_model_TriangleLib_resize(JNIEnv* env, jobject obj, jint width, jint height){glViewport(0, 0, width, height);glClear(GL_COLOR_BUFFER_BIT);}JNIEXPORT void JNICALL Java_com_github_cccxm_gles_model_TriangleLib_step(JNIEnv* env, jobject obj){glClear(GL_COLOR_BUFFER_BIT);glUseProgram(program);glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,VERTEX);glEnableVertexAttribArray(0);glDrawArrays(GL_TRIANGLES,0,3);}
到现在为止我们的第一个三角形程序就完成了,现在运行程序就能看到开篇图片上的三角形了。
下一篇:NDK开发OpenGL ES 3.0(三)——着色器基础
[1]Donald Hearn,M.Pauline Barker.计算机图形学 第四版(蔡士杰 译).北京:电子工业出版社
[2]Dave Shreiner,Graham Sellers.OpenGL编程指南 第八版(王锐 译).北京:机械工业出版社
[3]Dan Ginsburg,Budirjanto Purnomo.OpenGL ES 3.0 编程指南 第二版(姚军 译).北京:机械工业出版社
[4]GoogleSamples - Android NDK