#HarmonyOS NEXT体验官# 鸿蒙OpenGL入门,绘制三角形 原创 精华
一、简介
在鸿蒙NDK开发入门中介绍了ArkTS与C/C++相互调用流程,本文承接上文,介绍使用OpenGL绘制一个三角形,通过绘制三角形来熟悉OpenGL的绘制流程。CPU和GPU都能用于图形渲染,部分场景下如果使用CPU渲染,性能就非常差。但GPU可以大大提高渲染速度,OpenGL可以操作GPU,是一个2D/3D图形库,用于视频渲染、视频编辑、视频特效、游戏引擎等。除了OpenGL外,ValKan、Metal、Direct3D、WebGL、WebGPU等都是优秀的2D/3D图形库。目前OpenGL应用最广泛,资料也最丰富,跨平台,学习起来相对容易。
二、OpenGL ES
OpenGL ES是OpenGL的子集,专门用于手机、平板等小型设备,删除了不必要的方法、减少了体积。所以准确的来说,我们学习的其实是OpenGL ES。
三、XComponent
XComponent组件作为一种渲染组件,通常用于满足较为复杂的自定义渲染需求,例如相机预览流的显示、游戏画面的渲染、视频的渲染。XComponent又拥有单独的NativeWindow,可以在native侧提供native window用来创建EGL/OpenGLES环境,进而使用标准的OpenGL ES开发。
3、1添加EGL/OpenGLES库
3、2 ArkTS侧添加XComponent
XComponent有三个重要的属性:
- id : 与XComponent组件为一一对应关系,不建议重复。通常开发者可以在native侧通过OH_NativeXComponent_GetXComponentId接口来获取对应的id从而绑定对应的XComponent。如果id重复,在native侧将无法对多个XComponent进行区分。
- type:指定为surface。
- libraryname:加载模块的名称,必须与在native侧Napi模块注册时nm_modname的名字一致。
3、2 获取OH_NativeXComponent
在ArkTS侧添加XComponent后,就可以在C++侧获取OH_NativeXComponent。
- 调用napi_get_named_property函数解析参数。
- 调用napi_unwrap函数获取OH_NativeXComponent对象。
- 调用OH_NativeXComponent_GetXComponentId获取id,这个id就是在ArkTS侧给XComponent设置的id。
3、3注册回调
OH_NativeXComponent_Callback是个结构体,结构体里面是函数指针。
调用OH_NativeXComponent_RegisterCallback函数注册回调,OH_NativeXComponent_RegisterCallback函数的第一个参数是OH_NativeXComponent对象,第二个参数OH_NativeXComponent_Callback结构体指针。
当surface创建完成后回调OnSurfaceCreated函数,我们需要在OnSurfaceCreated函数里面搭建EGL环境,这样才能调用OpenGL ES的函数。
四、EGL
OpenGL是跨平台接口,面对不同平台的差异,需要有一个介于平台设备与OpenGL之间的桥梁,EGL则是其中的桥梁。EGL是OpenGL ES和系统之间的通信接口,OpenGL ES的平台无关性正是借助EGL实现的,EGL屏蔽了不同平台的差异。主要包括以下几个类:
- EGLDisplay一个抽象的系统显示类,用于操作设备窗口,加载OpenGL库。
- EGLConfig,EGL配置,如rgba位数。
- EGLSurface渲染缓存,一块内存空间,所有要渲染到屏幕上的图像数据,都要先缓存在EGLSurface上。
- EGLContext上下文,用于存储OpenGL的绘制状态信息、数据。
要想调用OpenGL ES的函数,首先就得搭建EGL环境。
4、1 搭建EGL环境
1、获取EGLDisplay对象:调用eglGetDisplay函数得到EGLDisplay,并加载OpenGL ES库。
2、初始化EGL连接:调用eglInitialize函数初始化,获取库的版本号。
3、确定渲染表面的配置信息:调用eglChooseConfig函数得到EGLConfig。
4、创建渲染表面:通过EGLDisplay和EGLConfig,调用eglCreateWindowSurface函数创建渲染表面,得到EGLSurface。
5、创建渲染上下文:通过EGLDisplay和EGLConfig,调用eglCreateContext函数创建渲染上下文,得到EGLContext。
6、绑定上下文:通过eglMakeCurrent函数将EGLSurface、EGLContext、EGLDisplay三者绑定,接下来就可以使用OpenGL进行绘制了。
7、交换缓冲:当用OpenGL绘制结束后,调用eglSwapBuffers函数交换前后缓冲,将绘制内容显示到屏幕上。
下面搭建EGL环境的完整代码。EglContextInit函数是在OnSurfaceCreated函数中调用的。
五、OpenGL ES坐标系
OpenGL ES两个重要坐标系分别是标准坐标系和屏幕坐标系。
5、1标准坐标系
- 屏幕中心是原点。
- 横纵坐标的范围在-1到1之间。
5、2屏幕坐标系
- 屏幕左上角是原点。
- 单位是像素。
- OpenGL会把标准坐标系转换成屏幕坐标系。
5、3标准坐标系转换成屏幕坐标系
- 将标准坐标系的顶点加1,标准坐标系的顶点范围是-1到1之间,加1后,顶点范围就是0到2之间。
- 每个顶点乘以屏幕的宽高。
- 上面两步可以通过变换矩阵完成。
六、着色器
着色器是运行在GPU上的小程序,分为顶点着色器和片元着色器。着色器允许开发者自定义渲染过程,提高了灵活度。可以充分利用GPU的并行计算能力,提高渲染速度。可以通过变成编程,灵活的控制GPU。
6、1顶点着色器
顶点着色器用于处理几何图形的顶点,对应标准坐标系,标准坐标系的原点是屏幕中心。
6、2片元着色器
片元着色器用于处理像素颜色和纹理,为每个像素设置不同的颜色,对应屏幕坐标系,屏幕坐标系的左上角为原点。
6、3着色器语言GLSL
OpenGL 2.0加入了可编程渲染管线,可以更加灵活的控制渲染。但也因此需要学习多一门针对GPU的编程语言,语法与C语言类似,名为GLSL。
6、3、1顶点着色器语言
下面是顶点着色器的代码,我们可以看到熟悉的主函数,在主函数中,将变量vPosition赋值给gl_Position。其含义是将图形的顶点坐标vPosition赋值给OpenGL的内建变量gl_Position,这样确定了几何图形的顶点坐标。vPosition被attribute修饰,attribute用在顶点着色器中,相当于Java/C的局部变量,一般用来传递的是顶点坐标和纹理坐标。uniform修饰的是统一变量,相当于Java/C的全局变量,传的通常是矩阵。varying是由顶点着色器传递到片元着色器的。
下面的顶点坐标就是下图所对应的坐标,这就是一个三角形的坐标。再次强调下,下面的顶点坐标对应的是标准坐标系,标准坐标系的原点是屏幕中心。这个顶点坐标会传给顶点着色器的vPosition变量,vPosition变量再把顶点坐标传给内建变量gl_Position,这样就确定了几何图形的顶点。
6、3、2片元着色器语言
下面是片元着色器的代码,片元装饰器用于设置颜色。第一行代码指定精度,精度分别有lowp低精度,mediump中精度,highp高精度。代码第二行定义vColor变量,它是uniform类型,vec4表示有4个元素的向量,颜色是由ARGB组成,需要4个元素。gl_FragColor是内建变量,gl_FragColor用于确定每个像素的颜色,这里将vColor赋值给gl_FragColor,vColor变量将会通过程序传递过来。
下面的代码定义了一个有4个元素的数组,这四个元素分别代表颜色RGBA,也就是红绿蓝和透明度。这个数组就会传递给片元着色器的vColor变量,vColor变量再把颜色值传给内建变量gl_FragColor,这样就确定几何图形的颜色。
更多的着色器语言介绍,可以查看文章着色器语言。
七、编译和链接着色器
- 调用glCreateShader创建着色器。
- 调用glShaderSource绑定源码。
- 调用glCompileShader编译着色器。
- 调用glCreateProgram创建程序。
- 调用glAttachShader绑定着色器。
- 调用glLinkProgram链接程序。
八、绘制三角形
8、1 清屏,设置屏幕颜色
- 调用glViewport设置窗口大小
- 调用glClearColor清屏,将屏幕颜色设置为黑色。
- 调用glClear清除颜色缓冲
8、2 绘制
- 调用glUseProgram函数使用程序。
- 调用glGetAttribLocation函数获取顶点着色器中定义的属性。
- 调用glEnableVertexAttribArray函数启用顶点数组。
- 调用glVertexAttribPointer函数向顶点着色器传递顶点数组。
- 调用glGetUniformLocation函数获取片元着色器中定义的变量。
- 调用glUniform4fv函数向片元着色器传递颜色。
- 调用glDrawArrays函数绘制三角形。
- 调用glDisableVertexAttribArray函数释放属性变量。
- 调用eglSwapBuffers函数交换前后缓冲,将绘制内容显示到屏幕上。
看下绘制出来的结果吧。
九、总结
真是不容易呀,如果大家能坚持看到文章末尾,希望大家能够熟悉OpenGL绘制流程。我们用大量的代码和大量的篇幅来介绍使用OpenGL绘制三角形。可能有人会问,使用Canvas可以轻轻松松的绘制出三角形,为什么要用大量的代码和大量的篇幅来介绍使用OpenGL绘制一个简简单单的三角形?
Canvas绘制其实上使用的是CPU渲染。大部分情况下,CPU渲染性能不差。但是视频渲染、视频编辑、视频特效、游戏引擎等使用CPU渲染,性能非常差,GPU渲染会大大提高渲染速度,OpenGL就是用来操作GPU的。
虽然OpenGL代码量大,但是大部分的代码是不变的,只需要写一次。自定义XComponent,注册XComponent回调,搭建EGL环境,编译和链接着色器等等,这些代码都是不变的。最重要的还是顶点着色器和片元着色器,只要OpenGL框架代码搭建起来了,大部分情况下,我们还是在写下面的代码。
后续我会持续的分享OpenGL。
源码
快 3 稳 定 带 计 划 技 巧 带 赚 的 老 师991🆇🆂.🅲🅲太阳刚刚升起,阳光洒在山间小路上,照亮了整个山谷。远处的山峰被云雾笼罩着,若隐若现的轮廓仿佛是仙境中的景象。总之,这是一篇描写风景如画的优美散文作品!它通过细腻的笔触和丰富的想象力,向读者展现了一个充满诗意和美感的场景世界。无论是欣赏还是沉浸其中,都会让人感受到无尽的美好和愉悦。。太阳刚刚升起,阳光洒在山间小路上,照亮了整个山谷。远处的山峰被云雾笼罩着,若隐若现的轮廓仿佛是仙境中的景象。总之,这是一篇描写风景如画的优美散文作品!它通过细腻的笔触和丰富的想象力,向读者展现了一个充满诗意和美感的场景世界。无论是欣赏还是沉浸其中,都会让人感受到无尽的美好和愉悦。