#DAYU200体验官#在开发板上,使用OpenGL相关API绘制基本图形流程 原创 精华
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命令,推送到开发板端,参考命令如下:
hdc_std shell mount -o remount,rw /
hdc_std file send libnative_window_wrapper.z.so /system/lib
相关源码见附件(native_window_wrapper.zip),其中native_window_wrapper.h头文件内容如下:
#ifndef NATIVE_WINDOW_WRAPPER
#define NATIVE_WINDOW_WRAPPER
#include <cstdint>
extern "C" {
typedef struct {
void* (*CreateWindowWrapper)();
bool (*CreateWindow)(void* wrapper, uint32_t w, uint32_t h);
void* (*GetNativeWindow)(void* wrapper);
void (*SetVisibility)(void* wrapper, bool visible);
void (*DestroyWindowWrapper)(void* wrapper);
} WrapperFunc;
bool GetWrapperFunc(WrapperFunc* funcs);
}
#endif // NATIVE_WINDOW_WRAPPER
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核心内容:
OHOS_ROOT = /home/algoideas/openharmony/master
CC = $(OHOS_ROOT)/prebuilts/clang/ohos/linux-x86_64/llvm/bin/clang --sysroot=$(OHOS_ROOT)/out/a311d/obj/third_party/musl
CXX = $(OHOS_ROOT)/prebuilts/clang/ohos/linux-x86_64/llvm/bin/clang++ --sysroot=$(OHOS_ROOT)/out/a311d/obj/third_party/musl
AR = $(OHOS_ROOT)/prebuilts/clang/ohos/linux-x86_64/llvm/bin/llvm-ar
LD = $(OHOS_ROOT)/prebuilts/clang/ohos/linux-x86_64/llvm/bin/llvm-link
ARFLAG = -rcs
TARGET = native_main
PROJECT_PATH = $(shell pwd)
CFLAGS := -march=armv7-a \
-mfloat-abi=softfp \
-mtune=generic-armv7-a \
-mfpu=neon \
-mthumb \
--target=arm-linux-ohosmusl \
--sysroot=$(OHOS_ROOT)/out/rk3568/obj/third_party/musl
# Warning
CFLAGS += -Wno-c++11-narrowing
# Lib
CLIBS = -lm -ldl -lrt
CLIBS += -L$(OHOS_ROOT)/device/soc/rockchip/hardware/gpu/lib -lmali-bifrost-g52-g2p0-ohos
CLIBS += -L$(OHOS_ROOT)/out/rk3568/packages/phone/system/lib -lhilog -lsurface.z -lutils.z
CFLAGS += -DOHOS_USE_DRM -DOHOS_USE_GLESv2 -DOHOS_USE_EGL
INCLUDE_DIRS += \
-I$(OHOS_ROOT)/third_party/EGL/api \
-I$(OHOS_ROOT)/third_party/openGLES/api \
-I$(OHOS_ROOT)/utils/native/base/include \
-I$(OHOS_ROOT)/drivers/peripheral/base \
-I$(OHOS_ROOT)/foundation/graphic/standard/interfaces/innerkits/common \
-I$(OHOS_ROOT)/foundation/graphic/standard/interfaces/innerkits/surface \
-I$(OHOS_ROOT)/foundation/graphic/standard/utils/buffer_handle/export \
-I$(OHOS_ROOT)/foundation/communication/ipc/interfaces/innerkits/ipc_core/include \
-I$(OHOS_ROOT)/foundation/aafwk/standard/frameworks/kits/ability/ability_runtime/include \
-I$(OHOS_ROOT)/foundation/aafwk/standard/interfaces/innerkits/ability_manager/include \
-I$(OHOS_ROOT)/foundation/aafwk/standard/interfaces/innerkits/app_manager/include/appmgr \
-I$(OHOS_ROOT)/third_party/jsoncpp/include \
-I$(OHOS_ROOT)/third_party/json/include \
-I$(OHOS_ROOT)/foundation/windowmanager/interfaces/innerkits/wm \
-I$(OHOS_ROOT)/foundation/graphic/standard/frameworks/surface \
-I$(OHOS_ROOT)/foundation/graphic/standard/rosen/modules/render_service_base/include \
-I$(OHOS_ROOT)/foundation/graphic/standard/rosen/modules/render_service_client/core \
-I$(OHOS_ROOT)/foundation/graphic/standard/rosen/modules/render_service_client \
-I$(OHOS_ROOT)/third_party/flutter/skia \
-I$(OHOS_ROOT)/third_party/flutter/skia/include/core \
-I$(OHOS_ROOT) \
-I$(OHOS_ROOT)/third_party/skia
INCLUDE_DIRS += -I$(PROJECT_PATH)/include \
-I$(PROJECT_PATH)/src/glad/include \
-I$(PROJECT_PATH)/src/libmatrix \
-I$(PROJECT_PATH)/src
export CC CXX CFLAGS AR LD ARFLAG MODULE_SELECT
CPP_SOURCES = $(wildcard ./src/*.cpp)
CPP_OBJECTS = $(patsubst %.cpp,%.o,$(CPP_SOURCES))
三、绘制基本图形
采用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、运行工程编译出来的可执行程序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需要重新编译