Android OpenGL纹理

来源:互联网 发布:java方法4种分类 编辑:程序博客网 时间:2024/03/29 14:33

Android OpenGL纹理

首先申明下,本文为笔者学习《OpenGL ES应用开发实践指南》的笔记,并加入笔者自己的理解和归纳总结。

1、纹理

纹理就是一个图像或照片,它们可以被加载进OpenGL中。
每个二维的纹理都有其自己的坐标空间,按照惯例,一个维度叫做S,而另一个叫做T。
大多数计算机图像都有一个默认的方法,通常是y轴向下,y随着向图像的底部移动而增加。


2、纹理过滤

当纹理大小被扩大或缩小时,我们还需要使用纹理过滤。

(1) 最近邻过滤
这个方式为每个片段选择最近的纹理元素。

(2) 双线性过滤
使用双线性插值平滑像素之间的过渡,使用四个邻接的纹理元素,并在它们之间用一个线性插值算法做插值。
双线性过滤很适合处理放大。

(3) MIP贴图
对于缩小处理,双线性过滤值给每个片段使用四个纹理元素,将会失去很多细节。
MIP贴图,可以生成一组优化过的不同大小的纹理。当生成这组纹理的时候,OpenGL会使用所有的纹理元素生成每个级别的纹理,当过滤纹理时,还要确保所有的纹理元素都能被使用。在渲染时,OpenGL会根据每个片段的纹理元素为每个片段选择最适合的解绑。

(4) 三线性过滤
三线性过滤,在最邻近的MIP贴图级别之间也要插值,这样,每个片段总共使用8个纹理元素插值,有助于消除每个MIP贴图级别之间的过渡,并且得到一个更平滑地图像。

3、加载纹理

(1) 生成新的纹理对象
int[] textureObjectIds = new int[1];GLES20.glGenTextures(1, textureObjectIds, 0);
(2) 设置过滤参数
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR_MIPMAP_LINEAR);GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);
OpenGL纹理过滤模式
GL_NEAREST最近邻过滤GL_NEAREST_MIPMAP_NEAREST使用MIP贴图的最近邻过滤GL_NEAREST_MIPMAP_LINEAR使用MIP贴图级别之间插值的最近邻过滤GL_LINEAR双线性过滤GL_LINEAR_MIPMAP_NEAREST使用MIP贴图的双线性过滤GL_LINEAR_MIPMAP_LINEAR三线性过滤(使用MIP贴图级别之间插值的双线性过滤)每种情况下允许的纹理过滤模式
     缩小     
GL_NEAREST                                   
GL_NEAREST_MIPMAP_NEAREST
GL_NEAREST_MIPMAP_LINEAR    
GL_LINEAR                                       
GL_LINEAR_MIPMAP_NEAREST    
GL_LINEAR_MIPMAP_LINEAR        
放大
GL_NEAREST                                  
GL_LINEAR                                      
(3) 绑定图片
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D);
(4) 加载纹理
protected int loadTexture(int resId) {// 生成一个新的OpenGL纹理的IDint[] textureObjectIds = new int[1];GLES20.glGenTextures(1, textureObjectIds, 0);if (textureObjectIds[0] == 0) {LogUtil.log("OpenGL", "Could not generate a new OpenGL texture object.");return 0;}BitmapFactory.Options options = new BitmapFactory.Options();// 需要原始的图像数据,而不是缩放版options.inScaled = false;Bitmap bitmap = BitmapFactory.decodeResource(getResources(), resId, options);if (bitmap == null) {LogUtil.log("OpenGL", "Resource ID " + resId + " could not be decoded.");// 如果失败,删除纹理对象GLES20.glDeleteTextures(1, textureObjectIds, 0);return 0;}// 我们需要告诉后面的纹理调用,都应该应用于这个纹理对象GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureObjectIds[0]);// 设置过滤参数GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_LINEAR_MIPMAP_LINEAR);GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);// 加载纹理到OpenGL,并生成MIP贴图GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D);// 回收图片bitmap.recycle();// 与当前纹理解除绑定,防止被修改GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);return textureObjectIds[0];}

4、着色器文件

(1) 顶点着色器,texture_vertex_shader.glsl文件
a_TextureCoordinates是纹理坐标,有两个分量:S坐标和T坐标

uniform mat4 u_Matrix;attribute vec4 a_Position;attribute vec2 a_TextureCoordinates;varying vec2 v_TextureCoordinates;void main(){    v_TextureCoordinates = a_TextureCoordinates;    gl_Position = u_Matrix * a_Position;}
(2) 片段着色器,texture_fragment_shader文件
u_textureUnit被定义为一个sampler2D,这个变量类型指的是一个二维纹理数据的数组。

precision mediump float;uniform sampler2D u_textureUnit;varying vec2 v_TextureCoordinates;void main(){     gl_FragColor = texture2D(u_textureUnit, v_TextureCoordinates);}

4、绘制着色器

(1) 顶点数据
纹理数据采用不同的坐标方式

float[] tableVertices = {   0f,    0f, 0.5f, 0.5f,-0.5f, -0.8f,   0f, 0.9f, 0.5f, -0.8f,   1f, 0.9f, 0.5f,  0.8f,   1f, 0.1f,-0.5f,  0.8f,   0f, 0.1f,-0.5f, -0.8f,   0f, 0.9f,};
(2) 绘制纹理
// 把活动的纹理单元设置为纹理单元0GLES20.glActiveTexture(GLES20.GL_TEXTURE0);// 把纹理绑定到这个单元GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId);// 把被选择的纹理单元传递给片段着色器中的GLES20.glUniform1i(uTextureUnitLocation, 0);
(3) 绘制不同着色器
Resource类,包含一个顶点数据类VertexArray,bindData方法用来绑定属性,draw方法用来绘制界面。

public class Resource {    protected VertexArray mVertextArray;    public Resource(float[] vertexData) {        mVertextArray = new VertexArray(vertexData);    }    public void bindData(int attributeLocation, int dataOffset,        int componentCount, int stride) {        mVertextArray.setVertexAttribPointer(attributeLocation,                dataOffset, componentCount, stride);    }    public void draw() {    }}
VertexArray类,保存顶点数据
public class VertexArray {    public static final int BYTES_PER_FLOAT = 4;    private final FloatBuffer floatBuffer;    public VertexArray(float[] vertexData) {        floatBuffer = ByteBuffer                .allocateDirect(vertexData.length * BYTES_PER_FLOAT)                .order(ByteOrder.nativeOrder())                .asFloatBuffer();        floatBuffer.put(vertexData);    }    public void setVertexAttribPointer(int attributeLocation, int dataOffset,                                       int componentCount, int stride) {        floatBuffer.position(dataOffset);        GLES20.glVertexAttribPointer(attributeLocation, componentCount,                GLES20.GL_FLOAT, false, stride, floatBuffer);        floatBuffer.position(0);        GLES20.glEnableVertexAttribArray(attributeLocation);    }}
Table类,绘制桌子
public class Table extends Resource {    private final static int POSITION_COMPONENT_COUNT = 2;    private final static int TEXTURE_COMPONENT_COUNT = 2;    private final static int STRIDE = (POSITION_COMPONENT_COUNT + TEXTURE_COMPONENT_COUNT)            * VertexArray.BYTES_PER_FLOAT;    private final static float[] vertexData = new float[]{               0f,    0f, 0.5f, 0.5f,            -0.5f, -0.8f,   0f, 0.9f,             0.5f, -0.8f,   1f, 0.9f,             0.5f,  0.8f,   1f, 0.1f,            -0.5f,  0.8f,   0f, 0.1f,            -0.5f, -0.8f,   0f, 0.9f,    };    public Table() {        super(vertexData);    }    public void bindData(TextureProgram program) {        bindData(program.getPositionLocation(), 0, POSITION_COMPONENT_COUNT, STRIDE);        bindData(program.getTextureCoordLocation(),                POSITION_COMPONENT_COUNT, TEXTURE_COMPONENT_COUNT, STRIDE);    }    @Override    public void draw() {        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, 6);    }}
Mallet类,暂用点代替
public class Mallet extends Resource {    private final static int POSITION_COMPONENT_COUNT = 2;    private final static int COLOR_COMPONENT_COUNT = 3;    private final static int STRIDE = (POSITION_COMPONENT_COUNT + COLOR_COMPONENT_COUNT)            * VertexArray.BYTES_PER_FLOAT;    private final static float[] vertexData = new float[]{            0f, -0.25f, 0f, 0f, 1f,            0f,  0.25f, 1f, 0f, 0f    };    public Mallet() {        super(vertexData);    }    public void bindData(ColorProgram program) {        bindData(program.getPositionLocation(), 0,                POSITION_COMPONENT_COUNT, STRIDE);        bindData(program.getColorLocation(), POSITION_COMPONENT_COUNT,                COLOR_COMPONENT_COUNT, STRIDE);    }    @Override    public void draw() {        GLES20.glDrawArrays(GLES20.GL_POINTS, 0, 2);    }}
Program类,保留顶点数据,加载着色器程序
public class Program {    protected int mProgramId;    public Program(Context context, int vertexShaderResId,                   int fragmentShaderResId) {        mProgramId = useProgram(context, vertexShaderResId, fragmentShaderResId);    }    public void setUniform(float[] projectionMatrix) {    }    protected String readShaderFromRaw(Context context, int resId) {        BufferedReader br = null;        StringBuffer stringBuffer = new StringBuffer();        try {            br = new BufferedReader(new InputStreamReader(                    context.getResources().openRawResource(resId)));            String line = null;            while ((line = br.readLine()) != null) {                stringBuffer.append(line + "\n");            }        } catch (IOException e) {        } finally {            if (br != null) {                try {                    br.close();                } catch (IOException e) {                }            }        }        return stringBuffer.toString();    }    protected int compileShader(int type, String shaderCode) {        // 创建一个新的着色器对象        int shaderObjectId = GLES20.glCreateShader(type);        if (shaderObjectId == 0) {            // 创建失败            return 0;        }        // 上传和编译着色器代码        GLES20.glShaderSource(shaderObjectId, shaderCode);        GLES20.glCompileShader(shaderObjectId);        // 获取编译状态        int[] compileStatus = new int[1];        GLES20.glGetShaderiv(shaderObjectId, GLES20.GL_COMPILE_STATUS, compileStatus, 0);        // 获取着色器信息日志        LogUtil.log("OpenGL", GLES20.glGetShaderInfoLog(shaderObjectId));        if (compileStatus[0] == 0) {            // 如果失败,删除着色器对象            GLES20.glDeleteShader(shaderObjectId);            return 0;        }        return shaderObjectId;    }    protected int linkProgram(int vertexShaderId, int fragmentShaderId) {        // 创建一个新的程序对象        int programId = GLES20.glCreateProgram();        if (programId == 0) {            return 0;        }        // 新建程序对象附上着色器,并链接程序        GLES20.glAttachShader(programId, vertexShaderId);        GLES20.glAttachShader(programId, fragmentShaderId);        GLES20.glLinkProgram(programId);        // 获取链接状态        int[] linkStatus = new int[1];        GLES20.glGetProgramiv(programId, GLES20.GL_LINK_STATUS, linkStatus, 0);        // 获取着程序链接信息日志        LogUtil.log("OpenGL", GLES20.glGetProgramInfoLog(programId));        if (linkStatus[0] == 0) {            // 如果链接失败,删除程序对象            GLES20.glDeleteProgram(programId);            return 0;        }        return programId;    }    protected boolean validateProgram(int programId) {        // 验证程序,只在开发阶段需要        GLES20.glValidateProgram(programId);        LogUtil.log("OpenGL", GLES20.glGetProgramInfoLog(programId));        int[] validateStatus = new int[1];        GLES20.glGetProgramiv(programId, GLES20.GL_VALIDATE_STATUS, validateStatus, 0);        return validateStatus[0] != 0;    }    protected int useProgram(Context context, int vertexShaderResId, int fragmentShaderResId) {        String vertexShaderCode = readShaderFromRaw(context, vertexShaderResId);        String fragmentShaderCode = readShaderFromRaw(context, fragmentShaderResId);        int vertexShaderId = compileShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);        int fragmentShaderId = compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);        int programId = linkProgram(vertexShaderId, fragmentShaderId);        validateProgram(programId);        GLES20.glUseProgram(programId);        return programId;    }}
ColorProgram类,绘制颜色着色器
public class ColorProgram extends Program {    private final static String A_POSITION = "a_Position";    private final static String A_COLOR = "a_Color";    private final static String U_MATRIX = "u_Matrix";    private int aPositionLocation, aColorLocation, uMatrixLocation;    public ColorProgram(Context context) {        super(context, R.raw.ortho_vertex_shader, R.raw.ortho_fragment_shader);        aPositionLocation = GLES20.glGetAttribLocation(mProgramId, A_POSITION);        aColorLocation = GLES20.glGetAttribLocation(mProgramId, A_COLOR);        uMatrixLocation = GLES20.glGetUniformLocation(mProgramId, U_MATRIX);    }    @Override    public void setUniform(float[] projectionMatrix) {        GLES20.glUseProgram(mProgramId);        GLES20.glUniformMatrix4fv(uMatrixLocation, 1, false, projectionMatrix, 0);    }    public int getPositionLocation() {        return aPositionLocation;    }    public int getColorLocation() {        return aColorLocation;    }}
TextureProgram类,绘制纹理着色器
public class TextureProgram extends Program {    private final static String A_POSITION = "a_Position";    private final static String A_TEXTURRE_COORDINATES = "a_TextureCoordinates";    private final static String U_MATRIX = "u_Matrix";    private final static String U_TEXTURE_UNIT = "u_TextureUnit";    private int aPositionLocation, aTextureCoordLocation, uMatrixLocation, uTextureUnitLocation;    private int mTextureId;    public TextureProgram(Context context, int resId) {        super(context, R.raw.texture_vertex_shader, R.raw.texture_fragment_shader);        aPositionLocation = GLES20.glGetAttribLocation(mProgramId, A_POSITION);        aTextureCoordLocation = GLES20.glGetAttribLocation(mProgramId, A_TEXTURRE_COORDINATES);        uMatrixLocation = GLES20.glGetUniformLocation(mProgramId, U_MATRIX);        uTextureUnitLocation = GLES20.glGetUniformLocation(mProgramId, U_TEXTURE_UNIT);        mTextureId = loadTexture(context, resId);    }    protected int loadTexture(Context context, int resId) {        // 生成一个新的OpenGL纹理的ID        int[] textureObjectIds = new int[1];        GLES20.glGenTextures(1, textureObjectIds, 0);        if (textureObjectIds[0] == 0) {            LogUtil.log("OpenGL", "Could not generate a new OpenGL texture object.");            return 0;        }        BitmapFactory.Options options = new BitmapFactory.Options();        // 需要原始的图像数据,而不是缩放版        options.inScaled = false;        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resId, options);        if (bitmap == null) {            LogUtil.log("OpenGL", "Resource ID " + resId + " could not be decoded.");            // 如果失败,删除纹理对象            GLES20.glDeleteTextures(1, textureObjectIds, 0);            return 0;        }        // 我们需要告诉后面的纹理调用,都应该应用于这个纹理对象        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureObjectIds[0]);        // 设置过滤参数        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,                GLES20.GL_LINEAR_MIPMAP_LINEAR);        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,                GLES20.GL_LINEAR);        // 加载纹理到OpenGL,并生成MIP贴图        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);        GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D);        // 回收图片        bitmap.recycle();        // 与当前纹理解除绑定,防止被修改        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);        return textureObjectIds[0];    }    @Override    public void setUniform(float[] projectionMatrix) {        GLES20.glUseProgram(mProgramId);        GLES20.glUniformMatrix4fv(uMatrixLocation, 1, false, projectionMatrix, 0);        // 把活动的纹理单元设置为纹理单元0        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);        // 把纹理绑定到这个单元        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId);        // 把被选择的纹理单元传递给片段着色器中的        GLES20.glUniform1i(uTextureUnitLocation, 0);    }    public int getPositionLocation() {        return aPositionLocation;    }    public int getTextureCoordLocation() {        return aTextureCoordLocation;    }}
(4) OpenGLTextureShaderRender类
class OpenGLTextureShaderRender implements GLSurfaceView.Renderer {private float[] projectionMatrix = new float[16];private float[] modelMatrix = new float[16];private float[] modelProjectionMatrix = new float[16];private TextureProgram mTextureProgram;private ColorProgram mColorProgram;private Table mTable;private Mallet mMallet;@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);mTable = new Table();mMallet = new Mallet();mTextureProgram = new TextureProgram(OpenGLTextureShaderActivity.this,R.drawable.air_hockey_surface);mColorProgram = new ColorProgram(OpenGLTextureShaderActivity.this);}@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {GLES20.glViewport(0, 0, width, height);// 创建透视投影Matrix.perspectiveM(projectionMatrix, 0, 45, (float)width / (float)height, 1, 10);// 定义模型矩阵Matrix.setIdentityM(modelMatrix, 0);// z轴平移-2.8Matrix.translateM(modelMatrix, 0, 0, 0, -2.8f);Matrix.rotateM(modelMatrix, 0, -60, 1f, 0f, 0f);// 把投影矩阵和模型矩阵相乘Matrix.multiplyMM(modelProjectionMatrix, 0, projectionMatrix, 0, modelMatrix, 0);}@Overridepublic void onDrawFrame(GL10 gl) {GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);mTextureProgram.setUniform(modelProjectionMatrix);mTable.bindData(mTextureProgram);mTable.draw();mColorProgram.setUniform(modelProjectionMatrix);mMallet.bindData(mColorProgram);mMallet.draw();}}
显示如下
原创粉丝点击