热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

linux3d图形编程,Android开发OpenGLES绘制3D图形实例详解

OpenGLES是OpenGL三维图形API的子集,针对手机、PDA和游戏主机等嵌入式设备而设计。Ophone目前支持OpenGLES1.0,OpenG

OpenGL ES是 OpenGL三维图形API 的子集,针对手机、PDA和游戏主机等嵌入式设备而设计。 Ophone目前支持OpenGL ES 1.0 ,OpenGL ES 1.0 是以 OpenGL 1.3 规范为基础的,OpenGL ES 1.1 是以 OpenGL 1.5 规范为基础的。本文主要介绍利用OpenGL ES绘制图形方面的基本步骤。

本文内容由三部分构成。首先通过EGL获得OpenGL ES的编程接口;其次介绍构建3D程序的基本概念;最后是一个应用程序示例。

OpenGL ES 本质上是一个图形渲染管线的状态机,而 EGL 则是用于监控这些状态以及维护帧缓冲和其他渲染面的外部层。图1 是一个典型的 EGL 系统布局图。EGL 视窗设计是基于人们熟悉的用于 Microsoft Windows ( WGL )和 UNIX ( GLX )上的 OpenGL 的 Native 接口,与后者比较接近。 OpenGL ES 图形管线的状态被存储于 EGL 管理的一个上下文中。帧缓冲和其他绘制渲染面通过 EGL API 创建、管理和销毁。 EGL 同时也控制和提供了对设备显示和可能的设备渲染配置的访问。

7098eb5de6d497305651de931d79a5a5.png

图1

OpenGL ES 需要一个渲染上下文和渲染面。渲染上下文中存储OpenGL ES的状态信息,渲染面用于图元的绘制。编写OpenGL ES之前需要EGL的操作有:

查询设备可以支持的显示句柄,并初始化。

创建渲染面,绘制OpenGL ES图形。

创建渲染上下文。EGL需要创建OpenGL ES渲染上下文用于关联到某个渲染面。

Ophone中EGL包括4个类,分别是EGLDisplay:显示句柄、EGLConfig:配置类;EGLContext:渲染上下文;的类和EGLSurface:可渲染的视图类。

EGL可以认为成OpenGL ES和本地窗口系统之间的中间层。 本地窗口系统指GNU/Linux上X窗口系统,或者Mac OX X's Quartz等。在EGL确定渲染面的类型前,EGL需要和底层的窗口系统进行通讯。因为在不同的操作系统上的窗口系统的不同,EGL提供一个透明窗口类型,即EGLDisplay。它抽象了各种窗口系统。所以首先要创建、初始化一个EGLDisplay对象。

// EGLContext的静态方法getEGL获得EGL实例

EGL10 egl = (EGL10)EGLContext.getEGL();

//创建EGLDisplay, EGL_DEFAULT_DISPLAY获得缺省的本地窗口系统类型

EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);

//初始化EGLDispla的同时获得版本号

int[] version = new int[2];

egl.eglInitialize(dpy, version);

每个 EGLDisplay 在使用前都需要初始化。初始化 EGLDisplay 的同时能够得到系统中 EGL 的实现版本号。通过版本号,合理运用相应OpenGL ES API,可以编写兼容性良好的程序,以适应更多的设备以及提供最大限度的移植性。初始化函数原型:

boolean eglInitialize(EGLDisplay display, int[] major_minor)

其中的display是一个有效的 EGLDisplay实例。函数调用完成时, major_minor将被赋予当前 EGL 版本号。比如 EGL1.0 , major_minor[0]为1,major_minor[1]为0。EGLSurface包含了EGL渲染面相关的所有信息。查询 EGLSurface配置信息有两种方法,一是查询所有的配置信息,从中选择一个最为适合的;二是指定好配置信息,由系统给出最佳匹配结果。一般采用第二种方法。用户通过configSpec指定出希望获得的配置,函数eglChooseConfig通过参数Configs返回最佳的配置列表。之后利用已获得的Configs,调用eglCreateContext创建一个渲染上下文,该函数返回EGLContext结构。渲染面EGLSurface的创建通过函数eglCreateWindowSurface完成。一个应用程序可以创建多个EGLContext。 eglMakeCurrent就是将某个渲染上下文绑定到渲染面。查询函数 eglGetCurrentContext, eglGetCurrentDisplay和eglGetCurrentSurface 分别用于获得当前系统的渲染上下文、显示句柄和渲染面。最后EGLContext的静态方法getGL获得OpenGL ES的编程接口。下面的程序片段总结了上述内容。

EGL10 egl = (EGL10)EGLContext.getEGL();

EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); int[] version = new int[2];

egl.eglInitialize(dpy, version);

int[] configSpec = {

EGL10.EGL_RED_SIZE, 5,

EGL10.EGL_GREEN_SIZE, 6,

EGL10.EGL_BLUE_SIZE, 5,

EGL10.EGL_DEPTH_SIZE, 16,

EGL10.EGL_NONE

};

EGLConfig[] configs = new EGLConfig[1];

int[] num_config = new int[1];

egl.eglChooseConfig(dpy, configSpec, configs, 1, num_config);

EGLConfig config = configs[0];

EGLContext context = egl.eglCreateContext(dpy, config,

EGL10.EGL_NO_CONTEXT, null);

EGLSurface surface = egl.eglCreateWindowSurface(dpy, config,

sHolder, null);

egl.eglMakeCurrent(dpy, surface, surface, context);

GL10 gl = (GL10)context.getGL();

构建3D图形的点

点是构建3D模型的基础。 OpenGL ES的内部计算是基于点的。 用点也可以表示光源的位置,物体的位置。一般我们用一组浮点数来表示点。 例如一个正方形的4个顶点可表示为:

float vertices[] = {

-1.0f, 1.0f, 0.0f, //左上

-1.0f, -1.0f, 0.0f, //左下

1.0f, -1.0f, 0.0f, //右下

1.0f, 1.0f, 0.0f, //右上

};

为了提高性能, 需要将浮点数组存入一个字节缓冲中。 所以有了下面的操作:

ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);

vbb.order(ByteOrder.nativeOrder());

FloatBuffer vertexBuffer = vbb.asFloatBuffer();

vertexBuffer.put(vertices);

vertexBuffer.position(0);

其中ByteOrder.nativeOrder()是获取本机字节顺序。OpenGL ES有操作图形渲染管线的函数,在默认情况下这些函数功能的使用状态是处于关闭的。 启用和关闭这些函数可以用glEnableClientState、glDisableClientState来完成。

// 指定需要启用定点数组

gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

// 说明启用数组的类型和字节缓冲,类型为GL_FLOAT

gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);

// 不再需要时,关闭顶点数组

gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);

边是连接两个点的一条线,是多边形面的边缘。

多边形

多边形是由边构成的单闭合环。 OpenGL ES中的多边形必须是凸多边形,即在多边形的内部任意取两点, 如果连接这两个点的线段都在多变的内部,这个多边形就是凸多边形。 绘制多边形时需要指定渲染的方向, 分为顺时针和逆时针。 因为方向决定了多边形的朝向, 即正面和背面。 避免渲染那些被遮挡的部分可以了有效提高程序性能。 函数glFrontFace定义了渲染顶点的方向。

// 设置CCW方向为“正面”,CCW即CounterClockWise,逆时针

glFrontFace(GL_CCW);

// 设置CW方向为“正面”,CW即ClockWise,顺时针

glFrontFace(GL_CW);

渲染

有了以上的概念讲解后,现在要进行最主要的工作—渲染。渲染是把物体坐标所指定的图元转化成帧缓冲区中的图像。图像和顶点坐标有着密切的关系。这个关系通过绘制模式给出。常用到得绘制模式有GL_POINTS、GL_LINE_STRIP、

GL_LINE_LOOP、GL_LINES、 GL_TRIANGLES、GL_TRIANGLE_STRIP、GL_TRIANGLE_FAN。下面分别介绍:

GL_POINTS:把每一个顶点作为一个点进行处理,顶点n即定义了点n,共绘制n个点。

6196b26e4ba9c5ad6fefaccae4ca1d0f.png

GL_LINES:把每一个顶点作为一个独立的线段,顶点2n-1和2n之间共定义了n个线段,总共绘制N/2条线段。,如果N为奇数,则忽略最后一个顶点。

3f107e9b59bcbd98b850143ada047f95.png

GL_LINE_STRIP:绘制从第一个顶点到最后一个顶点依次相连的一组线段,第n和n+1个顶点定义了线段n,总共绘制N-1条线段。

e24b59cb641a700b9fe58800c4d317fb.png

GL_LINE_LOOP:绘制从定义第一个顶点到最后一个顶点依次相连的一组线段,然后最后一个顶点与第一个顶点相连。第n和n+1个顶点定义了线段n,然后最后一个线段是由顶点N和1之间定义,总共绘制N条线段。

9890a995d5b26330663af00f5192b34d.png

GL_TRIANGLES:把每三个顶点作为一个独立的三角形。顶点3n-2,3n-1和3n定义了第n个三角形,总共绘制N/3个三角形。

37384c8729c5fc537c231c868368db4e.png

GL_TRIANGLE_STRIP:绘制一组相连的三角形。对于奇数点n,顶点n,n+1和n+2定义了第n个三角形;对于偶数n,顶点n+1,n和n+2定义了第n个三角形,总共绘制N-2个三角形。

c7498e30b6002220bc78d8c11a8fdc37.png

GL_TRIANGLE_FAN:绘制一组相连的三角形。三角形是由第一个顶点及其后给定的顶点所确定。顶点1,n+1和n+2定义了第n个三角形,总共绘制N-2个三角形。

198e30328a2271980ffeeca660a488c7.png

绘制函数:

void glDrawArrays(int mode, int first, int count)

void glDrawElements(int mode, int count, int type, Buffer indices)

glDrawArrays创建一个几何图元序列,使用每个被的数组中从first开始,到first + count – 1结束的数组元素, mode为绘制模式。

glDrawElements使用count个元素定义一个图元序列,type是indices数组中的数据类型,mode为绘制模式,indices数组存储顶

点的索引值。

应用举例

利用上面讲解的内容给出一个Ophone上绘制一个3D球形的程序。效果图如下:

49d3c8ebc1038e9eb2b5684d7990f366.png

图2 球形示例

主要的绘制程序:

static private FloatBuffer vertex;//顶点对应的字节缓冲

static private FloatBuffer normal;//法向量对应的字节缓冲

float[] lightPos = new float[] {10.0f, 10.0f, 10.0f, 1.0f };//光源的坐标

private static final int STEP = 24;//

private static final float RADIUS = 1.0f;//半径

protected void init(GL10 gl) {

gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);//设置背景颜色

gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightPos, 0);

gl.glEnable(GL10.GL_LIGHTING);//启用光照

gl.glEnable(GL10.GL_LIGHT0); //打开光源

gl.glClearDepthf(1.0f);//设置深度缓存

gl.glDepthFunc(GL10.GL_LEQUAL);//设置深度缓存比较函数,GL_LEQUAL表示新的像素的深度缓存值小于等于当前像素的深度缓存值时通过深度测试

gl.glEnable(GL10.GL_DEPTH_TEST);//启用深度缓存

gl.glEnable(GL10.GL_CULL_FACE);

gl.glShadeModel(GL10.GL_SMOOTH);//设置阴影模式GL_SMOOTH

}

protected void drawFrame(GL10 gl) {

gl.glClear(GL10.GL_COLOR_BUFFER_BIT |

GL10.GL_DEPTH_BUFFER_BIT);

gl.glMatrixMode(GL10.GL_MODELVIEW);

gl.glLoadIdentity();

GLU.gluLookAt(gl, 0, 0, 7f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);//

drawSphere(gl, RADIUS, STEP, STEP); //绘制球形

}

public static void gluLookAt (GL10 gl, float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ)

它共接受三组坐标,分别为eye、 center和up。eye表示我们眼睛在"世界坐标系"中的位置,center表示眼睛"看"的那个点的坐标,up坐标表示观察者本身的方向,如果将观察点比喻成我们的眼睛,那么这个up则表示我们是正立还是倒立异或某一个角度在看,这里是正立方式,所以是{0,1,0}。

private static void drawSphere(GL10 gl, float radius,

int stacks, int slices) {

vertex=allocateFloatBuffer( 4* 6 * stacks * (slices+1) );

normal=allocateFloatBuffer( 4* 6 * stacks * (slices+1) );

int i, j, triangles;

float slicestep, stackstep;

stackstep = ((float)Math.PI) / stacks;

slicestep = 2.0f * ((float)Math.PI) / slices;

for (i = 0; i

{

float a = i * stackstep;

float b = a + stackstep;

float s0 = (float)Math.sin(a);

float s1 = (float)Math.sin(b);

float c0 = (float)Math.cos(a);

float c1 = (float)Math.cos(b);

float nv;

for (j &#61; 0; j <&#61; slices; &#43;&#43;j)

{

float c &#61; j * slicestep;

float x &#61; (float)Math.cos(c);

float y &#61; (float)Math.sin(c);

nv&#61;x * s0;

normal.put(nv);

vertex.put( nv * radius);

nv&#61;y * s0;

normal.put(nv);

vertex.put( nv * radius);

nv&#61;c0;

normal.put(nv);

vertex.put( nv * radius);

nv&#61;x * s1;

normal.put(nv);

vertex.put( nv * radius);

nv&#61;y * s1;

normal.put(nv);

vertex.put( nv * radius);

nv&#61;c1;

normal.put(nv);

vertex.put( nv * radius);

}

}

normal.position(0);

vertex.position(0);

gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertex);

gl.glNormalPointer(GL10.GL_FLOAT, 0, normal);

gl.glEnableClientState (GL10.GL_VERTEX_ARRAY);

gl.glEnableClientState (GL10.GL_NORMAL_ARRAY);

triangles &#61; (slices &#43; 1) * 2;

for(i &#61; 0; i

gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,

i * triangles, triangles);

gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);

gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);

}

private static FloatBuffer allocateFloatBuffer(int capacity){

ByteBuffer vbb &#61; ByteBuffer.allocateDirect(capacity);

vbb.order(ByteOrder.nativeOrder());

return vbb.asFloatBuffer();

}

总结&#xff1a;

本文介绍了Ophone中利用OpenGL ES绘制图形的基本概念和方法。OpenGL ES中还有很多其他内容&#xff0c;诸如纹理、光照和材质、混合、雾、蒙板、反射、3D模型的加载等。利用OpenGL ES函数可以绘制丰富的图形应用和游戏界面。



推荐阅读
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • Android JSON基础,音视频开发进阶指南目录
    Array里面的对象数据是有序的,json字符串最外层是方括号的,方括号:[]解析jsonArray代码try{json字符串最外层是 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • Imtryingtofigureoutawaytogeneratetorrentfilesfromabucket,usingtheAWSSDKforGo.我正 ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • 如何提高PHP编程技能及推荐高级教程
    本文介绍了如何提高PHP编程技能的方法,推荐了一些高级教程。学习任何一种编程语言都需要长期的坚持和不懈的努力,本文提醒读者要有足够的耐心和时间投入。通过实践操作学习,可以更好地理解和掌握PHP语言的特异性,特别是单引号和双引号的用法。同时,本文也指出了只走马观花看整体而不深入学习的学习方式无法真正掌握这门语言,建议读者要从整体来考虑局部,培养大局观。最后,本文提醒读者完成一个像模像样的网站需要付出更多的努力和实践。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
author-avatar
可惜偏偏孤独一个小姐_448
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有