Android OpenGL ES(八)----纹理编程框架(二)

简介: Android OpenGL ES(八)----纹理编程框架(二)

4.为着色器程序添加类


我们会为纹理着色器创建一个类,并颜色器程序创建另一个类:我们会用纹理着色器绘制桌子,用颜色着色器绘制木槌。我们也会创建一个基类作为它们的公共函数。我们不用再担心那条直线,因为它是纹理的一部分。

40.png

我们开始给ShaderHelper加入一个辅助函数,打开博文第三篇的类,在其尾部加入如下方法:

public static int buildProgram(String vertexShaderSource,String fragmentShaderSource){
int program;
int vertexShader=compileVertexShader(vertexShaderSource);
int fragmentShader=compileFragmentShader(fragmentShaderSource);
program=linkProgram(vertexShader,fragmentShader);
validateProgram(program);
return program;
}


这个辅助函数会编译vertexShaderSource和fragmentShaderSource定义的着色器,并把它们链接在一起成为一个程序。我们会使用这个辅助函数组成我们的基类。


创建一个名为programs的包,并在包中创建一个名为ShaderProgram的新类,加入如下代码:

protected static final String U_MATRIX="u_Matrix";
protected static final StringU_TEXTURE_UNIT="u_TextureUnit";
protected static final StringA_POSITION="a_Position";
protected static final StringA_COLOR="a_Color";
protected static final StringA_TEXTURE_COORDINATES="a_TextureCoordinates";
protected final int program;
protected ShaderProgram(Context context,int vertexShaderResourceId,int fragmentShaderReourceId){
this.program=ShaderHelper.buildProgram(
TextResourceReader.readTextFileFromResource(context,vertexShaderResourceId),
TextResourceReader.readTextFileFromResource(context,fragmentShaderReourceId));
}
public void useProgram(){
GLES20.glUseProgram();
}


我们通过定义一些公用的常量作为这个类的开始,在构造函数中,我们调用刚刚定义过的辅助函数,其使用是指定的着色器构建了一个OpenGL着色器程序。我们用useProgram()作为结束,其调用glUseProgram()告诉OpenGL接下来的渲染要使用这个程序。


加入纹理着色器程序


我们现在将定义一个类来建立和表示纹理着色器程序。


创建一个名为TextureShaderProgram的新类,其继承自ShaderProgram,并在该类内部加入如下代码:

private final int uMatrixLocation;
private final int uTextureUnitLocation;
private final int aPositionLocation;
private final int aTextureCoordinatesLocation;

我们加入了四个整型用来保存那些uniform和属性的位置。


下一步是初始化着色器程序,创建用于初始化着色器程序的构造函数,代码如下:

public TextureShaderProgram(Context context){
super(context,R.raw.texture_vertex_shader,R.raw.texture_fragment_shader);
this.uMatrixLocation=GLES20.glGetUniformLocation(program,U_MATRIX);
this.uTextureUnitLocation=GLES20.glGetUniformLocation(program,U_TEXTURE_UNIT);
this.aPositionLocation=GLES20.glGetAttribLocation(program,A_POSITION);
this.aTextureCoordinatesLocation=GLES20.glGetAttribLocation(program,A_TEXTURE_COORDINATES);
}


这个构造函数会用我们选择的资源调用其父类的构造函数,其父类会构造着色器程序。我们读入并保存那些uniform和属性的位置。


设置uniform并返回属性的位置


传递矩阵和纹理给它们的uniform。加入如下代码:

public void setUniforms(float[] matrix,int textureId){
GLES20.glUniformMatrix4fv(this.uMatrixLocation,1,false,matrix,0);
GLES20.glActiveTexture(GLES20.GL_TEXTURE_2D);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,textureId);
GLES20.glUniformli(this.uTextureUnitLocation,0);
}


第一步是传递矩阵给它的uniform,这足够简单明了。下一部分就是需要更多的解释了。当我们在OpenGL里使用纹理进行绘制时,我们不需要直接给着色器传递纹理。相反,我们使用纹理单元保存那个纹理。之所以这样做,是因为一个GPU只能同时绘制数量有限的纹理。它使用这些纹理表示当前正在被绘制的活动的纹理。


如果需要切换纹理,我们可以在纹理单元中来回切换纹理,但是,如果我们切换得太频繁,可能会渲染的速度。也可以同时用几个纹理单元绘制多个纹理。


通过调用glActiveTexture()把活动的纹理单元设置成为纹理单元0,我们以此开始,然后通过调用glBindTexture()把这个纹理绑定到这个单元,接着,通过调用glUniformli()把被选定的纹理单元传递给片段着色器中的u_TextureUnit。


我们几乎已经完成了这个纹理器类;只需要一种方法来获取属性的位置,以便可以把它们绑定到正确的顶点数组数据矩阵。加入如下代码完成这个类:

public int getPositionLocation(){
return this.aPositionLocation;
}
public int getTextureLocation(){
return this.aTextureCoordinatesLocation;
}


加入颜色着色器程序


在同一个包中创建另一个类,命名为ColorShaderProgram。这个类应该也继承自ShaderProgram,它也遵循与TextureShaderProgram一样的模式:有一个构造函数,一个设置uniform的方法和获取属性位置的方法。在此类内部加入如下代码:

private final int uMatrixLocation;
private final int aPositionLocation;
private final int aColorLocation;
public ColorShaderProgram(Context context){
super(context,R.raw.simple_vertex_shader,R.raw.simple_fragment_shader);
this.uMatrixLocation=GLES20.glGetUniformLocation(program,U_MATRIX);
this.aPositionLocation=GLES20.glGetAttribLocation(program,A_POSITION);
this.aColorLocation=GLES20.glGetAttribLocation(program,A_COLOR);
}
public void setUniform(float[] matrix){
GLES20.glUniformMatrix4fv(this.uMatrixLocation,1,false,matrix,0);
}
public int getPositionLocation(){
return this.aPositionLocation;
}
public int getColorLocation(){
return this.aColorLocation;
}


我们会使用这个项目绘制木槌。


通过把这些着色器程序与这些程序要绘制的数据进行解耦,就很容易重用这些代码了。比如,我们可以通过这个颜色着色器程序用一种颜色属性绘制任何物体,而不仅仅是木槌。


5.绘制纹理


既然我们已经把顶点数据和着色器程序分别放于不同的类中了,现在就可以更新渲染类,使用纹理进行绘制了。打开LYJRenderer,删掉所有第三篇该类下面的代码,只保留onSurfaceChanged(),这是我们唯一不会改变的。加入如下成员变量和构造函数:


private final Context context;
private final float[] projectionMatrix=new float[16];
private final float[] modelMatrix=new float[16];
private Table table;
private Mallet mallet;
private TextureShaderProgram textureProgram;
private ColorShaderProgram colorProgram;
private int texture;
public LYJRenderer(Context context){
this.context=context
}


我们只保留上下文和矩阵的变量,并添加了顶点数组,着色器程序和纹理的变量。这个构造函数被简化为只保存一个Android上下文的引用。


初始化变量


在onSurfaceCreated()加入初始化这些变量:

GLES20.glClearColor(0.0f,0.f,0.0f,0.0f);
this.table=new Table();
this.mallet=new Mallet();
this.textureProgram=new TextureShaderProgram(context);
this.colorProgram=new ColorShaderProgram (context);
this.texture=TextureHelper.loadTexture(Context,R.drawable.air_hockey_surface);


我们把清屏颜色设置为黑色,初始化顶点数组和着色器程序。并用本篇博文的第一个小标题的函数加载纹理。

GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
this.textureProgram.useProgram();
this.textureProgram.setUniforms(this.projectionMatrix,this.texture);
this.table.bindData(this.textureProgram);
this.table.draw();
this.colorProgram.useProgram();
this.colorProgram.setUniforms(this.projectionMatrix);
this.mallet.bindData(this.colorProgram);
this.mallet.draw();

使用纹理进行绘制


不再赘述onSurfaceChanged(),因为它保持不变的,加入如下代码到onDrawFrame()绘制桌子和木槌:


我们清空了渲染表面,接下来,我们做的第一件事是绘制桌子。我们首先调用this.textureProgram.useProgram();告诉OpenGL使用这个程序,然后通过调用this.textureProgram.setUniforms(this.projectionMatrix,this.texture);把那些uniform传递进来。下一步是通过调用this.table.bindData(this.textureProgram);把顶点数组数据和着色器程序定起来,最后调用this.table.draw();绘制桌子。


我们重复同样的调用顺序,用颜色着色器程序绘制了木槌。


源代码地址:http://download.csdn.net/detail/liyuanjinglyj/8848105


程序运行后的效果图如下图所示:

41.png

相关文章
|
20天前
|
API Android开发 iOS开发
深入探索Android与iOS的多线程编程差异
在移动应用开发领域,多线程编程是提高应用性能和响应性的关键。本文将对比分析Android和iOS两大平台在多线程处理上的不同实现机制,探讨它们各自的优势与局限性,并通过实例展示如何在这两个平台上进行有效的多线程编程。通过深入了解这些差异,开发者可以更好地选择适合自己项目需求的技术和策略,从而优化应用的性能和用户体验。
|
5月前
|
移动开发 开发工具 Android开发
探索安卓与iOS开发的差异:平台特性与编程实践
【7月更文挑战第8天】在移动开发的广阔天地中,安卓和iOS这两大操作系统各自占据着半壁江山。它们在用户界面设计、系统架构及开发工具上展现出截然不同的特色。本文将深入探讨这两个平台在技术实现和开发生态上的关键差异,并分享一些实用的开发技巧,旨在为跨平台开发者提供有价值的见解和建议。
|
6月前
|
安全 IDE Android开发
探索Android与iOS开发的差异:平台特性与编程实践
【6月更文挑战第17天】在移动应用开发的广阔天地中,Android和iOS两大平台各自占据半壁江山。它们在用户群体、系统架构以及开发环境上的差异,为开发者带来了不同的挑战和机遇。本文深入探讨了这两个平台在技术实现、界面设计、性能优化等方面的主要区别,并提供了实用的开发建议,旨在帮助开发者更好地理解各自平台的特性,从而创造出更加优秀的移动应用。
|
5月前
|
安全 Java 数据处理
Android多线程编程实践与优化技巧
Android多线程编程实践与优化技巧
|
6月前
|
安全 Java 数据处理
Android多线程编程实践与优化技巧
Android多线程编程实践与优化技巧
|
7月前
|
Java Android开发 Dart
50家大厂面试万字精华总结android编程基础学习
50家大厂面试万字精华总结android编程基础学习
|
7月前
|
Android开发 开发者
Android开发之通过渲染纹理展示地球仪
该文阐述了如何使用OpenGL为三维物体添加纹理,以增强其真实感。纹理坐标是二维的,用于标记摊平后的“布料”对应物体的哪个部位,类似裁缝制作衣服的过程。在OpenGL中,启用纹理和深度测试是关键,还包括设置纹理参数、分配纹理编号、绑定位图材质等步骤。计算材质的纹理坐标后,通过`glDrawArrays`结合顶点和纹理坐标逐个贴图。最终示例展示了将世界地图贴到球体上形成逼真的地球仪效果。通过控制旋转、平移和缩放,能实现简单的三维动画效果。
82 2
Android开发之通过渲染纹理展示地球仪
|
7月前
|
Java Android开发
Android开发之使用OpenGL实现翻书动画
本文讲述了如何使用OpenGL实现更平滑、逼真的电子书翻页动画,以解决传统贝塞尔曲线方法存在的卡顿和阴影问题。作者分享了一个改造后的外国代码示例,提供了从前往后和从后往前的翻页效果动图。文章附带了`GlTurnActivity`的Java代码片段,展示如何加载和显示书籍图片。完整工程代码可在作者的GitHub找到:https://github.com/aqi00/note/tree/master/ExmOpenGL。
168 1
Android开发之使用OpenGL实现翻书动画
|
7月前
|
Android开发 开发者
Android开发之OpenGL的画笔工具GL10
这篇文章简述了OpenGL通过GL10进行三维图形绘制,强调颜色取值范围为0.0到1.0,背景和画笔颜色设置方法;介绍了三维坐标系及与之相关的旋转、平移和缩放操作;最后探讨了坐标矩阵变换,包括设置绘图区域、调整镜头参数和改变观测方位。示例代码展示了如何使用这些方法创建简单的三维立方体。
88 1
Android开发之OpenGL的画笔工具GL10
|
7月前
|
前端开发 API vr&ar
Android开发之OpenGL绘制三维图形的流程
即将连载的系列文章将探索Android上的OpenGL开发,这是一种用于创建3D图形和动画的技术。OpenGL是跨平台的图形库,Android已集成其API。文章以2D绘图为例,解释了OpenGL的3个核心元素:GLSurfaceView(对应View)、GLSurfaceView.Renderer(类似Canvas)和GL10(类似Paint)。通过将这些结合,Android能实现3D图形渲染。文章介绍了Renderer接口的三个方法,分别对应2D绘图的构造、测量布局和绘制过程。示例代码展示了如何在布局中添加GLSurfaceView并注册渲染器。
218 1
Android开发之OpenGL绘制三维图形的流程