前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[ - OpenGLES3.0 - ] 第一集 主线 - 打开新世界的大门

[ - OpenGLES3.0 - ] 第一集 主线 - 打开新世界的大门

作者头像
张风捷特烈
发布2020-04-30 14:47:21
6171
发布2020-04-30 14:47:21
举报

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

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

  • [- 多媒体 -] OpenGLES3.0 接入视频实现特效 - 引言
  • [ - OpenGLES3.0 - ] 第一集 主线 - 打开新世界的大门
  • [ - OpenGLES3.0 - ] 第二集 主线 - 绘制面与图片贴图
  • [ - OpenGLES3.0 - ] 第三集 主线 - shader着色器与图片特效
  • [ - OpenGLES3.0 - ] 第四集 支线1 - 相机接入OpenGLES3.0实现特效
  • [ - OpenGLES3.0 - ] 第五集 支线1 - 视频接入OpenGLES3.0实现特效
  • [ - OpenGLES3.0 - ] 第六集 主线 - OpenGL视口详解与矩阵变换(上篇)
  • [ - OpenGLES3.0 - ] 第七集 主线 - OpenGL视口详解与矩阵变换(下篇)
  • [ - OpenGLES3.0 - ] 第八集 支线2 - 复杂面的绘制
  • [ - OpenGLES3.0 - ] 第九集 支线2 - 立体图形的绘制
  • [ - OpenGLES3.0 - ] 第十集 支线2 - OpenGLES展现建模软件3D模型

1.黑屏的实现
1.1 GLSurfaceView的使用

Android中OpenGL通过GLSurfaceView进行展现,实现Renderer接口 实现接口方法:onSurfaceCreatedonSurfaceChangedonDrawFrame

代码语言:javascript
复制
public class GLWorld extends GLSurfaceView implements GLSurfaceView.Renderer {
    private static final String TAG = "GLWorld";
    public GLWorld(Context context) {
        this(context,null);
    }

    public GLWorld(Context context, AttributeSet attrs) {
        super(context, attrs);
        setEGLContextClientVersion(3);//设置OpenGL ES 3.0 context
        setRenderer(this);//设置渲染器
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        Log.e(TAG, "onSurfaceCreated: " );
        GLES30.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);//rgba
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        Log.e(TAG, "onSurfaceChanged: " );
        GLES30.glViewport(0, 0, width, height);//设置GL视口
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        Log.e(TAG, "onDrawFrame: " );
    }
}

日志如下: 默认onDrawFrame约隔16ms会不断刷新

代码语言:javascript
复制
2020-01-10 13:34:35.368 3934-4035/com.toly1994.tolygl E/GLWorld: onSurfaceCreated: 
2020-01-10 13:34:35.368 3934-4035/com.toly1994.tolygl E/GLWorld: onSurfaceChanged: 
2020-01-10 13:34:35.368 3934-4035/com.toly1994.tolygl E/GLWorld: onDrawFrame: 
2020-01-10 13:34:35.440 3934-4035/com.toly1994.tolygl E/GLWorld: onDrawFrame: 
2020-01-10 13:34:35.458 3934-4035/com.toly1994.tolygl E/GLWorld: onDrawFrame: 
2020-01-10 13:34:35.461 3934-4035/com.toly1994.tolygl E/GLWorld: onDrawFrame: 

1.2 GLWorld的使用

它就相当于一个View,现在直接放到setContentView中,便可以显示

代码语言:javascript
复制
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(GLWorld(this))
    }
}
复制代码

2.从点线开始说起

现在你对OpenGLES的认识就像眼前的黑屏一样,一无所知 我们需要去点亮它,展现出一个新世界,大门即将打开,请扶好站稳。 这道光,你扎住了,就已经赢了一大半,抓不住,就......算了吧


2.1 GLPoint的编写

为了不混乱和方便使用,创建一个GLPoint类负责点的绘制测试

代码语言:javascript
复制
[1] 准备顶点着色代码和片段着色代码
[2] 准备顶点和颜色数据
[3] 加载着色器代码并初始化程序
[4] 绘制逻辑 (添加程序->启用顶点->绘制)
代码语言:javascript
复制
public class GLPoint {
    //顶点着色代码
    final String vsh = "#version 300 es\n" +
            "layout (location = 0) in vec3 aPosition; \n" +
            "layout (location = 1) in vec4 aColor;\n" +
            "\n" +
            "out vec4 color2frag;\n" +
            "\n" +
            "void main(){\n" +
            "    gl_Position = vec4(aPosition.x,aPosition.y, aPosition.z, 1.0);\n" +
            "    color2frag = aColor;\n" +
            "gl_PointSize=10.0;"+
            "}";
    
    //片段着色代码
    final String fsh = "#version 300 es\n" +
            "precision mediump float;\n" +
            "out vec4 outColor;\n" +
            "in vec4 color2frag;\n" +
            "\n" +
            "void main(){\n" +
            "    outColor = color2frag;\n" +
            "}";

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

    private int program;
    private static final int VERTEX_DIMENSION = 3;
    private static final int COLOR_DIMENSION = 4;
    
    private FloatBuffer vertBuffer;
    private FloatBuffer colorBuffer;

    private  int aPosition =0;//位置的句柄
    private  int aColor =1;//颜色的句柄

    public GLPoint() {
        program = initProgram();
        vertBuffer= GLBuffer.getFloatBuffer(vertexes);
        colorBuffer= GLBuffer.getFloatBuffer(colors);
    }

    private int initProgram() {
        int program;
        ////顶点shader代码加载
        int vertexShader = GLLoader.loadShader(GLES30.GL_VERTEX_SHADER, vsh);
        //片段shader代码加载
        int fragmentShader = GLLoader.loadShader(GLES30.GL_FRAGMENT_SHADER, fsh);
        program = GLES30.glCreateProgram();//创建空的OpenGL ES 程序
        GLES30.glAttachShader(program, vertexShader);//加入顶点着色器
        GLES30.glAttachShader(program, fragmentShader);//加入片元着色器
        GLES30.glLinkProgram(program);//创建可执行的OpenGL ES项目
        return program;
    }

    public void draw(){
        //清除颜色缓存和深度缓存
        GLES30.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
        // 将程序添加到OpenGL ES环境中
        GLES30.glUseProgram(program);
        //启用顶点句柄
        GLES30.glEnableVertexAttribArray(aPosition);
        //启用颜色句柄
        GLES30.glEnableVertexAttribArray(aColor);
        //准备坐标数据
        GLES30.glVertexAttribPointer(
                aPosition, VERTEX_DIMENSION,
                GLES30.GL_FLOAT, false,
                VERTEX_DIMENSION * 4, vertBuffer);

        //准备颜色数据
        GLES30.glVertexAttribPointer(
                aColor, COLOR_DIMENSION,
                GLES30.GL_FLOAT, false,
                COLOR_DIMENSION * 4, colorBuffer);

        //绘制点
        GLES30.glDrawArrays(GLES30.GL_POINTS, 0, vertexes.length / VERTEX_DIMENSION);
        
        //禁用顶点数组
        GLES30.glDisableVertexAttribArray(aPosition);
        GLES30.glDisableVertexAttribArray(aColor);
    }

}
复制代码

2.2 缓冲数据

float数组需要通过FloatBuffer进行缓冲,以提高效率

代码语言:javascript
复制
/**
 * float数组缓冲数据
 *
 * @param vertexs 顶点
 * @return 获取浮点形缓冲数据
 */
public static FloatBuffer getFloatBuffer(float[] vertexs) {
    FloatBuffer buffer;
    ///每个浮点数:坐标个数* 4字节
    ByteBuffer qbb = ByteBuffer.allocateDirect(vertexs.length * 4);
    //使用本机硬件设备的字节顺序
    qbb.order(ByteOrder.nativeOrder());
    // 从字节缓冲区创建浮点缓冲区
    buffer = qbb.asFloatBuffer();
    // 将坐标添加到FloatBuffer
    buffer.put(vertexs);
    //设置缓冲区以读取第一个坐标
    buffer.position(0);
    return buffer;
}

2.3 使用

在我们的GLWorld中创建GLPoint对象,在onDrawFrame中绘制即可

代码语言:javascript
复制
public class GLWorld extends GLSurfaceView implements GLSurfaceView.Renderer {
    private static final String TAG = "GLWorld";
    private GLPoint glPoint;
    public GLWorld(Context context) {
        this(context,null);
    }
    
    public GLWorld(Context context, AttributeSet attrs) {
        super(context, attrs);
        setEGLContextClientVersion(3);//设置OpenGL ES 3.0 context
        setRenderer(this);//设置渲染器
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
         glPoint = new GLPoint();
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES30.glViewport(0, 0, width, height);//设置GL视口
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        glPoint.draw();
    }
}

2.4 多点绘制

现在绘制点,通过更改顶点和颜色即可

代码语言:javascript
复制
    //顶点数组
    private final float vertexes[] = {   //以逆时针顺序
            0.0f, 0.0f,0.0f,//原点
            0.5f,0.0f,0.0f,
            0.5f,0.5f,0.0f,
            0.0f,0.5f,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,//蓝色
    };

2.5 画线

前面处理好了,划线比较简单

代码语言:javascript
复制
//绘制
GLES30.glLineWidth(10);
//GLES30.glDrawArrays(GLES30.GL_POINTS, 0, vertexes.length / VERTEX_DIMENSION);
//GLES30.glDrawArrays(GLES30.GL_LINES, 0, vertexes.length / VERTEX_DIMENSION);
//GLES30.glDrawArrays(GLES30.GL_LINE_LOOP, 0, vertexes.length / VERTEX_DIMENSION);
GLES30.glDrawArrays(GLES30.GL_LINE_STRIP, 0, vertexes.length / VERTEX_DIMENSION);

注:着色器的代码会有单独一篇进行讲解,此处暂不做解释。


3.线绘制与比例校正

可能你已经发现了,本应是个正方形,可现实成了矩形 原因在于视口的比例不对,现在是这样个坐标系:


3.1 GLLine添加顶点变换矩阵

在顶点着色器代码中添加用于变换的矩阵uMVPMatrix

代码语言:javascript
复制
//顶点着色代码
final String vsh = "#version 300 es\n" +
        "layout (location = 0) in vec3 aPosition; \n" +
        "layout (location = 1) in vec4 aColor;\n" +
        "uniform mat4 uMVPMatrix;\n" +
        "out vec4 color2frag;\n" +
        "\n" +
        "void main(){\n" +
        "    gl_Position = uMVPMatrix*vec4(aPosition.x,aPosition.y, aPosition.z, 1.0);\n" +
        "    color2frag = aColor;\n" +
        "gl_PointSize=10.0;"+
        "}";

---->[变换矩阵相关代码]----
private  int uMVPMatrix ;//顶点变换矩阵句柄

//构造方法中获取句柄
uMVPMatrix = GLES30.glGetUniformLocation(program, "uMVPMatrix");

public void draw(float[] mvpMatrix){
   //英雄所见
   GLES30.glUseProgram(program);
   GLES30.glUniformMatrix4fv(uMVPMatrix, 1, false,mvpMatrix, 0);

3.2 GLWorld添加变换矩阵逻辑

具体的变换细节,将在第六集第七集讲述,此处不做解释 现在视角就已经校正了

代码语言:javascript
复制
public class GLWorld extends GLSurfaceView implements GLSurfaceView.Renderer {
    private static final String TAG = "GLWorld";
    private GLLine line;
    //Model View Projection Matrix--模型视图投影矩阵
    private final float[] mMVPMatrix = new float[16];
    //投影矩阵 mProjectionMatrix
    private final float[] mProjectionMatrix = new float[16];
    //视图矩阵 mViewMatrix
    private final float[] mViewMatrix = new float[16];
    
    public GLWorld(Context context) {
        this(context, null);
    }
    public GLWorld(Context context, AttributeSet attrs) {
        super(context, attrs);
        setEGLContextClientVersion(3);//设置OpenGL ES 3.0 context
        setRenderer(this);//设置渲染器
    }
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        line = new GLLine();
    }
    
    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES30.glViewport(0, 0, width, height);//设置GL视口
        float ratio = (float) width / height;
        //透视投影矩阵--截锥
        Matrix.frustumM(mProjectionMatrix, 0,
                -ratio, ratio, -1, 1,
                3, 7);
        // 设置相机位置(视图矩阵)
        Matrix.setLookAtM(mViewMatrix, 0,
                0, 0, 4,
                0f, 0f, 0f,
                0f, 1.0f, 0.0f);
    }
    
    @Override
    public void onDrawFrame(GL10 gl) {
        Matrix.multiplyMM(
                mMVPMatrix, 0,
                mProjectionMatrix, 0,
                mViewMatrix, 0);
        line.draw(mMVPMatrix);
    }
}

4.精简和优化
4.1 着色器shader的独立文件

着色器shader是OpenGL灵魂般的存在,所以直接写在代码里肯定不太好 一般放在assets文件夹里,另外值得一提的是AS的着色器代码高亮显示插件 个人习惯片段用.fsh的后缀名,顶点用.vsh的后缀名


4.2 读取资源文件并加载程序方法

这些通用的不变的操作可以提取出来进行复用

代码语言:javascript
复制
public static  int initProgramByAssets(Context ctx, String vertName, String fragName){
    int program=-1;
    ////顶点着色
    int vertexShader = loadShaderAssets(ctx, GLES30.GL_VERTEX_SHADER, vertName);
    //片元着色
    int fragmentShader = loadShaderAssets(ctx, GLES30.GL_FRAGMENT_SHADER, fragName);
    program = GLES30.glCreateProgram();//创建空的OpenGL ES 程序
    GLES30.glAttachShader(program, vertexShader);//加入顶点着色器
    GLES30.glAttachShader(program, fragmentShader);//加入片元着色器
    GLES30.glLinkProgram(program);//创建可执行的OpenGL ES项目
    return program;
}
//从sh脚本中加载shader内容的方法
private static int loadShaderAssets(Context ctx, int type, String name) {
    byte[] buf = new byte[1024];
    StringBuilder sb = new StringBuilder();
    int len;
    try (InputStream is = ctx.getAssets().open(name)) {
        while ((len = is.read(buf)) != -1) {
            sb.append(new String(buf, 0, len));
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return loadShader(type, sb.toString());
}

使用时直接加载即可,这样GLLine的内容就比较精简了。

代码语言:javascript
复制
program = GLLoader.initProgramByAssets(context, "base.vsh", "base.fsh");

这样也能正常表现,以后对着色器代码的修改就是家常便饭。

本篇为你介绍了OpenGLES的基础使用,旨在为你打开一扇OpenGLES的大门,其中很多细节一言蔽之,后面会一一道来。所以这一篇可以不求甚解,跑出来就算你成功了。下一篇将会为你介绍面的绘制和贴图,这是OpenGLES和图片关联起来的重要部分。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020年01月10日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.黑屏的实现
    • 1.1 GLSurfaceView的使用
      • 1.2 GLWorld的使用
      • 2.从点线开始说起
        • 2.1 GLPoint的编写
          • 2.2 缓冲数据
            • 2.3 使用
              • 2.4 多点绘制
                • 2.5 画线
                • 3.线绘制与比例校正
                  • 3.1 GLLine添加顶点变换矩阵
                    • 3.2 GLWorld添加变换矩阵逻辑
                    • 4.精简和优化
                      • 4.1 着色器shader的独立文件
                        • 4.2 读取资源文件并加载程序方法
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档