OpenGL ES 2.0 完全入门(二):矩形、图片、读取显存等

本文介绍如何使用OpenGL ES绘制矩形及图片纹理,包括顶点数据组织、索引缓冲使用、纹理加载及Shader编程等关键技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 绘制矩形

        上篇中有提到,三角形是基本形状,利用三角形我们可以“拼出”其他的任何形状,例如矩形。

        绘制两个三角形时,我们可以指定 6 个顶点的坐标,但实际上只有 4 个不同的点,这样有点浪费,OpenGL 支持用另一种方式完成绘制:用一个数组保存顶点数据,用另一个数组保存顶点的绘制顺序:

private static final float[] VERTEX = {   // in counterclockwise order:
        1, 1, 0,   // top right
        -1, 1, 0,  // top left
        -1, -1, 0, // bottom left
        1, -1, 0,  // bottom right
};
private static final short[] VERTEX_INDEX = { 0, 1, 2, 0, 2, 3 };

private final ShortBuffer mVertexIndexBuffer;

MyRenderer() {
    mVertexBuffer = ByteBuffer.allocateDirect(VERTEX.length * 4)
            .order(ByteOrder.nativeOrder())
            .asFloatBuffer()
            .put(VERTEX);
    mVertexBuffer.position(0);

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

          在上面的代码中,VERTEX 保存了 4 个顶点的坐标,VERTEX_INDEX 保存了顶点的绘制顺序。0 -> 1 -> 2 绘制的是 右上 -> 左上 -> 左下 上半个三角形,逆时针方向,而 0 -> 2 -> 3 则绘制的是 右上 -> 左下 -> 右下 下半个三角形,也是逆时针方向,这两个三角形则“拼接”成了一个矩形。

        顶点的绘制顺序重不重要?由于这里绘制的是纯颜色,看不出区别,在下面绘制图片纹理的时候,我发现,调换顺序似乎并没有影响,绘制的图片没有变化。

        shader 代码、投影变换的逻辑都不需要更改(但为了让矩形能完整显示,我们把 translateM 移动的 z 值设为 -5f),我们只需要改一下绘制时调用的函数即可:

@Override
public void onDrawFrame(GL10 unused) {
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

    GLES20.glUniformMatrix4fv(mMatrixHandle, 1, false, mMVPMatrix, 0);

    // 用 glDrawElements 来绘制,mVertexIndexBuffer 指定了顶点绘制顺序
    GLES20.glDrawElements(GLES20.GL_TRIANGLES, VERTEX_INDEX.length,
            GLES20.GL_UNSIGNED_SHORT, mVertexIndexBuffer);
}

        绘制效果图:

 2. 绘制图片纹理

         在绘制了矩形的基础上,我们更进一步,不再满足于绘制纯色纹理,而是绘制图片纹理。

        2.1. 加载图片

         首先我们需要加载图片并且保存在 OpenGL 纹理系统中:

public void onSurfaceCreated(GL10 unused, EGLConfig config) {
    // ...
    
    int[] texNames = new int[1];
    GLES20.glGenTextures(1, texNames, 0);
    mTexName = texNames[0];
    Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(),
            R.drawable.p_300px);
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexName);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
            GLES20.GL_LINEAR);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
            GLES20.GL_LINEAR);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
            GLES20.GL_REPEAT);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
            GLES20.GL_REPEAT);
    GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
    bitmap.recycle();
}

2.2. shader 代码 

        此时,我们的 shader 代码当然也需要进行更改了:

private static final String VERTEX_SHADER =
        "uniform mat4 uMVPMatrix;" +
        "attribute vec4 vPosition;" +
        "attribute vec2 a_texCoord;" +
        "varying vec2 v_texCoord;" +
        "void main() {" +
        "  gl_Position = uMVPMatrix * vPosition;" +
        "  v_texCoord = a_texCoord;" +
        "}";
private static final String FRAGMENT_SHADER =
        "precision mediump float;" +
        "varying vec2 v_texCoord;" +
        "uniform sampler2D s_texture;" +
        "void main() {" +
        "  gl_FragColor = texture2D(s_texture, v_texCoord);" +
        "}";

         这里出现了更多的关键字,uniformattributevarying,GLSL 并不是我关注的重点,不过这三者的区别可以看看 https://blog.csdn.net/EveryDayOneHour/article/details/122431249,讲的非常清晰易懂:

        uniform 由外部程序传递给 shader,就像是C语言里面的常量,shader 只能用,不能改;attribute 是只能在 vertex shader 中使用的变量;varying 变量是 vertex 和 fragment shader 之间做数据传递用的。

 2.3. 绘制

        首先我们需要指定截取纹理的哪一部分绘制到图形上:

private static final float[] TEX_VERTEX = {   // in clockwise order:
        1, 0,  // bottom right
        0, 0,  // bottom left
        0, 1,  // top left
        1, 1,  // top right
};
private final ShortBuffer mVertexIndexBuffer;

MyRenderer(final Context context) {
    // ...

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

        接着我们需要修改初始化和绘制的代码:

@Override
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
    // ...

    mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
    mTexCoordHandle = GLES20.glGetAttribLocation(mProgram, "a_texCoord");
    mMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
    mTexSamplerHandle = GLES20.glGetUniformLocation(mProgram, "s_texture");

    GLES20.glEnableVertexAttribArray(mPositionHandle);
    GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false,
            12, mVertexBuffer);

    GLES20.glEnableVertexAttribArray(mTexCoordHandle);
    GLES20.glVertexAttribPointer(mTexCoordHandle, 2, GLES20.GL_FLOAT, false, 0,
            mTexVertexBuffer);

    // ...
}

@Override
public void onDrawFrame(GL10 unused) {
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

    GLES20.glUniformMatrix4fv(mMatrixHandle, 1, false, mMVPMatrix, 0);
    GLES20.glUniform1i(mTexSamplerHandle, 0);

    // 用 glDrawElements 来绘制,mVertexIndexBuffer 指定了顶点绘制顺序
    GLES20.glDrawElements(GLES20.GL_TRIANGLES, VERTEX_INDEX.length,
            GLES20.GL_UNSIGNED_SHORT, mVertexIndexBuffer);
}

        绘制效果如下:

https://blog.piasy.com/2016/06/14/Open-gl-es-android-2-part-2/index.html 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值