美文网首页
[GLES] 最简固定管线GL程序

[GLES] 最简固定管线GL程序

作者: Cocoonshu | 来源:发表于2018-04-27 13:57 被阅读143次

Demo中的小姐姐来自于微博@好梦梦露,如有版权问题请联系本萌~


在这篇文章里,我们介绍一个最简的固定渲染管线的Android OpenGLES代码框架和一个最简的Shader程序用于基本的画面绘制。

在开始之前,我们要了解一些注意事项:

  • OpenGLES的所有API 只能在GLSurfaceView中的GLThread线程中调用才有用
  • 不要试图在MainThread中调用OpenGLES的API,在UI使用硬件加速且没有独立的RenderThread的Android版本中,在MainThread中调用OpenGLES的API会扰乱UI的正常绘制
  • Renderer上的三个接口(onSurfaceCreated / onSurfaceChanged / onDrawFrame)是执行在使用这个渲染器的GLSurfaceView的GLThread中的
  • GLSurfaceView.queue(Runnable)方法也可以使传入的Runnable执行在它的GLThread中
Screenshot_2016-06-30-13-17-24-05.png

编程

流程说明
OpenGLES 基本流程

这是一个简单的绘制图片的渲染流程,需要注意的是不是所有OpenGLES渲染流程都是这样,但大概的流程还是具有相似性的。

OpenGLES本身是个状态机,所以对OpenGLES的所有API的调用,都要注意当前设置的OpenGLES状态。比如,你需要先绑定一个纹理后,后续对纹理绘制效果的设置,才是针对已绑定的这个纹理生效。而对于模型的绘制,你需要在绘制之前,配置好所有绘制这个模型所需要的功能开关,然后设置好所有绘制这个模型所需要的参数和素材,一切就绪后,再调用“绘制”,这样才能正常完成模型的绘制。

总体代码
布局:res/layout/main_acitivity.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <android.opengl.GLSurfaceView
        android:id="@+id/GLSurfaceView_GLImage"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>
Activity:MainActivity.java
package com.cocoonshu.example.gl_image;
 
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.app.Activity;

/**
 * Main content page
 * @author Cocoonshu
 * @date   2016-06-29
 */

public class MainActivity extends Activity {
    private static final int OpenGLES_1_1 = 1; // 使用OpenGLES 1.1的API
    private static final int OpenGLES_2_0 = 2; // 使用OpenGLES 2.0的API

    private GLSurfaceView mGlvOpenGLImage = null; // 承载OpenGLES的控件
    private ImageRenderer mImageRenderer  = null; // 使用OpenGLES API的渲染器

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initializeOpenGLComponents(); 
    }
 
    private void initializeOpenGLComponents() {
        mGlvOpenGLImage = (GLSurfaceView) findViewById(R.id.GLSurfaceView_GLImage);
        mImageRenderer  = new ImageRenderer();
        
        mGlvOpenGLImage.setEGLConfigChooser(5, 6, 5, 0, 16, 8);             // 设置OpenGLES中画布中各个buffer的位数
        mGlvOpenGLImage.setEGLContextClientVersion(OpenGLES_1_1);           // 设置OpenGLES API版本
        mGlvOpenGLImage.setRenderer(mImageRenderer);                        // 设置渲染器
        mGlvOpenGLImage.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); // 设置OpenGLES的渲染驱动模式
    }

}

这种设置方法,不需要去继承GLSurfaceView来自定义一个新控件出来,适合对GLSurfaceView依赖不高的需求。

设置GLSurfaceView的几个方法需要说明一下:

  • setEGLConfigChooser(int redSize, int greenSize, int blueSize, int alphaSize, int depthSize, int stencilSize)

    • 用于设置OpenGLES绘制的颜色缓存的位深,深度缓冲的位深和模板缓冲的位深
    • [redSize, greenSize, int blueSize, int alphaSize]如果是[8, 8, 8, 8],则可以理解为是对应的ARGB_8888
    • [redSize, greenSize, int blueSize, int alphaSize]如果是[5, 6, 5, 0],则可以理解为是对应的RGB_565
    • [depthSize]对应的是OpenGLES中的深度缓冲的位数,深度缓冲可以认为是用来标记物体到摄像机的距离的数据结构
    • [stencilSize]对应的是OpenGLES中的模板缓冲的位数,模板缓冲是用来标记哪些位置应该被保护起来不被绘制的数据结构
  • setEGLContextClientVersion(int version)

    • 是用来配置要使用的OpenGLES API的版本
    • 这里的version号也需要在AndroidManifest.xml里配置对应的feature
    • version = 1,则使用OpenGLES 1.x的API,AndroidManifest.xml里配置应配置<uses-feature android:glEsVersion="0x00010000" />
    • version = 2,则使用OpenGLES 2.x的API,AndroidManifest.xml里配置应配置<uses-feature android:glEsVersion="0x00020000" />
    • version = 3,则使用OpenGLES 3.x的API,AndroidManifest.xml里配置应配置<uses-feature android:glEsVersion="0x00030000" />
  • setRenderMode(int renderMode)

    • 用来配置OpenGLES的渲染驱动模式,有两个值
      • GLSurfaceView.RENDERMODE_WHEN_DIRTY, 在这个模式下,只有当调用了GLSurfaceView.requestRender()后,才会绘制下一帧
      • GLSurfaceView.RENDERMODE_CONTINUOUSLY,在这个模式下,当一帧绘制完成后,下一帧会自动被调用,无论有没有画面更新的需要。所以这个驱动模式会比较耗电,适合在播放视频画面或者一直有持续动画的场景中使用
渲染器: ImageRenderer.java
package com.cocoonshu.example.glimage;
 
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES11;
import android.opengl.GLSurfaceView;
import android.opengl.GLUtils;
import android.opengl.GLSurfaceView.Renderer;
import android.opengl.GLU;
import android.os.AsyncTask;
import android.util.Log;
 
/**
 * A image renderer for GLSurfaceView
 * @author Cocoonshu
 * @date   2016-06-29
 */
public class ImageRenderer implements Renderer {
    protected static final String   TAG                   = "ImageRenderer";
    public    static final int      IMAGE_POSITION_LEFT   = 0;             // 左边图片的索引
    public    static final int      IMAGE_POSITION_CENTER = 1;             // 中间图片的索引
    public    static final int      IMAGE_POSITION_RIGHT  = 2;             // 右边图片的索引
    public    static final int      SCALE_WIDTH           = 0;             // 图片模型的宽度缩放值索引
    public    static final int      SCALE_HEIGHT          = 1;             // 图片模型的高度缩放值索引
    public    static final float    EXPECTED_SCALE        = 15.0f;         // 希望图片被放大到对角线为15.0f个单位的大小
    private   static final String[] ImagePathes           = new String[] { // 要显示的图片的地址
        "eGE1VW53c0Qv.jpg",
        "IdzdYT1pRPT0.jpg",
        "Q2tVRjlhVn5t.jpg"
    };
    
    private GLSurfaceView mHostView            = null;            // 使用此渲染器的GLSurfaceView
    private BitmapLoader  mBitmapLoader        = null;            // 图片异步加载器
    private FloatBuffer   mRectVertexBuffer    = null;            // 矩形的顶点
    private FloatBuffer   mRectTexcoordsBuffer = null;            // 矩形的贴图坐标
    private ShortBuffer   mRectIndiceBuffer    = null;            // 矩形的顶点索引
    private int[]         mTextureIDs          = new int[3];      // 图片的纹理ID集合
    private float[][]     mTextureScalers      = new float[3][2]; // 纹理根据图片的缩放比
    
    public ImageRenderer(GLSurfaceView hostView) {
        // 我们传入使用此渲染器的GLSurfaceView引用,主要是为了能够
        // GLSurfaceView的queue(Runnable)方法,这个方法能够把Runnable
        // 放置在OpenGLES所在的GLThread线程中执行
        mHostView     = hostView;
        mBitmapLoader = new BitmapLoader(mHostView.getResources(), this);
        
        // 准备模型数据
        buildMesh();
    }
    
    /**
     * 创建模型数据
     */
    private void buildMesh() {
        // 顶点数组
        // 创建长宽为单位1.0f的矩形
        float[] vertex = new float[] {
                -0.5f, -0.5f, 0.0f, // 左下角
                -0.5f, +0.5f, 0.0f, // 左上角
                +0.5f, -0.5f, 0.0f, // 右下角
                +0.5f, +0.5f, 0.0f  // 右上角
        };
        
        // 贴图坐标数组
        float[] texcoords = new float[] {
                0.0f, 1.0f,         // 左下角
                0.0f, 0.0f,         // 左上角
                1.0f, 1.0f,         // 右下角
                1.0f, 0.0f          // 右上角
        };
        
        // 顶点索引
        short[] indices = new short[] {
                0, 1, 2,            // 左下三角形
                2, 1, 3             // 右上三角形
        };
        
        // 组装成Buffer
        {// 顶点Buffer
            ByteBuffer byteBuffer = ByteBuffer.allocateDirect(vertex.length * Float.SIZE / 8);
            byteBuffer.order(ByteOrder.nativeOrder());
            mRectVertexBuffer = byteBuffer.asFloatBuffer();
            mRectVertexBuffer.put(vertex);
            mRectVertexBuffer.rewind();
        }
        {// 贴图坐标Buffer
            ByteBuffer byteBuffer = ByteBuffer.allocateDirect(texcoords.length * Float.SIZE / 8);
            byteBuffer.order(ByteOrder.nativeOrder());
            mRectTexcoordsBuffer = byteBuffer.asFloatBuffer();
            mRectTexcoordsBuffer.put(texcoords);
            mRectTexcoordsBuffer.rewind();
        }
        {// 顶点索引Buffer
            ByteBuffer byteBuffer = ByteBuffer.allocateDirect(indices.length * Short.SIZE / 8);
            byteBuffer.order(ByteOrder.nativeOrder());
            mRectIndiceBuffer = byteBuffer.asShortBuffer();
            mRectIndiceBuffer.put(indices);
            mRectIndiceBuffer.rewind();
        }
    }
    /**
     * 初始化OpenGLES: 设置OpenGLES中的各种开关和初始值
     */
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        // 功能性设置
        GLES11.glEnable(GLES11.GL_DEPTH_TEST);                                  // 开启深度测试,如果我们绘制的东西有远近层次之分,就开启它
        GLES11.glDisable(GLES11.GL_ALPHA_TEST);                                 // 关闭透明测试,如果我们需要通过对比模型的透明度来觉得是否绘制它,就开启它
        GLES11.glDisable(GLES11.GL_STENCIL_TEST);                               // 关闭模板测试,如果我们需要用蒙版来遮盖某些绘制部分,就开启它
        GLES11.glDisable(GLES11.GL_BLEND);                                      // 关闭颜色混合,如果我们需要使绘制的半透明模型有颜色的混合效果,就开启它
        GLES11.glEnable(GLES11.GL_DITHER);                                      // 开启颜色抖动,如果是要显示图片,开启它,显示的颜色数量会更丰富
        GLES11.glEnable(GLES11.GL_TEXTURE_2D);                                  // 开启贴图功能,如果我们要使用贴图纹理,就开启它
        GLES11.glDisable(GLES11.GL_LIGHTING);                                   // 关闭光照效果,如果想要在模型表面呈现出光照的明暗变化,就开启它
        GLES11.glDisable(GLES11.GL_FOG);                                        // 关闭雾霾效果,如果想要在场景中绘制出雾霾的效果,就开启它
        
        // 默认值设置
        GLES11.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);                            // 设置清除颜色缓冲的色值,它会是视窗的清屏颜色
        GLES11.glClearDepthf(1.0f);                                             // 设置清除深度缓冲的深度值,它会是深度缓冲的默认深度值
        GLES11.glDepthRangef(0.1f, 100.0f);                                     // 设置深度缓冲的深度范围
        
        // 绘图效果设置
        GLES11.glHint(GLES11.GL_PERSPECTIVE_CORRECTION_HINT, GLES11.GL_NICEST); // 设置透视矫正配置为:质量最好
        GLES11.glHint(GLES11.GL_POINT_SMOOTH_HINT, GLES11.GL_NICEST);           // 设置点绘制平滑度配置为:质量最好
        GLES11.glHint(GLES11.GL_LINE_SMOOTH_HINT, GLES11.GL_NICEST);            // 设置线条绘制平滑度配置为:质量最好
        GLES11.glHint(GLES11.GL_POLYGON_SMOOTH_HINT, GLES11.GL_DONT_CARE);      // 设置模型绘制平滑度配置为:自动
        
        // 开始加载图片并上传纹理
        mBitmapLoader.execute(ImagePathes);
    }
    /**
     * 配置绘图窗口尺寸:重新根据新的尺寸来配置投影矩阵、视窗和绘图区域
     */
    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        // 设置视窗
        GLES11.glViewport(0, 0, width, height);                           // 设置视窗尺寸为控件大小
        
        // 设置投影矩阵
        float fovy             = (float) Math.toRadians(60);              // 视野角度为120°
        float zNear            = 0.1f;                                    // 摄像机的最近端剪裁距离
        float zFar             = 100f;                                    // 摄像机的最远端剪裁距离
        float aspectRatio      = (float) width / (float) height;          // 计算视窗的显示比例
        float horizontalVolume = (float) (zNear * Math.tan(fovy * 0.5f)); // 计算视景体的宽度
        float verticalVolume   = horizontalVolume / aspectRatio;          // 计算视景体的高度
        GLES11.glMatrixMode(GLES11.GL_PROJECTION);                        // 把当前的操作矩阵切换到投影矩阵
        GLES11.glLoadIdentity();                                          // 把当前的操作矩阵重置为单位矩阵
        GLES11.glFrustumf(                                                // 设置投影矩阵为透视投影:
                -horizontalVolume, horizontalVolume,                      //   - 透视视景体的左右边位置
                -verticalVolume, verticalVolume,                          //   - 透视视景体的上下边位置
                zNear, zFar);                                             //   - 透视视景体的前后边位置
    }
    
    /**
     * 绘制一帧画面:相当于View.onDraw(Canvas),不过这里使用OpenGLES的API来绘制
     */
    @Override
    public void onDrawFrame(GL10 gl) {
        // 重置颜色缓存和深度缓冲
        GLES11.glClear(GLES11.GL_COLOR_BUFFER_BIT | GLES11.GL_DEPTH_BUFFER_BIT);
        
        // 设置视图矩阵
        GLES11.glMatrixMode(GLES11.GL_MODELVIEW);                         // 把当前的操作矩阵切换到模型视图矩阵
        GLES11.glLoadIdentity();                                          // 把当前的操作矩阵重置为单位矩阵
        GLU.gluLookAt(gl,                                                 // 设置摄像机的姿态:
                0.0f, 4.0f, 15.0f,                                        //   - 摄像机的位置
                0.0f, 5.0f, 0.0f,                                         //   - 摄像机拍摄的点
                0.0f, 1.0f, 0.0f);                                        //   - 摄像机顶部的朝向
        
        
        {// 摆放并绘制模型,模型应该从远及近地绘图
            // 开启OpenGLES客户端指定网格数据的操作方式
            // 以便从OpenGLES客户端指定网格数据来绘制模型
            GLES11.glEnableClientState(GL10.GL_VERTEX_ARRAY);                    // 启用OpenGLES客户端指定顶点数组的操作方式
            GLES11.glVertexPointer(3, GL10.GL_FLOAT, 0, mRectVertexBuffer);      // 指定顶点数组,每3个数作为一个顶点坐标
            GLES11.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);             // 启用OpenGLES客户端指定贴图坐标数组的操作方式
            GLES11.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mRectTexcoordsBuffer); // 指定贴图坐标数组,每2个数作为一个顶点坐标
            
            // 激活贴图纹理单元,用于纹理绘制
            // 纹理单元的数量是有限的,可以用过glGetIntegerv(GL_MAX_TEXTURE_UNITS, maxTextureUnits, 0);来查询
            //  - OpenGLES 1.0最少有1个纹理单元
            //  - OpenGLES 1.1最少有2个纹理单元
            // 如果一个模型只需要贴一层纹理,那么我们激活一个纹理就足够了
            GLES11.glActiveTexture(GLES11.GL_TEXTURE0); // 激活#0纹理单元
            
            
            {// 摆放并绘制模型
                {// 绘制左边的图片
                    GLES11.glPushMatrix();
                        // 设置模型矩阵:
                        //   - 1. 绕(0.0f, 1.0f, 0.0f)轴旋转40°
                        //   - 2. 把长宽为(1.0f, 1.0f)的矩形片缩放到图片尺寸的宽高比
                        //   - 3. 把矩形移动到左边靠后的位置
                        GLES11.glTranslatef(-10.0f, mTextureScalers[IMAGE_POSITION_LEFT][SCALE_HEIGHT] * 0.5f, -10.0f);
                        GLES11.glScalef(mTextureScalers[IMAGE_POSITION_LEFT][SCALE_WIDTH], mTextureScalers[IMAGE_POSITION_LEFT][SCALE_HEIGHT], 1);
                        GLES11.glRotatef(-40.0f, 0.0f, 1.0f, 0.0f);
                        
                        // 绑定要贴到矩形上的纹理
                        GLES11.glBindTexture(GLES11.GL_TEXTURE_2D, mTextureIDs[IMAGE_POSITION_LEFT]);
                        // 绘制这个模型
                        GLES11.glDrawElements(GLES11.GL_TRIANGLES, mRectIndiceBuffer.capacity(), GLES11.GL_UNSIGNED_SHORT, mRectIndiceBuffer);
                    GLES11.glPopMatrix();
                }
                
                {// 绘制中间的图片
                    GLES11.glPushMatrix();
                        // 设置模型矩阵:
                        //   - 1. 绕(0.0f, 1.0f, 0.0f)轴旋转0°
                        //   - 2. 把长宽为(1.0f, 1.0f)的矩形片缩放到图片尺寸的宽高比
                        //   - 3. 把矩形移动到原点的位置
                        GLES11.glTranslatef(0.0f, mTextureScalers[IMAGE_POSITION_CENTER][SCALE_HEIGHT] * 0.5f, 0.0f);
                        GLES11.glScalef(mTextureScalers[IMAGE_POSITION_CENTER][SCALE_WIDTH], mTextureScalers[IMAGE_POSITION_CENTER][SCALE_HEIGHT], 1);
                        GLES11.glRotatef(0.0f, 0.0f, 1.0f, 0.0f);
                        
                        // 绑定要贴到矩形上的纹理
                        GLES11.glBindTexture(GLES11.GL_TEXTURE_2D, mTextureIDs[IMAGE_POSITION_CENTER]);
                        // 绘制这个模型
                        GLES11.glDrawElements(GLES11.GL_TRIANGLES, mRectIndiceBuffer.capacity(), GLES11.GL_UNSIGNED_SHORT, mRectIndiceBuffer);
                    GLES11.glPopMatrix();
                }
                
                {// 绘制右边的图片
                    GLES11.glPushMatrix();
                        // 设置模型矩阵:
                        //   - 1. 绕(0.0f, 1.0f, 0.0f)轴旋转40°
                        //   - 2. 把长宽为(1.0f, 1.0f)的矩形片缩放到图片尺寸的宽高比
                        //   - 3. 把矩形移动到右边靠后原点的位置
                        GLES11.glTranslatef(10.0f, mTextureScalers[IMAGE_POSITION_RIGHT][SCALE_HEIGHT] * 0.5f, -10.0f);
                        GLES11.glScalef(mTextureScalers[IMAGE_POSITION_RIGHT][SCALE_WIDTH], mTextureScalers[IMAGE_POSITION_RIGHT][SCALE_HEIGHT], 1);
                        GLES11.glRotatef(40.0f, 0.0f, 1.0f, 0.0f);
                        
                        // 绑定要贴到矩形上的纹理
                        GLES11.glBindTexture(GLES11.GL_TEXTURE_2D, mTextureIDs[IMAGE_POSITION_RIGHT]);
                        // 绘制这个模型
                        GLES11.glDrawElements(GLES11.GL_TRIANGLES, mRectIndiceBuffer.capacity(), GLES11.GL_UNSIGNED_SHORT, mRectIndiceBuffer);
                    GLES11.glPopMatrix();
                }
            }
            
            // 关闭OpenGLES客户端指定网格数据的操作方式,以便做其他绘制操作,
            // 如果没有其他绘制操作,可以一直启用这种操作方式
            GLES11.glDisableClientState(GL10.GL_VERTEX_ARRAY);        // 关闭OpenGLES客户端指定顶点数组的操作方式
            GLES11.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); // 关闭OpenGLES客户端指定贴图坐标数组的操作方式
        }
    }
    /**
     * 获取OpenGLES中的上传纹理可能发生的错误
     */
    private void printGLError(String location) {
        int glError = GLES11.glGetError(); // 获取上一个OpenGLES API调用出现的错误码
        if (glError != GLES11.GL_NO_ERROR) {
            Log.e(TAG, String.format("[%s] GLError = %s",
                    location, GLUtils.getEGLErrorString(glError))); // 把错误码转换为可读字符串
        } else {
            Log.i(TAG, String.format("[%s] GLError = No error", location));
        }
    }
    
    /**
     * 把Bitmap显示到OpenGLES绘制的视窗中
     * @param bitmap
     */
    public void displayBitmap(final Bitmap bitmap, final int position) {
        if (mHostView != null) {
            // 因为这个Runnable中的代码是把Bitmap上传为OpenGLES可用的纹理,
            // 需要使用到OpenGLES API,所以需要用queueEvent方法把这个
            // Runnable抛到GLThread中去执行
            mHostView.queueEvent(new Runnable() {
                
                @Override
                public void run() {
                    if (bitmap == null) {
                        Log.e(TAG, String.format("[uploadTexture] bitmap is null, position is %d", position));
                        return;
                    }
                    
                    {// 读取图片的宽高比,以便能按比例显示纹理
                        float imageWidth   = bitmap.getWidth();
                        float imageHeight  = bitmap.getHeight();
                        float aspectRadian = (float) Math.atan2(imageHeight, imageWidth);
                        mTextureScalers[position][SCALE_WIDTH]  = (float) Math.cos(aspectRadian) * EXPECTED_SCALE;
                        mTextureScalers[position][SCALE_HEIGHT] = (float) Math.sin(aspectRadian) * EXPECTED_SCALE;
                        Log.e(TAG, String.format("[uploadTexture] position = %d, bitmapSize = (%f, %f), aspectRadian = %f, scale = (%f, %f)",
                                position, imageWidth, imageHeight, aspectRadian, mTextureScalers[position][SCALE_WIDTH], mTextureScalers[position][SCALE_HEIGHT]));
                    }
                    
                    {// 上传纹理
                        GLES11.glGenTextures(1, mTextureIDs, position);
                        GLES11.glBindTexture(GLES11.GL_TEXTURE_2D, mTextureIDs[position]);
                        GLES11.glTexParameterx(GLES11.GL_TEXTURE_2D, GLES11.GL_TEXTURE_WRAP_S, GLES11.GL_CLAMP_TO_EDGE);
                        GLES11.glTexParameterx(GLES11.GL_TEXTURE_2D, GLES11.GL_TEXTURE_WRAP_T, GLES11.GL_CLAMP_TO_EDGE);
                        GLES11.glTexParameterx(GLES11.GL_TEXTURE_2D, GLES11.GL_TEXTURE_MIN_FILTER, GLES11.GL_LINEAR);
                        GLES11.glTexParameterx(GLES11.GL_TEXTURE_2D, GLES11.GL_TEXTURE_MAG_FILTER, GLES11.GL_LINEAR);
                        GLUtils.texImage2D(GLES11.GL_TEXTURE_2D, 0, bitmap, 0);
                        bitmap.recycle();
                    }
                    
                    // 获取OpenGLES中的上传纹理可能发生的错误
                    printGLError("displayBitmap");
                    
                    // 纹理上传完成后,通知OpenGLES去重绘一帧,以便绘制已上传的纹理
                    mHostView.requestRender();
                }
            });
        }
    }
    
    /**
     * Bitmap asynchronous Loader
     * @author Cocoonshu@Plf.MediaCenter.Gallery
     * @date   2016-06-29 19:46:29
     */
    private class BitmapLoader extends AsyncTask<String, Integer, Void> {
        private Resources     mResource = null;
        private ImageRenderer mRenderer = null;
        
        public BitmapLoader(Resources resource, ImageRenderer renderer) {
            mResource = resource;
            mRenderer = renderer;
        }
        
        @Override
        protected Void doInBackground(String... imageAssetPaths) {
            if (imageAssetPaths != null) {
                for (int i = 0; i < imageAssetPaths.length; i++) {
                    String assetPath = imageAssetPaths[i];
                    Bitmap bitmap    = decodeImage(assetPath);
                    mRenderer.displayBitmap(bitmap, i);
                }
            }
            return null;
        }
        
        public Bitmap decodeImage(String assetPath) {
            try {
                AssetManager assetManager = mResource.getAssets(); 
                InputStream  inputStream  = assetManager.open(assetPath);
                Bitmap       bitmap       = BitmapFactory.decodeStream(inputStream);
                return bitmap;
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
}

相关文章

网友评论

      本文标题:[GLES] 最简固定管线GL程序

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