

在开发板上,使用OpenGL相关API绘制基本图形流程
source link: https://os.51cto.com/article/710802.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.


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):

Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK