[ - OpenGLES3.0 - ] 第二集 主线 - 绘制面与图片贴图

2,861 阅读7分钟

问:学OpenGL能干嘛? 答: 为所欲为。

说起OpenGLES,大家可能都敬而远之,其实它并没有想象中的那么可怕,当然也并没有那么容易
都0202年了,本系列使用OpenGLES3.0,这是一次有预谋的计划:

这是正文的第二篇,在上一篇讲述了OpenGLES的基本使用
现在你已经能够操作着色器绘制点线了,如果你还不会,请先看第一集


1.三角形绘制

三角形是OpenGL中最重要的一种图形,可以说所有的体和面都是由三角形拼组而成
所有这一节是非常重要的。


1.1 三点绘制三角形:GL_TRIANGLES

目前的坐标系如下: 三个点从右上角开始逆时针,白、红、绿

//顶点数组
private final float vertexes[] = {//以逆时针顺序
        1.0f, 1.0f, 0.0f,
        -1.0f, 1.0f, 0.0f,
        -1.0f, -1.0f, 0.0f,
};
// 颜色数组
private final float colors[] = new float[]{
        1.0f, 1.0f, 1.0f, 1.0f,//白色
        1.0f, 0.0f, 0.0f, 1.0f,//红色
        0.0f, 1.0f, 0.0f, 1.0f,//绿色
};

由三点进行绘制三角形,绘制时使用GLES30.GL_TRIANGLES即可

GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, vertexes.length / VERTEX_DIMENSION);

1.2 三角形三种模式比较

绘制三角形有三种模式,另外两个是:GL_TRIANGLE_STRIPGL_TRIANGLE_STRIP
下面通过四个点进行对比演示

//顶点数组
private final float vertexes[] = {   //以逆时针顺序
        1.0f, 1.0f, 0.0f,//原点
        -1.0f, 1.0f, 0.0f,
        -1.0f, -1.0f, 0.0f,
        1.0f, -1.0f, 0.0f,
};
// 颜色数组
private final float colors[] = new float[]{
        1.0f, 1.0f, 1.0f, 1.0f,//白色
        1.0f, 0.0f, 0.0f, 1.0f,//红色
        0.0f, 1.0f, 0.0f, 1.0f,//绿色
        0.0f, 0.0f, 1.0f, 1.0f,//蓝色
};

glDrawArrays:
---->[绘制点线]-------
GLES20.GL_POINTS            绘制点
GLES20.GL_LINES             两点一线
GLES20.GL_LINE_STRIP        相邻两点一线(不连首尾)
GLES20.GL_LINE_LOOP         相邻两点一线(连首尾)

---->[绘制三角形]-------
GLES20.GL_TRIANGLES         三点一个(不够三个,被忽略)
GLES20.GL_TRIANGLE_STRIP    相邻三点一个
GLES20.GL_TRIANGLE_FAN      第一点中心,散射到其他点

矩形也就是两个三角形拼成的,所以现在绘制面的技能你已经get了。


2.圆的绘制

现在你应该有所体会,OpenGL中最重要的是处理顶点和颜色的数据
圆形的绘制无非就是找到那些顶点在哪里,根据三角函数很容易求得

下面的图很好的体现了这些点的坐标是如何确定的

private void initData() {
    //顶点坐标数据的初始化
    int verticeCount = splitCount + 2;
    vertexes = new float[verticeCount * 3];//坐标数据
    colors = new float[verticeCount * 4];//颜色数据
    float thta = 360.f / splitCount;
    vertexes[0] = 0;
    vertexes[1] = 0;
    vertexes[2] = 0;
    colors[0] = 1;
    colors[1] = 1;
    colors[2] = 1;
    colors[3] = 1;
    
    for (int n = 1; n <= verticeCount - 1; n++) {
        vertexes[n * 3] = r * cos((n - 1) * thta);//x
        vertexes[n * 3 + 1] = r * sin((n - 1) * thta);//y
        vertexes[n * 3 + 2] = 0;//z
        
        colors[4 * n] = 1;
        colors[4 * n + 1] = 0;
        colors[4 * n + 2] = 0;
        colors[4 * n + 3] = 1.0f;
    }
}

3.贴图的使用

没有贴图,就像肉包里没有肉馅。之前我们都是自定义颜色去给顶点着色
而贴图就是使用图形象的像素信息来给顶点着色,get贴图技能之后,
你就可以用OpenGLES 对图片进行处理和展示,甚至保存。这也是支线1的基础


3.1 贴图纹理坐标

要注意,贴图的纹理坐标系是一个二维系,原点在左上角,注意和顶点系区分
下面是顶点系xoy面纹理系的示意图,我们需要给出纹理坐标,就可以把图片贴起来:

点位坐标与纹理坐标.png

//顶点数组
private final float vertexes[] = {   //以逆时针顺序
        1.0f, 1.0f, 0.0f,//原点
        -1.0f, 1.0f, 0.0f,
        -1.0f, -1.0f, 0.0f,
        1.0f, -1.0f, 0.0f,
};
// 贴图坐标
private final float textureCoo[] = new float[]{
        1.0f,0.0f,
        0.0f,0.0f,
        0.0f,1.0f,
        1.0f,1.0f,
};

private static final int VERTEX_DIMENSION = 3;
private static final int TEXTURE_DIMENSION = 2;

3.2 图片纹理的加载

这里给出一个图片加载成贴图的工具类

//贴图工具类
public class GLTexture {
    /**
     * 资源id 加载纹理,默认重复方式:RepeatType.REPEAT
     *
     * @param ctx   上下文
     * @param resId 资源id
     * @return 纹理id
     */
    public static int loadTexture(Context ctx, int resId) {
        return loadTexture(ctx, resId, RepeatType.REPEAT);
    }
    /**
     * 图片加载纹理,默认重复方式:RepeatType.REPEAT
     * @param bitmap 图片
     * @return 纹理id
     */
    public static int loadTexture(Bitmap bitmap) {
        return loadTexture(bitmap, RepeatType.REPEAT);
    }
    /**
     * 资源id 加载纹理
     *
     * @param ctx        上下文
     * @param resId      资源id
     * @param repeatType 重复方式 {@link RepeatType}
     * @return 纹理id
     */
    public static int loadTexture(Context ctx, int resId, RepeatType repeatType) {
        Bitmap bitmap = BitmapFactory.decodeResource(ctx.getResources(), resId);
        return loadTexture(bitmap, repeatType);
    }
    /**
     * bitmap 加载纹理
     *
     * @param bitmap     bitmap
     * @param repeatType 重复方式 {@link RepeatType}
     * @return 纹理id
     */
    public static int loadTexture(Bitmap bitmap, RepeatType repeatType) {
        //生成纹理ID
        int[] textures = new int[1];
        //(产生的纹理id的数量,纹理id的数组,偏移量)
        GLES30.glGenTextures(1, textures, 0);
        int textureId = textures[0];
        //绑定纹理id
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureId);
        //采样方式MIN
        GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST);
        GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);
        int wrapS = 0;
        int wrapT = 0;
        switch (repeatType) {
            case NONE:
                wrapS = GLES30.GL_CLAMP_TO_EDGE;
                wrapT = GLES30.GL_CLAMP_TO_EDGE;
                break;
            case REPEAT_X:
                wrapS = GLES30.GL_REPEAT;
                wrapT = GLES30.GL_CLAMP_TO_EDGE;
                break;
            case REPEAT_Y:
                wrapS = GLES30.GL_CLAMP_TO_EDGE;
                wrapT = GLES30.GL_REPEAT;
                break;
            case REPEAT:
                wrapS = GLES30.GL_REPEAT;
                wrapT = GLES30.GL_REPEAT;
                break;
        }
        //设置s轴拉伸方式---重复
        GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, wrapS);
        //设置t轴拉伸方式---重复
        GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, wrapT);
        //实际加载纹理(纹理类型,纹理的层次,纹理图像,纹理边框尺寸)
        GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, bitmap, 0);
        bitmap.recycle();          //纹理加载成功后释放图片
        return textureId;
    }
}

enum RepeatType {
    NONE,//不重复
    REPEAT_X,//仅x轴重复
    REPEAT_Y,//仅y轴重复
    REPEAT//x,y重复
}

3.3 shader着色器更改

主要将vsh中颜色的输入变量缓存坐标变量,在fsh中通过texture函数获取色值

---->[texture.vsh]----
#version 300 es
layout (location = 0) in vec3 aPosition;
layout (location = 1) in vec2 aTexCoord;
uniform mat4 uMVPMatrix;

out vec2 vTexCoord;

void main(){
    gl_Position = uMVPMatrix*vec4(aPosition.x, aPosition.y, aPosition.z, 1.0);
    vTexCoord = aTexCoord;
}

---->[texture.fsh]----
#version 300 es
precision mediump float;
out vec4 outColor;

in vec2 vTexCoord;

uniform sampler2D sTexture;

void main(){
    outColor = texture(sTexture, vTexCoord);
}

3.4 代码的使用

主体和前面一样,这里用GLTextureTriangle类进行贴图测试

public class GLTextureTriangle {
    //顶点数组
    private final float vertexes[] = {   //以逆时针顺序
            1.0f, 1.0f, 0.0f,//原点
            -1.0f, 1.0f, 0.0f,
            -1.0f, -1.0f, 0.0f,
            1.0f, -1.0f, 0.0f,
    };

    // 贴图坐标
    private final float textureCoo[] = new float[]{
            1.0f,0.0f,
            0.0f,0.0f,
            0.0f,1.0f,
            1.0f,1.0f,
    };

    private int program;
    private static final int VERTEX_DIMENSION = 3;
    private static final int TEXTURE_DIMENSION = 2;

    private FloatBuffer vertBuffer;
    private FloatBuffer textureCooBuffer;

    private int aPosition = 0;//位置的句柄
    private int aTexCoord = 1;//颜色的句柄
    private int uMVPMatrix;//顶点变换矩阵句柄
    private int textureId;//贴图id


    public GLTextureTriangle(Context context) {
        textureId= GLTexture.loadTexture(context, R.mipmap.chaos);

        program = GLLoader.initProgramByAssets(context, "texture.vsh", "texture.fsh");
        vertBuffer = GLBuffer.getFloatBuffer(vertexes);
        textureCooBuffer = GLBuffer.getFloatBuffer(textureCoo);
        uMVPMatrix = GLES30.glGetUniformLocation(program, "uMVPMatrix");
    }

    public void draw(float[] mvpMatrix) {
        //清除颜色缓存和深度缓存
        GLES30.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);

        // 将程序添加到OpenGL ES环境中
        GLES30.glUseProgram(program);
        GLES30.glUniformMatrix4fv(uMVPMatrix, 1, false, mvpMatrix, 0);

        //启用三角形顶点的句柄
        GLES30.glEnableVertexAttribArray(aPosition);
        //启用三角形顶点颜色的句柄
        GLES30.glEnableVertexAttribArray(aTexCoord);
        //准备三角坐标数据
        GLES30.glVertexAttribPointer(
                aPosition, VERTEX_DIMENSION,
                GLES30.GL_FLOAT, false,
                VERTEX_DIMENSION * 4, vertBuffer);

        //准备顶点颜色数据
        GLES30.glVertexAttribPointer(
                aTexCoord, TEXTURE_DIMENSION,
                GLES30.GL_FLOAT, false,
                TEXTURE_DIMENSION * 4, textureCooBuffer);


        //绑定纹理
        GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureId);

        //绘制点
        GLES30.glLineWidth(10);
        GLES30.glDrawArrays(GLES30.GL_TRIANGLE_FAN, 0, vertexes.length / VERTEX_DIMENSION);
        //禁用顶点数组
        GLES30.glDisableVertexAttribArray(aPosition);
        GLES30.glDisableVertexAttribArray(aTexCoord);
    }
}

3.5 多纹理贴图

上面只是贴了一张图,那如何将多张图传入着色器呢?

#version 300 es
precision mediump float;
out vec4 outColor;

in vec2 vTexCoord;

uniform sampler2D uTexture;
uniform sampler2D uTexture2;//多加一个纹理量

const float uT=0.5;

void main(){
    vec4 color= texture(uTexture, vTexCoord);
    vec4 color2 = texture(uTexture2, vTexCoord);//从纹理中采样出颜色值2
    outColor = color*(1.0-uT) + color2*uT;// 混合两个颜色值

}

接下来在GLTextureTriangle里进行处理


---->[声明纹理id和句柄]----
private int textureId1;//贴图id
private int textureId2;//贴图id
private int uTexture;
private int uTexture2;

---->[构造函数中加载贴图获取句柄]----
textureId1 = GLTexture.loadTexture(context, R.mipmap.girl);
textureId2 = GLTexture.loadTexture(context, R.mipmap.chaos);
program = GLLoader.initProgramByAssets(context, "textures.vsh","textures.fsh");
uTexture = GLES30.glGetUniformLocation(program, "uTexture");
uTexture2 = GLES30.glGetUniformLocation(program, "uTexture2");

---->[绘制时绑定纹理,设置纹理位置]----
GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureId1);
GLES30.glUniform1i(uTexture, 0);
GLES30.glActiveTexture(GLES30.GL_TEXTURE1);
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureId2);
GLES30.glUniform1i(uTexture2, 1);

4. 着色器小试牛刀

也许你会觉得,废了这么大半天的劲就展示了一个图片,有什么意义?
这就像你给一个不懂编程的人用计算机算出10+10=20一样,他也会觉得没什么意义
但当你演示34564*9894=341976216他就会觉得很厉害。其实本质并没有什么区别
通过着色器的编写,你就可以完成你需要的特效,比如OpenGLES3.0 接入视频实现特效 - 引言

理论上你可以通过shader完成一切图片特效。下一篇将会详细介绍着色器代码的使用,你将会了解如何通过着色器的代码控制像素值以及像素的位置。本片就这样,相信你已经可以完成贴图了。


@张风捷特烈 2020.01.11 未允禁转
我的公众号:编程之王
联系我--邮箱:1981462002@qq.com --微信:zdl1994328
~ END ~