Android OpenGL ES(九)----构建几何物体

简介: Android OpenGL ES(九)----构建几何物体

1.三角形扇


一个三角形扇以一个中心顶点作为起始,使用相邻的两个顶点创建第一个三角形,接下来的每个顶点都会创建一个三角形,围绕起始的中心点按扇形展开。为了使这个扇形闭合,我们只需要在最后重复第二个点。(以长方形为例)


构建三角形扇的步骤,如下图所示:


42.png


要使用OpenGL绘制这个三角形扇,需要在渲染类的onDrawFrame()中,使用如下方法:


GLES20.glDrawArray(GLES20.GL_TRIANGLE_FAN,0,6);


第一个参数是告诉OpenGL要绘制一个三角形扇,第二参数是告诉OpenGL从本地顶点数据的第几个位置开始取顶点坐标,第三个参数是告诉OpenGL要取多少个顶点坐标。


根据上面的6个点,就可以绘制一个长方形了。


三角形扇在OpenGL的应用:长方形,正方形,圆等。


2.三角形带


一个三角形带的前三个顶点定义了第一个三角形。这之后的每个额外的顶点都定义了另外的一个三角形。


构建三角形带的步骤,如下图所示:

43.png

要使用OpenGL绘制这个三角形带,需要在渲染类的onDrawFrame()中,使用如下方法:


GLES20.glDrawArray(GLES20.GL_TRIANGLE_STRIP,0,6);


第一个参数是告诉OpenGL要绘制一个三角形带,第二参数是告诉OpenGL从本地顶点数据的第几个位置开始取顶点坐标,第三个参数是告诉OpenGL要取多少个顶点坐标。


三角形带在OpenGL的应用:长方形,圆柱的侧面等。


3.圆柱体


想象一下在自己的手机上构建的圆柱体,并且以一个角度观察它,假如我们把圆柱放在桌面上,从侧面观察它,你会发现,圆柱体是一个由一个顶部的圆加上侧面卷起来的长方形构成。结合本文前2节的讲解,就可以知道其实就是一个三角形带和三角形扇构建一个圆柱体。


要构建三角形扇,我们首先定义一个圆心顶点,接着,我们围绕圆心的点按扇形展开,并把第一个点绕圆周重复两次使其圆闭合。我们接下来使用三角函数和单位圆的概念生成那个点。


44.png


为了生成沿一个圆周边的点,我们首先需要一个循环,它的覆盖范围从0到360度的整个圆,或者0到2π弧度。要找到圆周上的一个点的X的位置,我们需要调用cos(angle),如果你是放在Z-X平面那么,Z的位置我们就需要调用sin(angle);我们用圆的半径缩放这两个位置。这是圆柱上的圆的绘画过程。


如果是圆柱的侧面,我们就需要看图了解一下,我们假设圆柱垂直方向以Y为中心,圆柱高height,得到如下图:


45.png


我们该怎么用程序绘画出来这个圆柱体呢?其实在OpenGL如果想绘制的图像越清晰,那么它绘制的点就会越多越密集,所以由我们自己决定绘制这个圆柱体需要多少个顶点。


我们要计算圆柱体顶部顶点数量的方法作为开始,我们定义一个求圆柱体上面圆的顶点数的方法,如下:

private static int sizeOfCricleInVertices(int numPoint){
return 1+(numPoint+1);
}


一个圆柱体的顶部是一个用三角形扇构造的圆;它有一个顶点在圆心,围着圆的每个顶点点都有一个顶点,并且围着圆的顶点要重复两次,才能使圆闭合。


下面是计算圆柱体侧面顶点的数量:


private static int sizeOfOpenCylinderInVertices(int numPoint){
return (numPoints+1)*2;
}


一个圆柱体侧面是一个卷起来的长方形,由一个三角形带构造,围着顶部圆的每个点都需要两个顶点,并且前两个顶点要重复两次才能使这个管闭合。看三角形带,我们直指定了上面的点的数量,自然下面的点也要计算进去,所以都需要两个顶点。


添加几何图形的类


我们要构建几何物体,其实可以分解成几个类,这样便于管理和重用。创建一个新的类,为Geometry,在该类的内部我们定义一个坐标类,也就是点类:


public static class Point(){
public final float x,y,z;
public Point(float x,float y,float z){
this.x=x;
this.y=y;
this.z=z;
}
public Point translateY(float distance){
return new Point(x,y+distance,z);
}
}


其中有一个辅助函数用于把这个点沿着Y轴平移。我们也需要给,下面我们也给圆一个定义,如下,也为Geometry的内部类:


public static class Circle{
private final Point center;
private final float radius;
public Circle(Point center,float radius){
this.center=center;
this.radius=radius;
}
public Circle scale(float scale){
return new Circle(center,radius*scale);
}
}


我们同样在圆的类里面定义了一个辅助函数,用于缩放圆的半径,最后是给圆柱一个定义,如下:


public static class Cylinder{
public final Point center;
public final float radius;
public finla float height;
public Cylinder(Point center,float radius,float height){
this.center=center;
this.radius=radius;
this.height=height;
}
}


一个圆柱体就像一个扩展的圆,它有一个中心,一个半径和一个高度。


你可能注意到了我们已经把这几个几何物体定义的类定义为不可变的;无论什么时候改动它,都会返回一个新的对象。这有助于使代码更容易使用和理解。但是当你需要提高性能时,你也许想一直用简单的浮点数组,并用静态函数改变它们。


添加物体构建器


我们现在可以开始写物体构建器了,在你的objects包中创建一个名为“ObjectBuilder”的类,在类的内部,以下面的代码作为开始:


private static final int FLOATS_PER_VERTEX=3;
private final float[] vertexData;
private int offset=0;
private ObjectBuilder(int sizeInVertices){
this.vertexData=new float[sizeInVertices*FLOATS_PER_VERTEX];
}


我们定义了一个常量用来保存椒一个顶点需要多少浮点数,一个数组用于保存这些顶点,以及一个变量用于记录数组中下一个顶点的位置。这个构造函数基于需要的顶点数量初始化数组。


用三角形扇构建圆


在ObjectBuilder类中创建一个名为appendCircle的新方法,并加入如下代码:


private void appendCircle(Circle circle,int numPoint){
final int startVertex=offset/FLOATS_PER_VERTEX;
final int numVertices=sizeOfCircleInVertices(numPoint);
this.vertexData[offset++]=circle.center.x;
this.vertexData[offset++]=circle.center.y;
this.vertexData{offset++}=circle.center.z;
for(i=0;i<numPoint;i++){
float angleInRadians=(float)i/(float numPoints)*((float)Math.PI*2f);
this.vertexData[offset++]=circle.center.x+circle.radius*FloatMath.cos(angleInRadians);
this.vertexData[offset++]=circle.center.y;
this.vertexData[offset++]=circle.center.z+circle.radius*FloatMath.sin(angleInRadians);
}
}


我们知道我们想要使用本地存储的顶点,必须设置偏移量,也就是多少个顶点才是正确的坐标,比如我们现在在绘画一个圆柱体,把圆的顶点和侧面的顶点都存储在本地,如果我们开始绘画圆,那么自然偏移是0,但是圆的顶点都绘完后,我们总不能还是从开始的顶点开始取值把,所以跳过取顶点,跳过的就是偏移值startVertex。而numVertices就是要取顶点的长度。


接着我们在ObjectBuilder类的开始处定义一个绘画的接口,顺便也加入一个变量,如下:

static interface DrawCommand{
void draw();
}
private final List<DrawCommand> drawList=new ArrayList<DrawCommand>();


这个常量用于保存绘画命令,如下:

this.drawList.add(new Command(){
public void draw(){
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN,startVertex,numVertices);
}
});


用三角形带构造圆柱体的侧面,为了代码的重用,我们额外定义了一个绘制侧面的方法appendOpenCylinder(),它也需要偏移量和长度,而且,看第二节的图片,你发现没有,上面一排和下面一排的顶点Y值是一样的,所以首先我们在appendOpenCylinder()加入下面四个常量:


private void appendOpenCylinder(Cylinder cylinder,int numPoints){
final int startVertex=offset/FLOATS_PER_VERTEX;
final int numVertices=sizeOfOpenCylinderInVertices(numPoints);
final float yStart=cylinder.center.y-(cylinder.height/2f);
final float yEnd=cylinder.center.y+(cylinder.height/2f);
}


然后加入如下代码生成实际的三角形带:


for(int i=0;i<=numPoints;i++){
float angleInRadians=(float)i/(float numPoints)*((float)Math.PI*2F);
float xPosition=cylinder.center.x+cylinder.radius*FloatMath.cos(angleInRadians);
float zPosition=cylinder.center.z+cylinder.radius*FloatMath.sin(angleInRadians);
this.vertexData[offset++]=xPosition;
this.vertexData[offset++]=yStart;
this.vertexData[offset++]=zPosition;
this.vertexData[offset++]=xPosition;
this.vertexData[offset++]=yEnd;
this.vertexData[offset++]=zPosition;
}


最后在该方法中加入如下代码完成:

this.drawList.add(new Command(){
public void draw(){
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP,startVertex,numVertices);
}
});


我们在ObjectBuilder类中实现了构造圆和圆柱侧面的方法。是不是还少了什么,没错,你获得的顶点和数据怎么传递给其他的类呢?所以我们在ObjectBuilder里面定义了一个包装类,将存储的绘制命令和顶点数据都传递给它,如下:


static class GenerateData{
final float[] vertexData;
final List<Command> drawList;
GenerateData(float[] vertexData,List<DrawCommand> drawList){
this.drawList=drawList;
this.vertexData=vertexData;
}
}


最后就是将圆和侧面叠加成一个圆柱体,我们ObjectBuilder加入如下的代码:


static GeneratedData createCylindrical(Cylinder cylinder,int numPoints){
int size=sizeOfCricleInVertices()+sizeOfOpenCylinderInVertices(numPoints);//计算总的顶点数
ObjectBuilder builder=new ObjectBuilder(size);//根据顶点数实例化vertexData;
Circle circle=new Circle(cylinder.center.translateY(cylinder.height/2f),cylinder.radius);
builder.appendCircle(circle,numPoints);
builder.appendOpenCylinder(cylinder,numPoints);
return builder.build();
}
private GenerateData build(){
return new GenerateData(this.vertexData,this.drawList);
}


这样一个圆柱体绘制类就完成了。


源代码如下:http://download.csdn.net/detail/liyuanjinglyj/8859411


附上效果图:

46.png


相关文章
|
15天前
|
移动开发 Java Android开发
构建高效Android应用:探究Kotlin与Java的性能差异
【4月更文挑战第3天】在移动开发领域,性能优化一直是开发者关注的焦点。随着Kotlin的兴起,其在Android开发中的地位逐渐上升,但关于其与Java在性能方面的对比,尚无明确共识。本文通过深入分析并结合实际测试数据,探讨了Kotlin与Java在Android平台上的性能表现,揭示了在不同场景下两者的差异及其对应用性能的潜在影响,为开发者在选择编程语言时提供参考依据。
|
17天前
|
数据库 Android开发 开发者
构建高效Android应用:Kotlin协程的实践指南
【4月更文挑战第2天】随着移动应用开发的不断进步,开发者们寻求更流畅、高效的用户体验。在Android平台上,Kotlin语言凭借其简洁性和功能性赢得了开发社区的广泛支持。特别是Kotlin协程,作为一种轻量级的并发处理方案,使得异步编程变得更加简单和直观。本文将深入探讨Kotlin协程的核心概念、使用场景以及如何将其应用于Android开发中,以提高应用性能和响应能力。通过实际案例分析,我们将展示协程如何简化复杂任务,优化资源管理,并为最终用户提供更加流畅的体验。
|
20天前
|
缓存 监控 Java
构建高效Android应用:从优化用户体验到提升性能
在竞争激烈的移动应用市场中,为用户提供流畅和高效的体验是至关重要的。本文深入探讨了如何通过多种技术手段来优化Android应用的性能,包括UI响应性、内存管理和多线程处理。同时,我们还将讨论如何利用最新的Android框架和工具来诊断和解决性能瓶颈。通过实例分析和最佳实践,读者将能够理解并实施必要的优化策略,以确保他们的应用在保持响应迅速的同时,还能够有效地利用系统资源。
|
25天前
|
编解码 算法 Java
构建高效的Android应用:内存优化策略详解
随着智能手机在日常生活和工作中的普及,用户对移动应用的性能要求越来越高。特别是对于Android开发者来说,理解并实践内存优化是提升应用程序性能的关键步骤。本文将深入探讨针对Android平台的内存管理机制,并提供一系列实用的内存优化技巧,以帮助开发者减少内存消耗,避免常见的内存泄漏问题,并确保应用的流畅运行。
|
25天前
|
调度 数据库 Android开发
构建高效Android应用:Kotlin协程的实践与优化
在Android开发领域,Kotlin以其简洁的语法和平台友好性成为了开发的首选语言。其中,Kotlin协程作为处理异步任务的强大工具,它通过提供轻量级的线程管理机制,使得开发者能够在不阻塞主线程的情况下执行后台任务,从而提升应用性能和用户体验。本文将深入探讨Kotlin协程的核心概念,并通过实例演示如何在实际的Android应用中有效地使用协程进行网络请求、数据库操作以及UI的流畅更新。同时,我们还将讨论协程的调试技巧和常见问题的解决方法,以帮助开发者避免常见的陷阱,构建更加健壮和高效的Android应用。
33 4
|
28天前
|
移动开发 Java Android开发
构建高效Android应用:Kotlin协程的实践之路
【2月更文挑战第31天】 在移动开发领域,性能优化和流畅的用户体验一直是开发者追求的目标。随着Kotlin语言的流行,其异步编程解决方案——协程(Coroutines),为Android应用带来了革命性的并发处理能力。本文将深入探讨Kotlin协程的核心概念、设计原理以及在Android应用中的实际应用案例,旨在帮助开发者掌握这一强大的工具,从而提升应用的性能和响应能力。
|
28天前
|
安全 Android开发 开发者
构建高效Android应用:Kotlin与协程的完美结合
【2月更文挑战第30天】在移动开发领域,性能优化和流畅的用户体验是关键。本文深入探讨了如何通过结合Kotlin语言和协程技术来提升Android应用的性能和响应能力。我们将分析Kotlin的优势,介绍协程的基本概念,并通过实际案例展示如何在应用中实现协程以简化异步编程,从而提供更加高效的解决方案。
|
28天前
|
Java 编译器 Android开发
构建高效Android应用:探究Kotlin与Java的性能差异
【2月更文挑战第30天】 随着Kotlin成为开发Android应用的首选语言,开发者社区对于其性能表现持续关注。本文通过深入分析与基准测试,探讨Kotlin与Java在Android平台上的性能差异,揭示两种语言在编译效率、运行时性能和内存消耗方面的具体表现,并提供优化建议。我们的目标是为Android开发者提供科学依据,帮助他们在项目实践中做出明智的编程语言选择。
|
17天前
|
Java Android开发 开发者
构建高效Android应用:Kotlin协程的实践与优化
在响应式编程范式日益盛行的今天,Kotlin协程作为一种轻量级的线程管理解决方案,为Android开发带来了性能和效率的双重提升。本文旨在探讨Kotlin协程的核心概念、实践方法及其在Android应用中的优化策略,帮助开发者构建更加流畅和高效的应用程序。通过深入分析协程的原理与应用场景,结合实际案例,本文将指导读者如何优雅地解决异步任务处理,避免阻塞UI线程,从而优化用户体验。
|
22天前
|
Java 编译器 Android开发
构建高效Android应用:探究Kotlin与Java的性能差异
在开发高性能的Android应用时,选择合适的编程语言至关重要。近年来,Kotlin因其简洁性和功能性受到开发者的青睐,但其性能是否与传统的Java相比有所不足?本文通过对比分析Kotlin与Java在Android平台上的运行效率,揭示二者在编译速度、运行时性能及资源消耗方面的具体差异,并探讨在实际项目中如何做出最佳选择。
17 4