DAYU200是一款支持OpenHarmony的富设备,其硬件支持GPU,OpenHarmony的图形框架也使能了GPU相关功能。DAYU200的GPU型号为Mali-G52,支持OpenGL ES 1.1/2.0/3.2,OpenCL 2.0,Vulkan 1.1。本文分享在DAYU200上,以OpenHarmony为平台,如何搭建OpenGL开发环境,及用最简单的OpenGL API(C/C++语言)绘制基本图形 —— 三角形。
本文能够在OpenHarmony上采用OpenGL的API实现绘制基本图形,参考了相关开源项目或文章,在此对相关作者表示感谢。
一、OpenHarmony图形栈
OpenHarmony 图形栈如下图所示。OpenHarmony接口层,提供图形的 Native API能力,包括:WebGL、Native Drawing的绘制能力、OpenGL指令级的绘制能力支撑等。
OpenHarmony应用开发是主要基于JS API,平时大家开发应用,基本用不到C/C++的方式。了解如何使用OpenHarmony的Native Drawing的绘制能力及OpenGL指令来绘制一些简单图形,对加深OpenHarmony图形栈的认识颇有意义。

二、开发环境搭建
如果使用OpenGL API直接绘制基本图形,需要依赖OpenHarmony的接口层的相关API,如何方便使用,主要参考Gitee项目(https://gitee.com/honglianglin/glmark2_2)。
1、NativeWindow
将native_window_wrapper源码及BUILD.gn,加入到OpenHarmony编译后,生成libnative_window_wrapper.z.so库,OpenGL应用程序,会调用该动态库进行窗口的创建等。

库编译好后,可以使用hdc_std命令,推送到开发板端,参考命令如下:
相关源码见附件(native_window_wrapper.zip),其中native_window_wrapper.h头文件内容如下:
2、应用开发
为了便于直接开发使用OpenGL API的程序,环境基于glmark2,删除了其中benchmark相关代码,精简为一个基于Make构建的工程(工程源码见附件:native_window_ohos.zip),便于在OpenHarmony平台,应用开发快速验证OpenGL相关API,目录结构如下,开发时执行make即可编译。
.
├── include
│ └── native_window_wrapper.h
├── main.cpp
├── Makefile
└── src
├── canvas-generic.cpp
├── canvas-generic.h
├── canvas.h
├── glad
├── gl-headers.cpp
├── gl-headers.h
├── gl-state-egl.cpp
├── gl-state-egl.h
├── gl-state.h
├── gl-visual-config.cpp
├── gl-visual-config.h
├── include
├── libmatrix
├── native-state.h
├── native-state-ohos.cpp
├── native-state-ohos.h
├── ohos_wrapper_linker.cpp
├── ohos_wrapper_linker.h
├── options.cpp
├── options.h
├── shared-library.cpp
└── shared-library.h
Makefile核心内容:
三、绘制基本图形
采用OpenGL API,绘制基本图形 - 三角形的源码如下,绘制流程部分参考知乎。
#include "gl-headers.h"
#include "options.h"
#include "log.h"
#include "util.h"
#include "canvas-generic.h"
#include "native-state-ohos.h"
#include "gl-state-egl.h"
using std::vector;
using std::string;
int main(int argc, char *argv[])
{
if (!Options::parse_args(argc, argv))
return 1;
/* Initialize Log class */
Log::init(Util::appname_from_path(argv[0]), Options::show_debug);
if (Options::show_help) {
Options::print_help();
return 0;
}
/* Force 320x240 output for validation */
if (Options::validate &&
Options::size != std::pair<int,int>(320, 240))
{
Log::info("Ignoring custom size %dx%d for validation. Using 800x600.\n",
Options::size.first, Options::size.second);
Options::size = std::pair<int,int>(320, 240);
}
// Create the canvas
NativeStateOhos native_state;
GLStateEGL gl_state;
CanvasGeneric canvas(native_state, gl_state, Options::size.first, Options::size.second);
canvas.offscreen(Options::offscreen);
canvas.visual_config(Options::visual_config);
if (!canvas.init()) {
Log::error("%s: Could not initialize canvas\n", __FUNCTION__);
return 1;
}
Log::info("=======================================================\n");
canvas.print_info();
Log::info("=======================================================\n");
canvas.visible(true);
/**
** 数据处理: 生成和绑定VBO, 设置属性指针
**/
// 三角形的顶点数据, 规范化(x,y,z)都要映射到[-1,1]之间
const float triangle[] = {
// 位置
-0.5f, -0.5f, 0.0f, // 左下
0.5f, -0.5f, 0.0f, // 右下
0.0f, 0.5f, 0.0f // 正上
};
// 生成并绑定立方体的VBO
GLuint vertex_buffer_object; // VBO
glGenBuffers(1, &vertex_buffer_object);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_object);
// 将顶点数据绑定到当前默认的缓冲中, 好处是不用将顶点数据一个一个地发送到显卡上, 可以借助VBO一次性发送所有顶点数据
// GL_STATIC_DRAW表示顶点数据不会被改变
glBufferData(GL_ARRAY_BUFFER, sizeof(triangle), triangle, GL_STATIC_DRAW);
// 设置顶点属性指针
// 第一个参数0: 顶点着色器的位置值
// 第二个参数3: 位置属性是一个三分量的向量
// 第三个参数: 顶点的类型
// 第四个参数: 是否希望数据标准化,映射到[0,1]
// 第五个参数: 步长,表示连续顶点属性之间的间隔,下一组的数据再3个float之后
// 第六个参数: 数据的偏移量, 位置属性在开头, 因此为0, 还需要强制类型转换
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0);
glEnableVertexAttribArray(0); // 开启0通道
glBindBuffer(GL_ARRAY_BUFFER, 0);
/**
** 着色器: 顶点和片段着色器
**/
/* 着色器源码 -> 生成并编译着色器 -> 链接着色器到着色器程序 -> 删除着色器 */
const char *vertex_shader_source =
"attribute vec4 a_Position;\n" // 位置变量属性设置为0
"void main()\n"
"{\n"
" gl_Position = a_Position;\n"
"}\n\0";
/* 设置片元像素的颜色为红色, vec4(r,g,b,a) */
const char *fragment_shader_source =
"void main()\n"
"{\n"
" gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
"}\n\0";
/**
** 生成并编译着色器
**/
/* 顶点着色器 */
int vertex_shader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
glCompileShader(vertex_shader);
int success;
char info_log[512];
/* 检查着色器是否成功编译, 如果编译失败, 打印错误信息 */
glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success);
if(!success){
glGetShaderInfoLog(vertex_shader, 512, NULL, info_log);
Log::error("SHADER::VERTEX::COMPILATION_FAIILED %s\n", info_log);
}
/* 片元着色器 */
int fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
glCompileShader(fragment_shader);
/* 检查着色器是否成功编译, 如果编译失败, 打印错误信息 */
glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success);
if(!success){
glGetShaderInfoLog(fragment_shader, 512, NULL, info_log);
Log::error("glGetShaderiv fragment_shader fail %s\n", info_log);
}
/* 链接顶点和片段着色器至一个着色器程序 */
int shader_program = glCreateProgram();
glAttachShader(shader_program, vertex_shader);
glAttachShader(shader_program, fragment_shader);
glLinkProgram(shader_program);
/* 检查着色器是否成功链接, 如果链接失败, 打印错误信息 */
glGetProgramiv(shader_program, GL_LINK_STATUS, &success);
if(!success){
glGetProgramInfoLog(shader_program, 512, NULL, info_log);
Log::error("glGetShaderiv shader_program fail %s\n", info_log);
}
/* 删除顶点和片段着色器 */
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
/**
** 渲染
**/
/* 清空颜色缓冲 */
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // 用黑色背景色来清空
glClear(GL_COLOR_BUFFER_BIT);
/* 使用着色器程序 */
glUseProgram(shader_program);
/* 绘制三角形 */
glDrawArrays(GL_TRIANGLES, 0, 3); // 绘制三角形, 绘制三角形, 顶点起始索引值, 绘制数量
/* 更新交换缓冲 */
canvas.update();
glDeleteBuffers(1, &vertex_buffer_object);
getchar();
return 0;
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
- 173.
- 174.
- 175.
- 176.
- 177.
四、运行效果展示
1、运行工程编译出来的可执行程序native_main, 日志打印如下:

2、界面显示效果如下(左上角区域 320x240):

运行失败,输出:
==>[INIT] (native_main) CDBG: In file: ../cdbg/src/mali_cdbg_env.c line: 714 cdbgp_populate_from_system_environment
Initialization of a handle to the system environment failed (0)
rk-debug-maliso[new_display 209] enter rk_so_ver: v5
CreateWindow nativeWindow_ 0x112e0f0 w: 320, h: 240
请教如何解决,谢谢!
这个牛叉
现在还有问题没,这几天都没看评论
注意需要用到:libnative_window_wrapper.z.so
有的,我也是学习的,最近在看其他的单元测试demo,方便的话加个微信请教下:ampere_ufo,感谢:)
请问您的问题,搞定了吗?我也遇到了同样的问题
注意:libnative_window_wrapper.z.so需要重新编译