Android OpenGL ES:展示一张2d图片

前言

本文的环境和条件:GLSurfaceView + OpenGL ES 2.0

代码结构

  1. Activity视图使用GLSurfaceView;
  2. GLSurfaceView使用自定义GLSurfaceView.Render实现,(GLSurfaceView.setRender())
  3. 在Render实现图片纹理化和渲染。

代码Show you the code

自定义的GLSurfaceView.Renderer实现。

public class BitmapRender implements GLSurfaceView.Renderer {

    private static final String VERTEX_SHADER =
            "uniform mat4 uMVPMatrix;" +
                    "attribute vec4 aPosition;" +
                    "attribute vec2 aTexCoord;" +
                    "varying vec2 vTexCoord;" +
                    "void main() {" +
                    "  gl_Position = uMVPMatrix * aPosition;" +
                    "  vTexCoord = aTexCoord;" +
                    "}";
    private static final String FRAGMENT_SHADER =
            "precision lowp float;" +
                    "precision highp sampler2D;" +
                    "precision highp int;" +
                    "uniform sampler2D uTextureUnit;" +
                    "varying vec2 vTexCoord;" +
                    "void main() {" +
                    "  gl_FragColor = texture2D(uTextureUnit, vTexCoord);" +
                    "}";
    private static final int COORDS_PRE_VERTEX = 2;
    private static final int COORDS_PRE_TEXTURE = 2;
    // 顶点坐标:世界坐标系
    private static final float[] VERTEX = {
            1, 1,  // right, top
            -1, 1, // left, top
            1, -1, // right, bottom
            -1, -1 // left, bottom
    };

    // 纹理坐标
    private static final float[] TEXTUER = {
            1, 0.f, // right, top
            0.f, 0.f, // left, top
            1, 1, // right, bottom
            0.f, 1, // left, bottom
    };
    private FloatBuffer mVertexBuffer;
    private FloatBuffer mTextureBuffer;
    private float[] mMVPMatrix = new float[16];
    private int mProgram;
    private int mMVPMatrixHandle;
    private int mPositionHandle;
    private int mTexCoordHandle;
    private Bitmap bitmap;
    private int mBitmapWidth;
    private int mBitmapHeight;
    private int texUnitHandle;
    private int imageTexture = -1;

    public BitmapRender(String imagePath) {
        mVertexBuffer = GLESUtils.createFloatBuffer(VERTEX);
        mTextureBuffer = GLESUtils.createFloatBuffer(TEXTUER);
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        //GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);

        int vertexShader = GLESUtils.createVertexShader(VERTEX_SHADER);
        int fragmentShader = GLESUtils.createFragmentShader(FRAGMENT_SHADER);
        mProgram = GLESUtils.createProgram(vertexShader, fragmentShader);
        // GLES20.glGetAttribLocation方法:获取着色器程序中,指定为attribute类型变量的id。
        mPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
        // GLES20.glGetUniformLocation方法:获取着色器程序中,指定为uniform类型变量的id
        mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
        mTexCoordHandle = GLES20.glGetAttribLocation(mProgram, "aTexCoord");
        texUnitHandle = GLES20.glGetUniformLocation(mProgram, "uTextureUnit");
    }

    int viewWidth;
    int viewHeight;

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES20.glViewport(0, 0, width, height);
        this.viewWidth = width;
        this.viewHeight = height;
    }

    public void setBitmap(Bitmap bitmap) {
        this.bitmap = bitmap;
        mBitmapWidth = bitmap.getWidth();
        mBitmapHeight = bitmap.getHeight();
        
        mVertexBuffer = GLESUtils.createFloatBuffer(VERTEX);
        mTextureBuffer = GLESUtils.createFloatBuffer(TEXTUER);

        mMVPMatrix = GLESUtils.changeMVPMatrixInside(viewWidth, viewHeight, mBitmapWidth, mBitmapHeight);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
        drawBitmap();

        // 绘制完毕,关闭顶点数据数组、解除纹理绑定等
        GLES20.glDisableVertexAttribArray(mPositionHandle);
        GLES20.glDisableVertexAttribArray(mTexCoordHandle);
        GLES20.glUseProgram(0);
    }

    private void drawBitmap() {
        if (bitmap != null) {
            // 创建图片纹理
            imageTexture = GLESUtils.createImageTexture(bitmap);
        } else {
            return;
        }

        GLES20.glUseProgram(mProgram);
        // 设置顶点变换矩阵数据
        GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);
        // 设置顶点坐标
        GLES20.glEnableVertexAttribArray(mPositionHandle);
        // 为顶点属性赋值
        GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PRE_TEXTURE, GLES20.GL_FLOAT, true, 0, mVertexBuffer);

        // 设置纹理坐标
        GLES20.glEnableVertexAttribArray(mTexCoordHandle);
        // 纹理坐标数据
        GLES20.glVertexAttribPointer(mTexCoordHandle, COORDS_PRE_TEXTURE, GLES20.GL_FLOAT, true, 0, mTextureBuffer);
        // 激活纹理单元
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        // 绑定纹理
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, imageTexture);
        // 把选定的纹理单元传给片段着色器:0对应激活的GL_TEXTURE0
        // 完成纹理id和采样器的关联绑定
        GLES20.glUniform1i(texUnitHandle, 0);

        // 图形绘制
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, VERTEX.length / COORDS_PRE_VERTEX);
    }

}

工具类GLESUtils

/**
 * @description: OpenGL ES工具类
 * @author: MichaelX
 * @date: 2021/1/14
 */
public class GLESUtils {
    public static final float[] IDENTITY_MATRIX = new float[16];
    public static final int SIZE_OF_FLOAT = 4;
    private static final String TAG = "GLESUtils";
	
	// 根据坐标创建FloatBuffer数据
    public static FloatBuffer createFloatBuffer(float[] coords) {
        ByteBuffer bb = ByteBuffer.allocateDirect(coords.length * SIZE_OF_FLOAT);
        bb.order(ByteOrder.nativeOrder());
        FloatBuffer fb = bb.asFloatBuffer();
        fb.put(coords);
        fb.position(0);
        return fb;
    }

	// 创建顶点shader
    public static int createVertexShader(String vertexShader) {
        return createShader(GLES20.GL_VERTEX_SHADER, vertexShader);
    }
	// 创建片元shader
    public static int createFragmentShader(String fragmentShader) {
        return createShader(GLES20.GL_FRAGMENT_SHADER, fragmentShader);
    }

	// 创建shader的通用方法
    private static int createShader(int type, String shaderScript) {
        int shader = GLES20.glCreateShader(type);
        GLES20.glShaderSource(shader, shaderScript);
        // 编译shader
        GLES20.glCompileShader(shader);
        int[] compileStatus = new int[1];
		// 检测编译结果
        GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
        if (compileStatus[0] == GLES20.GL_FALSE) {
            Log.e(TAG, "compile shader: " + type + ", error: " + GLES20.glGetShaderInfoLog(shader));
            GLES20.glDeleteShader(shader);
            shader = 0;
        }
        return shader;
    }

	// 创建program
    public static int createProgram(int vertexShader, int fragmentShader) {
        if (vertexShader == 0 || fragmentShader == 0) {
            Log.e(TAG, "shader can't be 0!");
            return 0;
        }
        int program = GLES20.glCreateProgram();
        checkGlError("glCreateProgram");
		// 关联顶点着色器
        GLES20.glAttachShader(program, vertexShader);
        checkGlError("glAttachShader");
		// 管理片元着色器
        GLES20.glAttachShader(program, fragmentShader);
        checkGlError("glAttachShader");
        // 链接整个管线流程
        GLES20.glLinkProgram(program);
        int linkStatus[] = new int[1];
		// 检查链接状态
        GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
        if (linkStatus[0] != GLES20.GL_TRUE) {
            Log.e(TAG, "link program error: " + GLES20.glGetProgramInfoLog(program));
            GLES20.glDeleteProgram(program);
            program = 0;
        }
        return program;
    }

    private static void checkGlError(String op) {
        int error = GLES20.glGetError();
        if (error != GLES20.GL_NO_ERROR) {
            String msg = op + ": glError 0x" + Integer.toHexString(error);
            Log.e(TAG, "checkGlError: " + msg);
        }
    }

	// 利用图片创建纹理id
    public static int createImageTexture(Bitmap bitmap) {
        int[] texture = new int[1];
        if (bitmap != null && !bitmap.isRecycled()) {
            //生成纹理
            GLES20.glGenTextures(1, texture, 0);
            //绑定纹理
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture[0]);
 
			/**配置纹理属性,主要是纹理采样边缘的处理**/
            //设置缩小过滤为使用纹理中坐标最接近的一个像素的颜色作为需要绘制的像素颜色
            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);
            //设置环绕方向S,截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
            //设置环绕方向T,截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
 
            //根据以上指定的参数,生成一个2D纹理。上传图片数据到纹理对象中
            GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);

            // 生成 MIP 贴图
            GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D);
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, GLES20.GL_NONE);

            return texture[0];
        }
        return 0;
    }

    public static float[] changeMVPMatrixInside(float viewWidth, float viewHeight, float textureWidth, float textureHeight) {
        float scale = viewWidth * textureHeight / viewHeight / textureWidth;
        float[] mvp = new float[16];
        Matrix.setIdentityM(mvp, 0);
        Matrix.scaleM(mvp, 0, scale > 1 ? (1F / scale) : 1F, scale > 1 ? 1F : scale, 1F);
        return mvp;
    }

    public static float[] changeMvpMatrixCrop(float viewWidth, float viewHeight, float textureWidth, float textureHeight) {
        float scale = viewWidth * textureHeight / viewHeight / textureWidth;
        float[] mvp = new float[16];
        Matrix.setIdentityM(mvp, 0);
        Matrix.scaleM(mvp, 0, scale > 1 ? 1F : (1F / scale), scale > 1 ? scale : 1F, 1F);
        return mvp;
    }
	
	// 获取设备支持的OpenGL ES版本号
    public static int getSupportGLVersion(Context context) {
        final ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
        int version = configurationInfo.reqGlEsVersion >= 0x30000 ? 3 : 2;
        String glEsVersion = configurationInfo.getGlEsVersion();
        Log.d(TAG, "reqGlEsVersion: " + Integer.toHexString(configurationInfo.reqGlEsVersion)
                + ", glEsVersion: " + glEsVersion + ", return: " + version);
        return version;
    }
}

注意事项

很多示例代码没有上面的mMVPMatrixHandlemMVPMatrix,这个是为了不让图片展示变形的mvp矩阵,否则会因为坐标系的转换而显示图片宽高比例变形。

  • 0
    点赞
  • 1
    收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:编程工作室 设计师:CSDN官方博客 返回首页
评论 1

打赏作者

MichaelX_Blog

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值