android opengles 纹理
2017-01-08 19:43
405 查看
1. 纹理映射
1.1 纹理映射就是将图片贴到绘制的图像上1.2 纹理坐标的坐标系 横轴为S 纵轴为T
1.3 opengles对纹理做了归一化处理,坐标范围都是0.0~1.0
demo:
import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.opengl.GLES20; import android.opengl.GLSurfaceView; import android.opengl.GLUtils; import android.opengl.Matrix; import android.os.Bundle; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new MSur(this)); } } class MSur extends GLSurfaceView { Render render; public MSur(Context context) { super(context); this.setEGLContextClientVersion(2); render = new Render(context); setRenderer(render); this.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); } } class Render implements GLSurfaceView.Renderer { private int vCount; private Context ctx; private FloatBuffer fbv; private FloatBuffer mTexCoorBuffer; static float[] mMMatrix = new float[16]; int mProgram;// 自定义渲染管线程序id int muMVPMatrixHandle;// 总变换矩阵引用id int maPositionHandle; // 顶点位置属性引用id int maTexCoorHandle; // 顶点颜色属性引用id int msTexHandle;//纹理引用id int textureId;//系统分配的纹理id public static float[] mProjMatrix = new float[16];// 4x4矩阵 投影用 public static float[] mVMatrix = new float[16];// 摄像机位置朝向9参数矩阵 public static float[] mMVPMatrix;// 最后起作用的总变换矩阵 public Render(Context ctx) { super(); this.ctx = ctx; } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { GLES20.glClearColor(0, 0, 0, 1.0f); initVertex(); initShader(); GLES20.glEnable(GLES20.GL_DEPTH_TEST); initTexture(); } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { GLES20.glViewport(0, 0, width, height); float ratio = (float) width / height; Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 1, 10); Matrix.setLookAtM(mVMatrix, 0, 0, 0, 3, 0f, 0f, 0f, 0f, 1.0f, 0.0f); } @Override public void onDrawFrame(GL10 gl) { GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT); draw(); } public void draw() { GLES20.glUseProgram(mProgram); Matrix.setRotateM(mMMatrix, 0, 0, 0, 1, 0); mMVPMatrix = new float[16]; Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, mMMatrix, 0); Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0); GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0); GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, 3 * 4, fbv); //TODO 5 将纹理坐标传入glsl GLES20.glVertexAttribPointer(maTexCoorHandle, 2, GLES20.GL_FLOAT, false, 2*4, mTexCoorBuffer); GLES20.glEnableVertexAttribArray(maPositionHandle); GLES20.glEnableVertexAttribArray(maTexCoorHandle); //TODO 6 绑定纹理 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId); //如果只有一个纹理 可以不做这一步 GLES20.glUniform1i(msTexHandle, 0/*GLES20.GL_TEXTURE0=0 GLES20.GL_TEXTURE1=1*/); //TODO 7 显示 GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vCount); } //初始化数据 private void initVertex() { float v[] = { 1f, -1f, 0, 0, 1f, 0, -1f, -1f, 0 }; vCount = v.length / 3; ByteBuffer bb = ByteBuffer.allocateDirect(v.length * 4); bb.order(ByteOrder.nativeOrder()); fbv = bb.asFloatBuffer(); fbv.put(v); fbv.position(0); //TODO 4 初始化纹理坐标 float texCoor[]=new float[]{0, 1, 0.5f, 0, 1, 1}; //创建顶点纹理坐标数据缓冲 ByteBuffer cbb = ByteBuffer.allocateDirect(texCoor.length*4); cbb.order(ByteOrder.nativeOrder());//设置字节顺序 mTexCoorBuffer = cbb.asFloatBuffer();//转换为Float型缓冲 mTexCoorBuffer.put(texCoor);//向缓冲区中放入顶点着色数据 mTexCoorBuffer.position(0);//设置缓冲区起始位置 } //初始化纹理 public void initTexture(){ //TODO 2 生成纹理ID int[] textures = new int[1]; GLES20.glGenTextures ( 1, //产生的纹理id的数量 textures, //纹理id的数组 0 //偏移量 ); textureId=textures[0]; GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId); //纹理采样 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_NEAREST); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR); //纹理拉伸 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_CLAMP_TO_EDGE); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_CLAMP_TO_EDGE); //通过输入流加载图片 InputStream is = this.ctx.getResources().openRawResource(R.raw.wall); Bitmap bitmapTmp; try { bitmapTmp = BitmapFactory.decodeStream(is); } finally { try { is.close(); } catch(IOException e) { e.printStackTrace(); } } //TODO 3 实际加载纹理 GLUtils.texImage2D ( GLES20.GL_TEXTURE_2D, //纹理类型,在OpenGL ES中必须为GL10.GL_TEXTURE_2D 0, //纹理的层次,0表示基本图像层,可以理解为直接贴图 bitmapTmp, //纹理图像 0 //纹理边框尺寸 ); bitmapTmp.recycle(); //纹理加载成功后释放图片 } //初始化shader private void initShader() { String vertex = loadSH("vertex.sh"); String shader = loadSH("frag.sh"); int verS = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER); if (verS != 0) { GLES20.glShaderSource(verS, vertex); GLES20.glCompileShader(verS); } int fragS = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER); if (fragS != 0) { GLES20.glShaderSource(fragS, shader); GLES20.glCompileShader(fragS); } mProgram = GLES20.glCreateProgram(); if (mProgram != 0) { GLES20.glAttachShader(mProgram, verS); GLES20.glAttachShader(mProgram, fragS); GLES20.glLinkProgram(mProgram); } maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition"); //TODO 1 关联glsl的纹理坐标 maTexCoorHandle= GLES20.glGetAttribLocation(mProgram, "aTexCoor"); muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); msTexHandle = GLES20.glGetUniformLocation(mProgram, "sTexture"); } //将sh文件加载进来 private String loadSH(String fname) { String result = null; try { InputStream in = ctx.getAssets().open(fname); int ch = 0; ByteArrayOutputStream baos = new ByteArrayOutputStream(); while ((ch = in.read()) != -1) { baos.write(ch); } byte[] buff = baos.toByteArray(); baos.close(); in.close(); result = new String(buff, "UTF-8"); result = result.replaceAll("\\r\\n", "\n"); } catch (Exception e) { e.printStackTrace(); } return result; } }
frag.sh:
precision mediump float; varying vec2 vTextureCoord; //接收从顶点着色器过来的参数 uniform sampler2D sTexture;//纹理内容数据 void main() { //给此片元从纹理中采样出颜色值 gl_FragColor = texture2D(sTexture, vTextureCoord); }
vertex.sh:
uniform mat4 uMVPMatrix; //总变换矩阵 attribute vec3 aPosition; //顶点位置 attribute vec2 aTexCoor; //顶点纹理坐标 varying vec2 vTextureCoord; //用于传递给片元着色器的变量 void main() { gl_Position = uMVPMatrix * vec4(aPosition,1); //根据总变换矩阵计算此次绘制此顶点位置 vTextureCoord = aTexCoor;//将接收的纹理坐标传递给片元着色器 }
2. 纹理拉伸
当设置纹理的s,t大于1.0时,纹理就会产生拉伸现象拉伸的方式有三种:
2.1 设置S轴的拉伸方式为重复
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_REPEAT);
2.2 设置S轴的拉伸方式为镜像重复
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_MIRRORED_REPEAT);
2.3 设置S轴的拉伸方式为截取
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_CLAMP_TO_EDGE);
s轴与t轴需要分别设置
3. 纹理采样
纹理采样:根据片元的纹理坐标到纹理提取对应位置的颜色渲染时,通过纹理坐标不一定能在纹理图中找到完全对应的像素,此时需要一些策略,常用的有最近点采样和线性采样
3.1 最近点采样 GLES20.GL_NEAREST
纹理由一个一个像素点组成,最近采样点就是直接获取s,t坐标对应的像素点的颜色值
优点:速度快,简单
缺点:将小纹理图映射到大图元时,会产生锯齿现象
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_NEAREST);
3.2 线性采样 GLES20.GL_LINEAR
对采样范围内的多个像素进行加权平均,使纹理平滑过渡从而避免锯齿现象
优点:有效避免锯齿现象
缺点:运算量变大,虽然解决了锯齿,但是颜色差距过大,会产生颜色边界模糊现象
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);
3.3
GLES20.GL_TEXTURE_MAG_FILTER与
GLES20.GL_TEXTURE_MIN_FILTER
大纹理图映射到小图元时 使用GL_TEXTURE_MAG_FILTER
小纹理图映射到大图元时 使用GL_TEXTURE_MIN_FILTER
4. mipmap
若需要设置一个大场景,如地图时,在远处看的很清楚,但是在近处会模糊,因为在近处纹理被拉大。因此需要在远处使用分辨率低的纹理,在近处使用分辨率大的图元,这就是mipmap。mipmap由系统自动完成,只需要提供一张原始图,加载时系统会自动生成一系列纹理,每个纹理图都是前一个的1/2,直到最后一个1x1像素
GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D);
5. 多重纹理与过程纹理
多重纹理:一个图元使用多个纹理,使用GLES20.glUniform1i过程纹理:多个纹理间的平滑过渡
6. 纹理压缩
常规的纹理是将图片解压后送入纹理缓冲区,图像文件会很大。gles采用压缩的方式,通用的格式为ETC1,不支持透明,gles3.0的通用格式ETC2/EAC开始支持。加载时可以直接将压缩文件送到纹理缓冲区使用。加载使用:
ETC1Util.loadTexture
补充:gles3.0新特性
增加了色彩通道,可以对映射到图元的rgba分量进行单独的控制增加3D纹理
2D纹理数组,一个着色器中需要多个纹理时,简化开发
采样器配置对象sampler,加载每一个纹理时,都需要设置拉伸方式和采样方式,采用采样适配器可以统一设置,避免重复,提高效率
参考《opengles 3.x 游戏开发 上卷》
相关文章推荐
- 利用android_ndk开发OpenGLES如何利用上层Bitmap生成纹理
- Android OpenglES 绘制多个纹理
- Android Opengles2.0 多纹理融合
- (转)使用OpenGL显示图像(七)Android OpenGLES2.0——纹理贴图之显示图片
- OpenglES2.0 for Android:再谈纹理映射
- android OpenGLES开发 第五课 纹理映射
- Android Camera2 Opengles2.0 预览图像实时滤镜 视频编码
- Android 优化之 纹理
- osg for android 学习之十四:再说纹理
- android开发基础知识:OpenGL纹理本质
- Android+NDK+OpenGLES开发环境配置
- Android Opengles 学习一
- Android OpenGLES – Load Customized Mesh File
- Android中使用OpenGL ES实现纹理贴图以及实现多重纹理混合
- android opengles光照效果-环境光
- Android OpenGL中的纹理
- android opengles 最简单的三角形的方法
- iOS和android游戏纹理优化和内存优化(cocos2d-x)
- android 游戏导引(4. 简单纹理贴图)
- Android上使用OpenGLES2.0显示YUV数据