问:学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

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 contextsetRenderer(this);//设置渲染器}@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {Log.e(TAG, "onSurfaceCreated: " );GLES30.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);//rgba}@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {Log.e(TAG, "onSurfaceChanged: " );GLES30.glViewport(0, 0, width, height);//设置GL视口}@Overridepublic void onDrawFrame(GL10 gl) {Log.e(TAG, "onDrawFrame: " );}
}
复制代码

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

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中,便可以显示

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

2.从点线开始说起

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


2.1 GLPoint的编写

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

[1] 准备顶点着色代码和片段着色代码
[2] 准备顶点和颜色数据
[3] 加载着色器代码并初始化程序
[4] 绘制逻辑 (添加程序->启用顶点->绘制)
复制代码
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进行缓冲,以提高效率

/*** 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();// 将坐标添加到FloatBufferbuffer.put(vertexs);//设置缓冲区以读取第一个坐标buffer.position(0);return buffer;
}
复制代码

2.3 使用

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

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 contextsetRenderer(this);//设置渲染器}@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {glPoint = new GLPoint();}@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {GLES30.glViewport(0, 0, width, height);//设置GL视口}@Overridepublic void onDrawFrame(GL10 gl) {glPoint.draw();}
}
复制代码

2.4 多点绘制

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

    //顶点数组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 画线

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

//绘制
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

//顶点着色代码
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添加变换矩阵逻辑

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

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];//投影矩阵 mProjectionMatrixprivate final float[] mProjectionMatrix = new float[16];//视图矩阵 mViewMatrixprivate 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 contextsetRenderer(this);//设置渲染器}@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {line = new GLLine();}@Overridepublic 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);}@Overridepublic 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 读取资源文件并加载程序方法

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

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的内容就比较精简了。

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

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

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


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

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

  1. python2.7打开webdriver打不开ie_18个提高效率改变生活的网站,为你打开新世界的大门...

    本文由什么值得买用户原创:值行 哈喽大家好,这里是值行,我是小编桃子. 今天小编要分享18个超赞的网站,每一个都好用到不行!如果你之前没用过这些网站,保证这篇文章带你打开新世界的的大门. 1.Fire ...

  2. 闲鱼2.0:“信任”是打开闲置经济大门的唯一钥匙

     闲鱼2.0  12月1日,闲鱼举行2020年品牌战略升级发布会. 阿里巴巴集团副总裁汤兴(平畴)称:"今年闲鱼品牌升级的一切行动,都围绕提升信任展开." 丰富大量的闲置好物为用户 ...

  3. 以python入门教程新世界-Python打开新世界的大门-入门篇1

    目录 题外话 之前没有写博客的习惯,现在开始写觉得入门也太晚了吧,看看同龄的大哥都写了十几万字.于是心想,我也要开启我的博客之旅.本篇讲python入门操作,适合新手!!!老鸟拐弯不送.懒得起名字了, ...

  4. 打开新世界的大门——初识c语言

    一.第一个C语言程序 学C语言当然第一个程序就是"hello world",但就是这个简短的代码也是c的代码的启蒙. #include--编译预处理指令 stdio.h--系统的库 ...

  5. office插件开发_Office神插件,打开新世界的大门

    微软出品的office三件套是目前普及率最高的办公软件,除了其自身强大的功能以外,许多开发者都为其开发了功能便捷的插件,避免很多重复的操作以及超多神操作! 今天,本钧掏出家底给你们分享一波Office ...

  6. echart 打开新世界的大门

    实时折线图 option = {backgroundColor:'#2B2B2B',tooltip: {trigger: 'axis'},legend: {data:['频率'],textStyle: ...

  7. 精心推荐10个高质量的网站,打开新世界的大门

    这道题小互会,作为一个工具控,浏览器收藏夹藏着500多个网站,对网站有非常深入的研究.今天小智给大家分享10个高质量的网站,可以解决很多问题,直接上干货! 1.Convertio Convertio是 ...

  8. [C++]打开新世界的大门之C++入门

  9. 打开新世界的大门。。吧?

    由于学校下学期计划学C++,而学习发的课本又实在是太过于老古董,所以从网上买了一本各路大神都推荐的<C++ primer>来看,而今天,这本书是终于到我家了.(幸好买的早,没赶上春节). ...

  10. 逆天好用丨打开新世界的大门

    1⃣️悦音配音 一款非常实用的创作工具,做各种风格的配音,提取你想要的视频文案或音频文案,导入图片,一键生成三联封面,无水印视频下. 2⃣️薄荷外刊 以前看外刊总得到各种网站寻找PDF, 有它之后都省 ...

最新文章

  1. SC-A-LOAM:在A-LOAM中加入回环检测
  2. Java学习第三天160818 表单 框架 下拉列表等
  3. 微软部分服务已迁移至中国本地服务器
  4. react 逆地理 高德地图_高德地图又出逆天黑科技!全国各大城市模型直接获取...
  5. CNN的几种经典模型
  6. 企业如何选择数据分析架构?——谈谈3种架构的利弊
  7. 计算机文化理论基础考试单机版,计算机文化基础授课计划表
  8. OSPF高级特性(华为设备)
  9. STM32F4 + HAL库 + W25Q256的验证
  10. 2022-01-18国产Linux深度操作系统deepin20.4发布,涉及桌面和内核升级。
  11. Spring+Mybatis整合
  12. Access Violation(非法访问)解析
  13. OSSH免费版华为Portal
  14. android 通知权限设置在哪,Android 打开消息通知权限
  15. SDN控制器OpenDaylight简介
  16. Pycharm以及cmd调用Anaconda已配置环境的方法
  17. 蒂森调试软件Tcm manager ,mc1-mc2系统,电脑端,可以调试蒂森电梯程序
  18. Java图片压缩及解决遇到压缩时出现黑底的问题
  19. 育人才,促就业!全国高等学校民航服务技能大赛吸引广泛关注
  20. jquery-1.11.1.min.js文件(下拉选带搜索)

热门文章

  1. 天气预报接口_JMeter 接口自动化测试篇 26
  2. 30条html代码编写规范
  3. ubuntu搭建PHP网站完整实例教程
  4. 算法学习:501.二叉搜索树中的众数
  5. Remix Icon
  6. ft232h引脚_K9K8G08U0B-PIB0--斗门--镁光MICRON内存收购
  7. 服务器硬盘无法显示扩展卷,win10硬盘分区合并扩展卷没法点怎么解决_win10硬盘分区不能点扩展卷修复方法-win7之家...
  8. EXCEL 快捷键集合
  9. kafka报错Error while fetching metadata with correlation
  10. 小码哥C++_面向对象