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

相关文章
|
4月前
|
物联网 区块链 vr&ar
未来已来:探索区块链、物联网与虚拟现实技术的融合与应用安卓与iOS开发中的跨平台框架选择
【8月更文挑战第30天】在科技的巨轮下,新技术不断涌现,引领着社会进步。本文将聚焦于当前最前沿的技术——区块链、物联网和虚拟现实,探讨它们各自的发展趋势及其在未来可能的应用场景。我们将从这些技术的基本定义出发,逐步深入到它们的相互作用和集成应用,最后展望它们如何共同塑造一个全新的数字生态系统。
|
5月前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台框架解析
在移动应用开发的广阔舞台上,安卓和iOS一直是两大主角。随着技术的进步,开发者们渴望能有一种方式,让他们的应用能同时在这两大平台上运行,而不必为每一个平台单独编写代码。这就是跨平台框架诞生的背景。本文将探讨几种流行的跨平台框架,包括它们的优势、局限性,以及如何根据项目需求选择合适的框架。我们将从技术的深度和广度两个维度,对这些框架进行比较分析,旨在为开发者提供一个清晰的指南,帮助他们在安卓和iOS的开发旅程中,做出明智的选择。
|
25天前
|
算法 JavaScript Android开发
|
2月前
|
Java 程序员 API
Android|集成 slf4j + logback 作为日志框架
做个简单改造,统一 Android APP 和 Java 后端项目打印日志的体验。
106 1
|
3月前
|
前端开发 Java 数据库
💡Android开发者必看!掌握这5大框架,轻松打造爆款应用不是梦!🏆
在Android开发领域,框架犹如指路明灯,助力开发者加速应用开发并提升品质。本文将介绍五大必备框架:Retrofit简化网络请求,Room优化数据库访问,MVVM架构提高代码可维护性,Dagger 2管理依赖注入,Jetpack Compose革新UI开发。掌握这些框架,助你在竞争激烈的市场中脱颖而出,打造爆款应用。
385 3
|
3月前
|
编译器 Android开发 开发者
带你了解Android Jetpack库中的依赖注入框架:Hilt
本文介绍了Hilt,这是Google为Android开发的依赖注入框架,基于Dagger构建,旨在简化依赖注入过程。Hilt通过自动化的组件和注解减少了DI的样板代码,提高了应用的可测试性和可维护性。文章详细讲解了Hilt的主要概念、基本用法及原理,帮助开发者更好地理解和应用Hilt。
85 8
|
4月前
|
设计模式 Java Android开发
探索安卓应用开发:从新手到专家的旅程探索iOS开发中的SwiftUI框架
【8月更文挑战第29天】本文旨在通过一个易于理解的旅程比喻,带领读者深入探讨安卓应用开发的各个方面。我们将从基础概念入手,逐步过渡到高级技术,最后讨论如何维护和推广你的应用。无论你是编程新手还是有经验的开发者,这篇文章都将为你提供有价值的见解和实用的代码示例。让我们一起开始这段激动人心的旅程吧!
|
4月前
|
Android开发
基于Amlogic 安卓9.0, 驱动简说(三):使用misc框架,让驱动更简单
如何使用Amlogic T972安卓9.0系统上的misc框架来简化驱动程序开发,通过misc框架自动分配设备号并创建设备文件,从而减少代码量并避免设备号冲突。
50 0
基于Amlogic 安卓9.0, 驱动简说(三):使用misc框架,让驱动更简单
|
4月前
|
存储 前端开发 Java
Android MVVM框架详解与应用
在Android开发中,随着应用复杂度的增加,如何有效地组织和管理代码成为了一个重要的问题。MVVM(Model-View-ViewModel)架构模式因其清晰的结构和高效的开发效率,逐渐成为Android开发者们青睐的架构模式之一。本文将详细介绍Android MVVM框架的基本概念、优势、实现流程以及一个实际案例。
115 0
|
5月前
|
前端开发 安全 数据库
💡Android开发者必看!掌握这5大框架,轻松打造爆款应用不是梦!🏆
【7月更文挑战第28天】在Android开发领域,五大框架如星辰般指引方向,加速进程,提升应用品质。1. **Retrofit**:Square公司的类型安全HTTP客户端,使网络请求变得优雅简洁。2. **Room**:Google推荐的ORM库,简化SQLite数据库访问。3. **MVVM**:一种架构模式,提高代码可维护性和扩展性。4. **Dagger 2**:依赖注入框架,减少样板代码,以声明方式管理依赖。5. **Jetpack Compose**:全新的UI工具包,采用声明式UI编程,让UI开发更直观高效。掌握这些框架,能有效应对Android开发挑战,助力打造爆款应用。
257 0