Android OpenGL 基础入门
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 基础入门的更多相关文章
- # 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封装 ...
- 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封装 ...
- 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封装 ...
- 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封装 ...
- 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封装 ...
- 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封装 ...
- 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封装 ...
- 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 02 封装的代码实现
088 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 02 封装的代码实现 本文知识点:Java封装的代码实现 说明:因为时间紧张,本人写博客过程中只 ...
- 087 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 01 封装的概念和特点
087 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 01 封装的概念和特点 本文知识点:封装的概念和特点 说明:因为时间紧张,本人写博客过程中只是对 ...
随机推荐
- 5分钟破解wpa2密码(转)
首先大家要明白一种数学运算,它叫做哈希算法(hash),这是一种不可逆运算,你不能通过运算结果来求解出原来的未知数是多少,有时我们还需要不同的未知数通过该算法计算后得到的结果不能相同,即你不太可能找到 ...
- MS SQL SERVER索引优化相关查询
查找缺失索引 -- ============================================= -- Description: 查询当前数据库中缺失的索引,知道你进行优化的 ...
- ORA-01033:ORACLE initialization or shutdown in progress
借用他人的经验 客户Oracle服务器进入PL/SQL Developer时报ora-01033:oracle initializationg or shutdown in progress 错误提示 ...
- rocketmq总结
1:角色关系 2:顺序消息 消费消息的顺序要同収送消息的顺序一致,在 RocketMQ 中,主要挃的是尿部顺序,即一类消息为满足顺序性,必须 Producer 单线程顺序収送,丏収送到同一个队列,返样 ...
- Apache Error: Invalid command ‘Allow’, perhaps misspelled or defined by a module not included in the server configuration
在一个Window Server 2008R2系统上使用Apache架设了一个PHP的网站项目 在配置Apache的过程中出现了以下问题 根据上面的提示说是没有相应的权限,那就在虚拟主机里进行了配 ...
- [转] Visual Studio Code behind a proxy
http://www.tuicool.com/articles/jyyIBf3 http://blog.majcica.com/2016/04/07/visual-studio-code-behind ...
- memcached命令
memcached相对于redis来说,简直简单太多,命令也少很多,一般应用都是使用redis,但了解一下也还是不错的. 具体命令和用法很参见:http://www.runoob.com/memcac ...
- Android上面安装Linux的方法
方法一: 并行安装Linux(不在Android操作系统之上运行,需要设备已经unlocked并且rooted) 我还没玩过.放两个书签: How to Install Ubuntu on Andro ...
- 1. 初探MongoDB —— 介绍、安装和配置
一.写在前面 之前一直以来,都是使用关系型数据库.也很早就听闻,当数据量大的时候,关系型数据库使用起来会有很多问题.诸如查询数据慢,加载花费时间长等等. 也早就耳闻NOSQL(NoSQL = Not ...
- iOS CoreData技术学习资源汇总
一.CoreData学习指引 1. 苹果官方:Core Data Programming Guide 什么是CoreData? 创建托管对象模型 初始化Core Data堆栈 提取对象 创建和修改自定 ...