美文网首页SurfaceViewUnityShader
《OpenGL从入门到放弃06》纹理图片显示

《OpenGL从入门到放弃06》纹理图片显示

作者: 蓝师傅_Android | 来源:发表于2019-04-27 14:51 被阅读0次

    之前的文章:
    《OpenGL从入门到放弃01 》一些基本概念
    《OpenGL从入门到放弃02 》GLSurfaceView和Renderer
    《OpenGL从入门到放弃03 》相机和视图
    《OpenGL从入门到放弃04 》画一个长方形
    《OpenGL从入门到放弃05》着色器语言

    这一节我们来学习一下如何用OpenGL显示一张图片,先上图


    在OpenGL中显示一张图片,需要使用到纹理采样函数 texture2D,整个步骤大概如下

    我们在《OpenGL从入门到放弃04 》画一个长方形的基础上修改

    1、修改着色器

        // 顶点着色器的脚本
        String vertexShaderCode =
                "uniform mat4 uMVPMatrix;" +         //接收传入的转换矩阵
                "attribute vec4 vPosition;" +      //接收传入的顶点
                "attribute vec2 aTexCoord;" +       //接收传入的顶点纹理位置
                "varying vec2 vTextureCoord;" +     //增加用于传递给片元着色器的纹理位置变量
                "void main() {" +
                        "gl_Position = uMVPMatrix * vPosition;" +  //矩阵变换计算之后的位置
                        "vTextureCoord = aTexCoord;" +
                " }";
    
        // 片元着色器的脚本
        String fragmentShaderCode =
                " precision mediump float;" +  // 声明float类型的精度为中等(精度越高越耗资源)
                  "varying vec2 vTextureCoord;" +  //纹理坐标
                  "uniform sampler2D sTexture;" + //纹理采样器,代表一副纹理
                  " void main() {" +
                         "gl_FragColor = texture2D(sTexture,vTextureCoord);" +//进行纹理采样
                   " }";
    

    片元的颜色不再是简单的单色,而是通过texture2D进行纹理采样,得到的颜色。

    纹理采样器 sampler2D sTexture,变量名sTexture是我们自己取的,我们不用为sTexture赋值,着色器会自动处理sTexture,指向我们待会儿要传过去的纹理图片,纹理坐标由顶点着色器传过来。

    2、准备纹理坐标数据

    先说一下纹理坐标系

    纹理贴图:把一个纹理(对于2D贴图,可以简单的理解为图片),按照所期望的方式显示在图形的表面。

    如果想把一幅纹理映射到相应的几何图元,就必须告诉GPU如何进行纹理映射,也就是为图元的顶点指定恰当的纹理坐标。纹理坐标用浮点数来表示,范围一般从0.0到1.0,左上角坐标为(0.0,0.0),右上角坐标为(1.0,0.0),左下角坐标为(0.0,1.0),右下角坐标为(1.0,1.0)

    纹理坐标
    顶点坐标

    看上面两张图就明白了,假如长方形的顶点顺序是V1、v2、v4、v3,那么纹理坐标对应V1、v2、v4、v3,才能正常显示

    了解了纹理坐标之后,开始写代码了~

    根据长方形的顶点坐标,定义对应的纹理坐标

    private void initVertext() {
            float vertices[] = new float[]{
                    -1, 1, 0,
                    -1, -1, 0,
                    1, 1, 0,
                    1, -1, 0,
            };//顶点位置
    
            float[] colors = new float[]{
                    0, 0,
                    0, 1,
                    1, 0,
                    1, 1,
            };//纹理顶点数组
            //转换成 FloatBuffer
            mVertexBuffer = GLUtil.floatArray2FloatBuffer(vertices);
            mTexCoordBuffer = GLUtil.floatArray2FloatBuffer(colors);
        }
    
    

    这一步非常简单了,顶点坐标跟纹理坐标一一对应,比如顶点在左上角[-1,1],纹理坐标对应[0,0],此处忽略z轴。不明白再看看上面两张图

    3、获取纹理坐标句柄

    顶点着色器中增加了纹理坐标变量 vTextureCoord,
    需要给他赋值,所以先获取句柄,然后在draw方法进行设置数据

            //纹理位置句柄
            maTexCoordHandle = GLES20.glGetAttribLocation(mProgram,"aTexCoord");
    

    光有纹理坐标,没有纹理数据也不行,所以下一步是设置纹理数据

    4、设置纹理数据

    先上代码

        private void initTexture(){
            int textures[] = new int[1]; //生成纹理id
    
            GLES20.glGenTextures(  //创建纹理对象
                    1, //产生纹理id的数量
                    textures, //纹理id的数组
                    0  //偏移量
            );
            mTextureId = textures[0];
    
            //绑定纹理id,将对象绑定到环境的纹理单元
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId);
    
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                    GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_NEAREST);//设置MIN 采样方式
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                    GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);//设置MAG采样方式
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                    GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_CLAMP_TO_EDGE);//设置S轴拉伸方式
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                    GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_CLAMP_TO_EDGE);//设置T轴拉伸方式
    
            if (mBitmap == null){
                Log.e("lxb", "initTexture: mBitmap == null");
                return;
            }
            //加载图片
            GLUtils.texImage2D( //实际加载纹理进显存
                    GLES20.GL_TEXTURE_2D, //纹理类型
                    0, //纹理的层次,0表示基本图像层,可以理解为直接贴图
                    mBitmap, //纹理图像
                    0 //纹理边框尺寸
            );
        }
    

    设置纹理数据有几个步骤

    1. 创建纹理对象 GLES20.glGenTextures,可以拿到纹理id
    2. 绑定纹理id GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId);
    3. 设置采样方法 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
      GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_NEAREST)
    4. 实际加载纹理进显存 GLUtils.texImage2D,需要先准备Bitmap

    4、纹理坐标赋值再绘制

    纹理数据设置好了,纹理句柄有了,只差最后一步,把纹理坐标传给着色器

            //把纹理坐标传给着色器
            GLES20.glEnableVertexAttribArray(maTexCoordHandle);
            GLES20.glVertexAttribPointer(maTexCoordHandle, 2,
                    GLES20.GL_FLOAT, false,
                    2*4, mTexCoordBuffer);
    

    主要修改以上几点,其它的保存不变,整理后的代码如下

    /**
     *
     * 增加纹理
     */
    public class Square02 {
    
        // 顶点着色器的脚本
        String vertexShaderCode =
                "uniform mat4 uMVPMatrix;" +         //接收传入的转换矩阵
                "attribute vec4 vPosition;" +      //接收传入的顶点
                "attribute vec2 aTexCoord;" +       //接收传入的顶点纹理位置
                "varying vec2 vTextureCoord;" +     //增加用于传递给片元着色器的纹理位置变量
                "void main() {" +
                        "gl_Position = uMVPMatrix * vPosition;" +  //矩阵变换计算之后的位置
                        "vTextureCoord = aTexCoord;" +
                " }";
    
    
        // 片元着色器的脚本
        String fragmentShaderCode =
                " precision mediump float;" +  // 声明float类型的精度为中等(精度越高越耗资源)
                  "varying vec2 vTextureCoord;" +
                  "uniform sampler2D sTexture;" + //纹理采样器,代表一副纹理
                  " void main() {" +
                         "gl_FragColor = texture2D(sTexture,vTextureCoord);" +//进行纹理采样
                   " }";
    
        private FloatBuffer mVertexBuffer;  //顶点坐标数据要转化成FloatBuffer格式
        private FloatBuffer mTexCoordBuffer;//顶点纹理坐标缓存
    
    
        // 数组中每3个值作为一个坐标点
        static final int COORDS_PER_VERTEX = 3;
    
        //一个顶点有3个float,一个float是4个字节,所以一个顶点要12字节
        private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per mVertex
    
        //当前绘制的顶点位置句柄
        private int vPositionHandle;
        //变换矩阵句柄
        private int mMVPMatrixHandle;
        //这个可以理解为一个OpenGL程序句柄
        private  int mProgram;
        //纹理坐标句柄
        private int maTexCoordHandle;
    
        //变换矩阵,提供set方法
        private float[] mvpMatrix = new float[16];
        private int mTextureId;
    
        public void setMvpMatrix(float[] mvpMatrix) {
            this.mvpMatrix = mvpMatrix;
        }
        private Bitmap mBitmap;
    
        public Square02(Bitmap bitmap) {
            this.mBitmap = bitmap;
            initVertext();
            initShder();
            initTexture();
        }
    
        private void initVertext() {
            float vertices[] = new float[]{
                    -1, 1, 0,
                    -1, -1, 0,
                    1, 1, 0,
                    1, -1, 0,
            };//顶点位置
    
            float[] colors = new float[]{
                    0, 0,
                    0, 1,
                    1, 0,
                    1, 1,
            };//纹理顶点数组
            mVertexBuffer = GLUtil.floatArray2FloatBuffer(vertices);
            mTexCoordBuffer = GLUtil.floatArray2FloatBuffer(colors);
        }
    
        private void initShder() {
            //获取程序,封装了加载、链接等操作
            mProgram = GLUtil.createProgram(vertexShaderCode,fragmentShaderCode);
            /***1.获取句柄*/
            // 获取顶点着色器的位置的句柄(这里可以理解为当前绘制的顶点位置)
            vPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
            // 获取变换矩阵的句柄
            mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
            //纹理位置句柄
            maTexCoordHandle = GLES20.glGetAttribLocation(mProgram,"aTexCoord");
        }
    
        private void initTexture(){
            int textures[] = new int[1]; //生成纹理id
    
            GLES20.glGenTextures(  //创建纹理对象
                    1, //产生纹理id的数量
                    textures, //纹理id的数组
                    0  //偏移量
            );
            mTextureId = textures[0];
    
            //绑定纹理id,将对象绑定到环境的纹理单元
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId);
    
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                    GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_NEAREST);//设置MIN 采样方式
    //        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
    //                GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);//设置MAG采样方式
    //        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
    //                GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_CLAMP_TO_EDGE);//设置S轴拉伸方式
    //        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
    //                GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_CLAMP_TO_EDGE);//设置T轴拉伸方式
    
            if (mBitmap == null){
                Log.e("lxb", "initTexture: mBitmap == null");
                return;
            }
            //加载图片
            GLUtils.texImage2D( //实际加载纹理进显存
                    GLES20.GL_TEXTURE_2D, //纹理类型
                    0, //纹理的层次,0表示基本图像层,可以理解为直接贴图
                    mBitmap, //纹理图像
                    0 //纹理边框尺寸
            );
        }
    
        public void draw() {
            // 将程序添加到OpenGL ES环境
            GLES20.glUseProgram(mProgram);
    
            /**设置数据*/
            // 启用顶点属性,最后对应禁用
            GLES20.glEnableVertexAttribArray(vPositionHandle);
            GLES20.glEnableVertexAttribArray(maTexCoordHandle);
    
            //设置三角形坐标数据(一个顶点三个坐标)
            GLES20.glVertexAttribPointer(vPositionHandle, 3,
                    GLES20.GL_FLOAT, false,
                    3 * 4, mVertexBuffer);
            //设置纹理坐标数据
            GLES20.glVertexAttribPointer(maTexCoordHandle, 2,
                    GLES20.GL_FLOAT, false,
                    2*4, mTexCoordBuffer);
    
            // 将投影和视图转换传递给着色器,可以理解为给uMVPMatrix这个变量赋值为mvpMatrix
            GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
    
    //        //设置使用的纹理编号
    //        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    
            /** 绘制三角形,三个顶点*/
            GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
    
            // 禁用顶点数组(好像不禁用也没啥问题)
            GLES20.glDisableVertexAttribArray(vPositionHandle);
            GLES20.glDisableVertexAttribArray(maTexCoordHandle);
        }
    }
    
    

    源码已经给出,大家可以先动手试试,有问题欢迎留言。

    下一篇文章比较有意思了,关于滤镜的,实现黑白图片、冷暖色调、四分镜等等,敬请期待。

    四分镜

    相关文章

      网友评论

        本文标题:《OpenGL从入门到放弃06》纹理图片显示

        本文链接:https://www.haomeiwen.com/subject/zyyhnqtx.html