本文来自:http://www.cnblogs.com/bpasser/archive/2011/10/19/2217411.html

本文为转载,版权为原作者所有!

运行时检测OpenGL ES版本

对此Android文档中并没有提及,但是<NDK>/sampels/hello-gl2 示例工程中透露出了一些蛛丝马迹,从中我们可以归纳出,要进行OpenGL ES 2.0的渲染,需要有2个步骤:

(1)创建OpenGL ES 2.0 的 Rendering Context

EGL 创建 Context 的函数是

EGLContext eglCreateContext(EGLDisplay display,    EGLConfig config,    EGLContext share_context,    EGLint const * attrib_list)

最后一个参数我们一般会给一个NULL值。根据 EGL 1.0 规范,最后一个参数不使用,但是EGL实现可能扩展此参数用于特定目的。Android 的 EGL 正是在此处引入一个值为 0x3098 的属性,用于指定 OpenGL ES 的版本。OpenGL ES 2.0 的版本值为 2

(2)获取 OpenGL ES 2.0 的 EGL Config

对于

EGLBoolean eglChooseConfig(EGLDisplay display,    EGLint const * attrib_list,    EGLConfig * configs,    EGLint config_size,    EGLint * num_config)

的 attrib_list 参数,Android 引入了一个 EGL10.EGL_RENDERABLE_TYPE=0x3040 的属性,EGL 1.0 规范中并未规定此属性。要进行OpenGL ES 2.0 的渲染,需指定此属性值为 4

根据以上两点,我们可以在运行时首先初始化 EGL 支持 OpenGL ES 2.0,如果失败,则回退到 OpenGL ES 1.x,如下:

public class EGLHelper {    public static final int OPENGL_ES_VERSION_1x = 1;    public static final int OPENGL_ES_VERSION_2 = 2;    private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;    private static final int EGL_OPENGL_ES2_BIT = 4;    private EGL10 egl;    private EGLDisplay eglDisplay;    private EGLConfig eglConfig;    private EGLContext eglContext;    private EGLSurface eglSurface;    public EGLHelper() {    }    public boolean initialize(SurfaceHolder holder, int glesVersion) {        if (OPENGL_ES_VERSION_1x != glesVersion && OPENGL_ES_VERSION_2 != glesVersion) {            throw new IllegalArgumentException("GL ES version has to be one of " + OPENGL_ES_VERSION_1x + " and "                    + OPENGL_ES_VERSION_2);        }        // ...        // Choose an EGLConfig        int[] attrList;        if (OPENGL_ES_VERSION_1x == glesVersion) {            attrList = new int[] { //            EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT, //                    EGL10.EGL_RED_SIZE, 8, //                    EGL10.EGL_GREEN_SIZE, 8, //                    EGL10.EGL_BLUE_SIZE, 8, //                    EGL10.EGL_DEPTH_SIZE, 16, //                    EGL10.EGL_NONE //            };        } else {            attrList = new int[] { //            EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, //                    EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT, //                    EGL10.EGL_RED_SIZE, 8, //                    EGL10.EGL_GREEN_SIZE, 8, //                    EGL10.EGL_BLUE_SIZE, 8, //                    EGL10.EGL_DEPTH_SIZE, 16, //                    EGL10.EGL_NONE //            };        }        EGLConfig[] configOut = new EGLConfig[1];        int[] configNumOut = new int[1];        if (egl.eglChooseConfig(eglDisplay, attrList, configOut, 1, configNumOut) && 1 == configNumOut[0]) {            eglConfig = configOut[0];        } else {            // ...            return false;        }        // Create rendering context        int[] contextAttrs;        if (OPENGL_ES_VERSION_1x == glesVersion) {            contextAttrs = null;        } else {            contextAttrs = new int[] { EGL_CONTEXT_CLIENT_VERSION, OPENGL_ES_VERSION_2, //                    EGL10.EGL_NONE };        }        eglContext = egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, contextAttrs);        if (null == eglContext || EGL10.EGL_NO_CONTEXT == eglContext) {            // ...            return false;        }        // ...        return true;    }    public void destroy() {        // ...    }}

然后:

            eglHelper = new EGLHelper();            if (eglHelper.initialize(getHolder(), EGLHelper.OPENGL_ES_VERSION_2)) {                // Create RenderingEngine2            } else if (eglHelper.initialize(getHolder(), EGLHelper.OPENGL_ES_VERSION_1x)) {                // Create RenderingEngine1            } else {                // ...            }

Shader

OpenGL ES 2.0 引入了 Shader 的概念。Shader 就是交给 OpenGL 去执行的一段程序,用 OpenGL Shading Language(GLSL)编写。Shader 分为 Vertex Shader 和 Fragement Shader 两类,Vertex Shader 用于处理通过 glDrawArrays() 提交给 OpenGL 的顶点数据,Fragement Shader 用于计算顶点的颜色

目前我所知的就是这么多了。本篇 HellowArrow 程序所用的 Vertex Shader 和 Fragement Shader 我直接从原书复制。Vertex Shader 源代码文件 Shade.vert 内容为

const char* VERTEX_SHADER =STRINGIFY(attribute vec4 Position;attribute vec4 SourceColor;varying vec4 DestinationColor;uniform mat4 Projection;uniform mat4 Modelview;void main(void){    DestinationColor = SourceColor;    gl_Position = Projection * Modelview * Position;});

很怪异的语法,后面再学习。Fragement Shader 源文件 Shader.frag 的内容为:

const char * FRAG_SHADER=STRINGIFY(varying lowp vec4 DestinationColor;void main(void){    gl_FragColor = DestinationColor;});

这两个 Shader 源文件其实是两个 STRINGIFY 宏的扩展(或者说调用吧),GLSL 作为宏变量 。STRINGIFY 宏定义在 RenderingEngine2.c 中,并且用#include 将这两个文件的内容包含进来:

#define STRINGIFY(A) #A#include "Shader.vert"#include "Shader.frag"

注意上面 STRINGIFY 宏定义中“#A”前缀的 # 符号。# 是一个 c 预处理符,它将宏变量转换为字符串字面值,例如:

#define AS_STRING(A) #Aconst char * str = AS_STRING(this is a c string!);const char * str_qt = AS_STRING("this is a quoted c string!");

等效于

const char * str = "this is a c string!";const char * str_qt = "\"this is a quoted c string!\"";

因此,前面的 RenderingEngine2.c 代码片断定义了 VERTEX_SHADER 和 FRAG_SHADER 两个字符串变量,字符串内容分别为 Vertex Shade 和 Fragement Shader 代码。这两段代码会在运行时提交给 OpenGL 编译并执行

RenderingEngine2.c

RenderingEngine2.c 对应于前一篇中的 RenderingEngine1.c,OpenGL 绘图操作(包括动画)都在这个文件中实现,只不过这次用 OpenGL ES 2.0 而不是 OpenGL ES 1.x 来实现。下面我只列出 RenderingEngine2.c 与 RenderingEngine1.c 中不同的地方

initialize()

与 RenderingEngine1.c 的 initialize() 函数一样,主要功能还是设置 Viewport 和正交投影矩阵,代码为:

static GLuint simpleProgram;void initialize(int width, int height) {     glViewport(0, 0, width, height);    simpleProgram = buildProgram(VERTEX_SHADER, FRAG_SHADER);    glUseProgram(simpleProgram);    // Initialize the projection matrix.    applyOrtho(2, 3); }

glViewport() 函数在前一篇讲过了,它设置 OpenGL 绘图的范围

接下来出现了 OpenGL ES 2.0 的新东西。OpenGL ES 2.0 将几个 Shader 链接成一个单元,称为 Program(程序),我们这里用一个 buildProgram() 函数创建一个 Program,它由前面定义的 Vertex Shader 和 Fragement Shader 构成。buildProgram() 函数的代码是:

static GLuint buildProgram(const char* vertexShaderSource,        const char* fragmentShaderSource) {    GLuint vertexShader = buildShader(vertexShaderSource, GL_VERTEX_SHADER);    GLuint fragmentShader = buildShader(fragmentShaderSource, GL_FRAGMENT_SHADER);    GLuint programHandle = glCreateProgram();    glAttachShader(programHandle, vertexShader);    glAttachShader(programHandle, fragmentShader);    glLinkProgram(programHandle);    GLint linkSuccess;    glGetProgramiv(programHandle, GL_LINK_STATUS, &linkSuccess);    if (linkSuccess == GL_FALSE) {        // ...    }    return programHandle;}

buildShader() 是自定义函数,用 GLSL 创建 Shader,稍后再看这个函数。其他的几个 glXxx() 函数,从函数名就能看出它的作用

buildShader() 代码为:

static GLuint buildShader(const char* source, GLenum shaderType) {    GLuint shaderHandle = glCreateShader(shaderType);    glShaderSource(shaderHandle, 1, &source, 0);    glCompileShader(shaderHandle);    GLint compileSuccess;    glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &compileSuccess);    if (compileSuccess == GL_FALSE) {        // ...    }    return shaderHandle;}

很直观的过程:创建、指定GLSL代码、编译

前一篇中用 OpenGL ES 1.0 设置正交投影的过程是:

    glMatrixMode(GL_PROJECTION);    // ...    glOrthof(-maxX, maxX, -maxY, maxY, -1.0f, 1.0f);    glMatrixMode(GL_MODELVIEW);

但现在通过自定义 applyOrtho() 函数来设置OpenGL ES 的正交投影矩阵:

static void applyOrtho(float maxX, float maxY) {    float a = 1.0f / maxX;    float b = 1.0f / maxY;    float ortho[16] = {//            a, 0, 0, 0, //            0, b, 0, 0, //            0, 0, -1, 0,//            0, 0, 0, 1 //            };    GLint projectionUniform = glGetUniformLocation(simpleProgram,            "Projection");    glUniformMatrix4fv(projectionUniform, 1, 0, &ortho[0]);}

太奥妙了,直接上矩阵了,不懂。后面再慢慢淆吧:)

render()

render() 又是一段看不懂的代码啊:

void render() {    glClearColor(0.5f, 0.5f, 0.5f, 1);    glClear(GL_COLOR_BUFFER_BIT);    applyRotation(-currentDegree);    GLuint positionSlot = glGetAttribLocation(simpleProgram, "Position");    GLuint colorSlot = glGetAttribLocation(simpleProgram, "SourceColor");    glEnableVertexAttribArray(positionSlot);    glEnableVertexAttribArray(colorSlot);    GLsizei stride = sizeof(struct Vertex);    const GLvoid* pCoords = vertices[0].position;    const GLvoid* pColors = vertices[0].color;    glVertexAttribPointer(positionSlot, 2, GL_FLOAT, GL_FALSE, stride, pCoords);    glVertexAttribPointer(colorSlot, 4, GL_FLOAT, GL_FALSE, stride, pColors);    GLsizei vertexCount = sizeof(vertices) / sizeof(struct Vertex);    glDrawArrays(GL_TRIANGLES, 0, vertexCount);    glDisableVertexAttribArray(positionSlot);    glDisableVertexAttribArray(colorSlot); }

glClearColor()、glClear() 清屏

旋转图形,原来 OpenGL ES 1.0 是 glRotatef(),现在通过自定义函数 applyRotate():

static void applyRotation(float degrees) {    float radians = degrees * 3.14159f / 180.0f;    float s = sin(radians);    float c = cos(radians);    float zRotation[16] = { //            c, s, 0, 0, //            -s, c, 0, 0,//            0, 0, 1, 0,//            0, 0, 0, 1//            };    GLint modelviewUniform = glGetUniformLocation(simpleProgram, "Modelview");    glUniformMatrix4fv(modelviewUniform, 1, 0, &zRotation[0]);}

跟前面 initialize() 里面的 applyOrtho() 一样,直接用矩阵运算。。。无论如何,applyRotate() 将图形旋转了 -currentDegree,这个和前一篇最终结果是一样的

接下来又不同了。OpenGL ES 1.0 通过 glVertexPointer()、glColorPointer() 函数将顶点坐标、颜色分量数组的地址提交给OpenGL,现在对应的函数是 两个 glXxxAttribPointer(),它们的第1个参数似乎和前面的 Vertex Shader 联系上了。。。大概的流程是一致的。。。先这样吧

updateAnimation()、onRotate()

这两个函数只是更新 desiredDegree 和 currentDegree 这2个状态变量,与 RenderingEngine1.c 中的一模一样

本篇完

本篇用OpenGL ES 2.0 重新实现了 RenderingEngine 接口。对 Shader、GLSL 以及使用 2.0 与 1.0 进行3D渲染之间的区别留下了第一印象,但是,未知和疑问也更多了。路漫漫其修远兮。。。


更多相关文章

  1. C语言函数的递归(上)
  2. android 图形系统加速学习系列 (一)
  3. Android(安卓)Lua 相互调用
  4. android ApiDemos里的Transition3d翻转修复完善
  5. Android使用代码进行界面布局和改变图标、标题、名称、主界面
  6. Android(安卓)在一个程序中启动另一个程序(包名,或者类名)
  7. #Android项目# ——day03 Android(安卓)沉浸式状态栏的解决方案
  8. 仿新浪微博布局学习——妙用TabHost
  9. Android(安卓)IPC 机制详解:IBinder

随机推荐

  1. Android游戏源码合集(主要是AndEngine和Li
  2. 【Android】Android优化-oom
  3. Android 框架学习1:EventBus 3.0 的特点与
  4. Unity3d和Android的互相调用(一)
  5. Android 学习之路
  6. Andoid自动判断输入是电话,网址或者Email
  7. Android打造个性十足的组件技术之复合组
  8. 开源分享二(Android相机开发实战)
  9. android WebView 和js互调用的使用说明。
  10. 关注细节-TWaver Android