Android 自从2.2 版本之后之后开始支持OpenGL,在没有支持OpenGL 的 GPU的情况下,也可以使用(通过软件来模拟)。在Android上使用Opengl操作的对象是GLSurfaceView,这是一个继承自View的扩展。

  在Android上Opengl是通过Vertex Shader 和 Fragment Shader 这两种定点着色器程序来实现图片的加载和渲染的,中文称为定点着色器和片段着色器。一个完整的Opengl程序需要创建定点着色器和片段着色器并将他们Link起来组成一个完整的OpenGL程序。

  顶点着色器的作用是为每一个顶点生成坐标,因此每个顶点都要运行一遍顶点着色器程序,一旦顶点坐标计算出来之后,OpenGL就能够使用这些顶点来组成点,线,和三角形。所有任意的图形都是由这三种基本元素来描述的。下图是顶点着色器进行坐标转换的过程(稍微有点复杂):

  

  这个过程包含了从原始的对象坐标经过模型视图转换生成眼坐标,再经过投影转换生成裁剪坐标,再通过w坐标的归一化转换成为NDC(顶点坐标由(x,y,z,w)构成,在shader程序中一般用一个四维向量vec4来描述),最终通过视口变换生成屏幕坐标显示在屏幕上。这一系列的转换都是通过矩阵来完成的,转换过程和原理是Opengl的精华内容,对于需要进入3D世界的同学需要掌握。在2D世界中我们只需要了解NDC就行了,这里就把NDC叫做OpenGL 坐标,加上Normalized 的修饰是因为这些坐标的值都在(-1,1)之间,OpenGL坐标表示见下图:

  OpenGL坐标原点在屏幕中央,左右坐标范围为[-1,1],在2d环境中坐标值只存在(x,y),并且坐标范围都只能在-1 到 1之间,屏幕中心坐标为(0,0)。因此如果需要指定一张图片的显示位置,指定坐标需要根据这个坐标系来,此外为了保证图片显示比例(长宽比例),在portrait 到 landscape 之间变换的时候一般需要乘以一个aspectRatio (width / height) 来重新设定坐标值。

  了解了Opengl坐标,在来了解一下屏幕坐标(屏幕坐标的坐标原点在左上角)和纹理坐标(纹理坐标的坐标原点在左下角):

屏幕坐标系                

  纹理坐标系

  片段着色器的作用是为点,线或者三角形的每一个顶点的片段(Fragment)生成渲染后的最终颜色。片段就是一个小的单色矩形区域,可以简单的认为是屏幕上的一个像素点。

  以上基本知识基本上可以处理Opengl实现2D图像的绘制和处理。下面来简单看一下Shader的写法,在Android平台上Shader程序一般以字符串的形式出现,或者在res/raw/目录下以*.glsl的格式出现,如果是以单独的文件出现,需要定义专门的文件读入接口来加载Shader程序。这里就介绍一下字符串的形式的Shader程序,来看下面简单的Vertex Shader 和 Fragment Shader:

    private static final String VERTEX_SHADER =
"attribute vec4 a_position;\n" +
"attribute vec2 a_texcoord;\n" +
"varying vec2 v_texcoord;\n" +
"void main() {\n" +
" gl_Position = a_position;\n" +
" v_texcoord = a_texcoord;\n" +
"}\n"; private static final String FRAGMENT_SHADER =
"precision mediump float;\n" +
"uniform sampler2D tex_sampler;\n" +
"varying vec2 v_texcoord;\n" +
"void main() {\n" +
" gl_FragColor = texture2D(tex_sampler, v_texcoord);\n" +
"}\n";

  先看一下VERTEX_SHADER,定义了两种类型的变量 atrribute 和 varying。

  其中 attribute变量是只能在vertex shader中使用的变量。(它不能在fragment shader中声明attribute变量,也不能被fragment shader中使用),一般用attribute变量来表示一些顶点的数据,这些顶点数据包含了顶点坐标,法线,纹理坐标,顶点颜色等。应用中一般用函数glBindAttribLocation()来绑定每个attribute变量的位置,然后用函数glVertexAttribPointer()为每个attribute变量赋值。

  varying被称为易变量,一般用于从Vertex Shader 向 Fragment Shader传递数据,上面例子中在VertexShader中定义了attribute 类型的二维向量a_texcoord,并将该值赋值给varying类型的二维向量 v_texcoord。此外对于Vertex Shader 在main() 中必须将顶点坐标赋值给系统变量gl_Position。

  看一下FRAGMENT_SHADER,定义了两种类型的变量,uniform 和 varying。此外还多了一句 precision mediump float,这句话用于定义数据精度,Opengl中可以设置三种类型的精度(lowp,medium 和 highp),对于Vertex Shader来说,Opengl使用的是默认最高精度级别(highp),因此没有定义。

  uniform变量是APP序传递给(vertex和fragment)shader的变量。通过函数glUniform**()函数赋值的。 在(vertex和fragment)shader程序内部,uniform变量就像是C语言里面的常量(const ),它不能被shader程序修改(shader只能用,不能改)。如果uniform变量在vertex和fragment两者之间声明方式完全一样,则它可以在vertex和fragment共享使用。 (相当于一个被vertex和fragment shader共享的全局变量)uniform变量一般用来表示:变换矩阵,材质,光照参数和颜色等信息。

  对于Fragment Shader也需对gl_FragColor赋值。

  现在对基本的Shader有了了解,来看一下Android怎么使用Shader程序。先说明一下Opengl中的资源一般都是用一个句柄(handle)来引用,句柄一般由gl***接口返回,代表一个特定的资源。

  在Android上使用gl,需要用到一些列接口,这里按照一般的调用顺序来列一下基本的接口(暂不包含错误处理)

  1. 和创建Shader相关的:glCreateShader -> glShaderSource -> glCompileShader , 通过这几个接口的调用最终返回一个Shader的句柄

  2. 和创建Shader程序相关的(每个shader程序都必须包含Vertex Shader 和 Fragment Shader两部分):glCreateProgram -> glAttachShader -> glLinkProgram,通过这些接口的调用最终返回个Program的句柄。

  3. 获取Shader内部变量赋值和传递数据到gl,对于不同类型的数据使用不同的接口:GLES20.glGetUniformLocation(mProgram, "tex_sampler"),GLES20.glGetAttribLocation(mProgram, "a_texcoord"),以上两个接口中mProgram 代表glCreateProgram返回的 Shader程序句柄,“a_texcoord”和"tex_sampler"是 Vertex Shader和 Fragment Shader中定义的变量,通过这两个接口获取了变量的句柄,便于向这些变量中传入值。获得变量的句柄之后就要向其中传值,一般通过glVertexAttribPointer()来完成,详细参数这里没有列出,在实例代码中可以学习一下,传值之后需要使glEnableVertexAttribArray才能将数据传入到gl中,传入数据使用glDrawArrays() 。

  这里介绍了Opengl的基本知识,下一篇结合实例介绍一下Opengl在Android上的使用。

Android OpenGL 基础入门的更多相关文章

  1. # 095 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 03 封装总结 01 封装知识点总结

    095 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 ...

  2. 094 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 02 static关键字 04 static关键字(续)

    094 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 ...

  3. 093 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 02 static关键字 03 static关键字(下)

    093 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 ...

  4. 092 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 02 static关键字 02 static关键字(中)

    092 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 ...

  5. 091 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 02 static关键字 01 static关键字(上)

    091 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 ...

  6. 090 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 04 使用包进行类管理(2)——导入包

    090 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 ...

  7. 089 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 使用包进行类管理(1)——创建包

    089 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 ...

  8. 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 02 封装的代码实现

    088 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 02 封装的代码实现 本文知识点:Java封装的代码实现 说明:因为时间紧张,本人写博客过程中只 ...

  9. 087 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 01 封装的概念和特点

    087 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 01 封装的概念和特点 本文知识点:封装的概念和特点 说明:因为时间紧张,本人写博客过程中只是对 ...

随机推荐

  1. Postman测试Web API

    如何查看查询字符串? →输入包含查询字符串的URL,比如:http://localhost:54176/api/ProductCategories?name=darren&age=25→点击P ...

  2. 简单谈谈Resource,Drawable和Bitmap之间的转换

    一直接触这些东西,还是归个类整理一下比较好. Resource -> Drawable Drawable draw1 = this.getResources().getDrawable(R.dr ...

  3. 集群: 如何在spring 任务中 获得集群中的一个web 容器的端口号?

    系统是两台机器, 跑四个 web 容器, 每台机器两个容器 . nginx+memcached+quartz集群,web容器为 tomcat . web 应用中 用到spring 跑多个任务,任务只能 ...

  4. mysql慢日志设置

    mysql的慢日志查询对于sql的优化还是很有意义的,具体说下如何开启这个mysql慢查询日志(默认是开启的). 关于设置在mysql的官方手册或网上都有很多,但是要注意的是,mysql5.6与之前的 ...

  5. Spectrum to XYZ to sRGB

    如何将频谱响应转换为对应的RGB显示值: 首先要在频率功率分布(SPD)曲线的基础上,分别使用X/Y/Z三个频率匹配曲线(spectral matching curves,又名CIE XYZ Colo ...

  6. Scala 深入浅出实战经典 第46讲: ClassTag 、Manifest、ClasMainifest TagType实战

    王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-64讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...

  7. python 中偏函数 partial 的使用

    函数的partial应用 函数在执行时,要带上所有必要的参数进行调用.但是,有时参数可以在函数被调用之前提前获知.这种情况下,一个函数有一个或多个参数预先就能用上,以便函数能用更少的参数进行调用. 例 ...

  8. Lumia 830 win10m 启用触摸按键

    用了一年半的830疑似翘屏… 按键基本失灵 每次按的时候基本都是 appbar 那里有反映… 实在懒于换手机(主要是不想花钱…) 研究下怎么启用虚拟按键,还能再战几年… 启用虚拟按键 其实简单说法就是 ...

  9. ecshop退出登录会清空购物车的bug优化,最完美解决方法

    ecshop退出登陆后,会清空购物车,大家都应该知道有这样的勉强算bug的问题. 网上类似的教程相当多,但都有问题,说句不好听的,算是引新手入歧途! 总结网上方法如下: 1.修改init.php,把s ...

  10. 彻底解决Android SDK Manager更新慢的问题

    Android SDK 下载速度慢,解决方法大概有两种.第一,FQ.这种方法比较彻底,但是要想有稳定的效果还的要花大价钱.第二,有些高人直接给了SDK中各软件的下载地址,直接下载速度非常快,下载后将包 ...