OpenGL ES之十——纹理贴图(展示一张图片)

概述

这是一个系列的Android平台下OpenGl ES介绍,从最基本的使用最终到VR图的展示的实现,属于基础篇。(后面针对VR视频会再有几篇文章,属于进阶篇)

OpenGL ES之一——概念扫盲
OpenGL ES之二——Android中的OpenGL ES概述
OpenGL ES之三——绘制纯色背景
OpenGL ES之四——绘制点,线,三角形
OpenGL ES之五——相机和投影,绘制等腰三角形
OpenGL ES之六——绘制矩形和圆形
OpenGL ES之七——着色器语言GLSL
OpenGL ES之八——GLES20类和Matrix类
OpenGL ES之九——相机和投影
OpenGL ES之十——纹理贴图(展示一张图片)
OpenGL ES之十一——绘制3D图形
OpenGL ES之十二——地球仪和VR图

本篇概述

学了半天了不可能就让我们使用OpenGL ES绘制几个破多边形吧,那肯定是不会的,但是基础是十分的重要的。只有一点点循序渐进的深入才能感觉知识来的不是那么的突兀,让人难以理解。接下来我们就来展示一张图片。展示图片之前我们要先了解几个概念,之后就很好理解2D纹理绘制也是很简单的。

一.2D纹理和坐标

1.1 2D纹理

在真实世界中,纹理表示一个物体的表面的颜色、纹路以及触觉特征,它是3D的多维的(包括视觉和触觉)。而在OpenGL ES中由于所有的图像都是呈现在屏幕上的,所以就只剩下颜色和纹路也就是图案是2D的。2D纹理本质上是一个图像数据的二维数组
一个纹理的基本数据元素称作"纹素(texel)"。用2D纹理渲染时,纹理坐标用作纹理图像中的索引。2D纹理的纹理坐标用一对2D坐标(s,t)指定,有时也 称作(u,v)坐标。

1.2 纹理映射时的坐标系

纹理坐标用浮点数来表示,范围一般从0.0到1.0,左上角坐标为(0.0,0.0),右上角坐标为(1.0,0.0),左下角坐标为(0.0,1.0),右下角坐标为(1.0,1.0),具体如下:
纹理坐标系(纹理映射时的坐标系下文中均使用此简称)和OpenGL ES是不同的,先看下图:
OpenGL ES之十——纹理贴图(展示一张图片)_第1张图片
纹理坐标也是一个虚拟坐标,如图中我们发现和我们显示器的坐标是一样的,因为他就是为了显示而服务的,我们可以将它看作是一个pc显示器或者手机的显示器
这里为了下面的顶点之间的对应这里也将OpenGLES坐标系贴出如下:
OpenGL ES之十——纹理贴图(展示一张图片)_第2张图片

1.3纹理坐标和OpenGL坐标

从上面我们知道了两种坐标系,从中我们知道作为两种坐标系中各个顶点位置的对应关系,例如在两个坐标系中右下角的顶点在OpenGLES中为(1,-1)而在纹理坐标系中为(1,1)如下图,黑色为纹理坐标,红色为OpenGLES坐标。
OpenGL ES之十——纹理贴图(展示一张图片)_第3张图片

通过指定纹理坐标,可以映射到纹素。例如一个256x256大小的二维纹理,坐标(0.5,1.0)对应的纹素即是(256x0.5 = 128, 256x1.0 = 256)。

纹理映射时只需要为物体的顶点指定纹理坐标即可,其余部分由片元着色器插值完成

OpenGL ES之十——纹理贴图(展示一张图片)_第4张图片

模型变换

模型变换,就是对物体进行缩放、旋转、平移等操作。当对物体进行这些操作时,顶点对应的纹理坐标不会进行改变,通过插值后,物体的纹理也像紧跟着物体发生了变化一样。

二 纹理绘制

2.1着色器文件

顶点着色器

#version 300 es
layout (location = 0) in vec4 vPosition;
layout (location = 1) in vec2 aTextureCoord;
uniform mat4 u_Matrix;
//输出纹理坐标(s,t)
out vec2 vTexCoord;
void main() {
    gl_Position  = u_Matrix*vPosition;
    gl_PointSize = 10.0;
    vTexCoord = aTextureCoord;
}

片段着色器

#version 300 es
precision mediump float;
uniform sampler2D uTextureUnit;
//接收刚才顶点着色器传入的纹理坐标(s,t)
in vec2 vTexCoord;
out vec4 vFragColor;
void main() {
    vFragColor = texture(uTextureUnit,vTexCoord);
}

从上面的着色器中我们可以看出变化和之前的绘制普通带颜色的几何图形没什么太大的变化

变化的地方

  1. 顶点着色器中:将之前颜色向量vec4 aColor变为了纹理向量vec2 aTextureCoord
  2. 片段着色器中:之前直接输出顶点着色器来的颜色,现在变为经过纹理处理最终成为输出颜色

2.2 渲染器

主要的变化:
1. 将原来顶点颜色处理程序变为了纹理处理;
2. 变换矩阵因为显示图片具有宽高尺寸在获取的时候发生变化。

完整的渲染器类如下:

public class Texture2DRenderer implements GLSurfaceView.Renderer {

    private static final String TAG = "Texture2DRenderer";

    private final FloatBuffer vertexBuffer, mTexVertexBuffer;

    private final ShortBuffer mVertexIndexBuffer;

    private int mProgram;

    //纹理id
    private int textureId;

    //相机矩阵
    private final float[] mViewMatrix = new float[16];
    //投影矩阵
    private final float[] mProjectMatrix = new float[16];
    //最终变换矩阵
    private final float[] mMVPMatrix = new float[16];

    //返回属性变量的位置
    //变换矩阵
    private int uMatrixLocation;
    //顶点
    private int aPositionLocation;
    //纹理
    private int aTextureLocation;

    /**
     * 顶点坐标
     * (x,y,z)
     */
    private static final float[] POSITION_VERTEX = new float[]{
            0f, 0f, 0f,     //顶点坐标V0
            1f, 1f, 0f,     //顶点坐标V1
            -1f, 1f, 0f,    //顶点坐标V2
            -1f, -1f, 0f,   //顶点坐标V3
            1f, -1f, 0f     //顶点坐标V4
    };

    /**
     * 纹理坐标
     * (s,t)
     */
    private static final float[] TEX_VERTEX = {
            0.5f, 0.5f, //纹理坐标V0
            1f, 0f,     //纹理坐标V1
            0f, 0f,     //纹理坐标V2
            0f, 1.0f,   //纹理坐标V3
            1f, 1.0f    //纹理坐标V4
    };

    /**
     * 绘制顺序索引
     */
    private static final short[] VERTEX_INDEX = {
            0, 1, 2,  //V0,V1,V2 三个顶点组成一个三角形
            0, 2, 3,  //V0,V2,V3 三个顶点组成一个三角形
            0, 3, 4,  //V0,V3,V4 三个顶点组成一个三角形
            0, 4, 1   //V0,V4,V1 三个顶点组成一个三角形
    };

    //图片生成的位图
    private Bitmap mBitmap;

    public Texture2DRenderer() {
        vertexBuffer = ByteBuffer.allocateDirect(POSITION_VERTEX.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        vertexBuffer.put(POSITION_VERTEX);
        vertexBuffer.position(0);

        mTexVertexBuffer = ByteBuffer.allocateDirect(TEX_VERTEX.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer()
                .put(TEX_VERTEX);
        mTexVertexBuffer.position(0);

        mVertexIndexBuffer = ByteBuffer.allocateDirect(VERTEX_INDEX.length * 2)
                .order(ByteOrder.nativeOrder())
                .asShortBuffer()
                .put(VERTEX_INDEX);
        mVertexIndexBuffer.position(0);
    }


    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        //将背景设置为黑色
        GLES30.glClearColor(0.0f,0.0f,0.0f,1.0f);

        //编译
        final int vertexShaderId = ShaderUtils.compileVertexShader(ResReadUtils.readResource(R.raw.vertex_texture2d_shader));
        final int fragmentShaderId = ShaderUtils.compileFragmentShader(ResReadUtils.readResource(R.raw.fragment_texture2d_shader));
        //链接程序片段
        mProgram = ShaderUtils.linkProgram(vertexShaderId, fragmentShaderId);
        //在OpenGLES环境中使用程序
        GLES30.glUseProgram(mProgram);

        //获取属性位置
        uMatrixLocation = GLES30.glGetUniformLocation(mProgram, "u_Matrix");
        aPositionLocation = GLES30.glGetAttribLocation(mProgram, "vPosition");
        aTextureLocation = GLES30.glGetAttribLocation(mProgram,"aTextureCoord");
        //加载纹理
        textureId = loadTexture(App.getInstance(), R.drawable.texture2dshow);
    }

    private int loadTexture(Context context, int resourceId) {
        final int[] textureIds = new int[1];
        //创建一个纹理对象
        GLES30.glGenTextures(1, textureIds, 0);
        if (textureIds[0] == 0) {
            Log.e(TAG, "Could not generate a new OpenGL textureId object.");
            return 0;
        }
        final BitmapFactory.Options options = new BitmapFactory.Options();
        //这里需要加载原图未经缩放的数据
        options.inScaled = false;
        mBitmap= BitmapFactory.decodeResource(context.getResources(), resourceId, options);
        if (mBitmap == null) {
            Log.e(TAG, "Resource ID " + resourceId + " could not be decoded.");
            GLES30.glDeleteTextures(1, textureIds, 0);
            return 0;
        }
        //绑定纹理到OpenGL
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[0]);

        //设置默认的纹理过滤参数
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR_MIPMAP_LINEAR);
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);

        //加载bitmap到纹理中
        GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, mBitmap, 0);

        //生成MIP贴图
        GLES30.glGenerateMipmap(GLES30.GL_TEXTURE_2D);

        //数据如果已经被加载进OpenGL,则可以回收该bitmap
        mBitmap.recycle();

        //取消绑定纹理
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);

        return textureIds[0];
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        //设置绘制窗口
        GLES30.glViewport(0, 0, width, height);

        int w=mBitmap.getWidth();
        int h=mBitmap.getHeight();
        float sWH=w/(float)h;
        float sWidthHeight=width/(float)height;
        if(width>height){
            if(sWH>sWidthHeight){
                Matrix.orthoM(mProjectMatrix, 0, -sWidthHeight*sWH,sWidthHeight*sWH, -1,1, 3, 7);
            }else{
                Matrix.orthoM(mProjectMatrix, 0, -sWidthHeight/sWH,sWidthHeight/sWH, -1,1, 3, 7);
            }
        }else{
            if(sWH>sWidthHeight){
                Matrix.orthoM(mProjectMatrix, 0, -1, 1, -1/sWidthHeight*sWH, 1/sWidthHeight*sWH,3, 7);
            }else{
                Matrix.orthoM(mProjectMatrix, 0, -1, 1, -sWH/sWidthHeight, sWH/sWidthHeight,3, 7);
            }
        }
        //设置相机位置
        Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 7.0f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
        //计算变换矩阵
        Matrix.multiplyMM(mMVPMatrix,0,mProjectMatrix,0,mViewMatrix,0);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);

        //将变换矩阵传入顶点渲染器
        GLES30.glUniformMatrix4fv(uMatrixLocation,1,false,mMVPMatrix,0);
        //启用顶点坐标属性
        GLES30.glEnableVertexAttribArray(aPositionLocation);
        GLES30.glVertexAttribPointer(aPositionLocation, 3, GLES30.GL_FLOAT, false, 0, vertexBuffer);
        //启用纹理坐标属性
        GLES30.glEnableVertexAttribArray(aTextureLocation);
        GLES30.glVertexAttribPointer(aTextureLocation, 2, GLES30.GL_FLOAT, false, 0, mTexVertexBuffer);
        //激活纹理
        GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
        //绑定纹理
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureId);
        // 绘制
        GLES30.glDrawElements(GLES20.GL_TRIANGLES, VERTEX_INDEX.length, GLES20.GL_UNSIGNED_SHORT, mVertexIndexBuffer);

        //禁止顶点数组的句柄
        GLES30.glDisableVertexAttribArray(aPositionLocation);
        GLES30.glDisableVertexAttribArray(aTextureLocation);
    }
}

2.3显示效果

竖屏
OpenGL ES之十——纹理贴图(展示一张图片)_第5张图片
横屏

你可能感兴趣的:(OpenGL,ES)