#DAYU200体验官#在开发板上,使用OpenGL相关API绘制基本图形流程 原创 精华

AlgoIdeas
发布于 2022-6-1 17:28
浏览
6收藏

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图形栈的认识颇有意义。

#DAYU200体验官#在开发板上,使用OpenGL相关API绘制基本图形流程-鸿蒙开发者社区

二、开发环境搭建

如果使用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应用程序,会调用该动态库进行窗口的创建等。
#DAYU200体验官#在开发板上,使用OpenGL相关API绘制基本图形流程-鸿蒙开发者社区

库编译好后,可以使用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, 日志打印如下:
#DAYU200体验官#在开发板上,使用OpenGL相关API绘制基本图形流程-鸿蒙开发者社区

2、界面显示效果如下(左上角区域 320x240):
#DAYU200体验官#在开发板上,使用OpenGL相关API绘制基本图形流程-鸿蒙开发者社区

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
native_window_wrapper.zip 1.94K 165次下载
native_window_ohos.zip 131.8K 208次下载
已于2022-6-1 17:28:41修改
8
收藏 6
回复
举报
7条回复
按时间正序
/
按时间倒序
大愚AA
大愚AA

运行失败,输出:

==>[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

 

请教如何解决,谢谢!

回复
2022-6-9 00:48:45
挖墙脚的农民工
挖墙脚的农民工

这个牛叉

回复
2022-6-10 17:45:01
AlgoIdeas
AlgoIdeas 回复了 大愚AA
运行失败,输出: ==>[INIT] (native_main) CDBG: In file: ../cdbg/src/mali_cdbg_env.c line: 714 cdbgp_populate_from_system_environmentInitialization of a handle to the system environment failed (0)rk-debug-maliso[new_display 209] enter rk_so_ver: v5CreateWindow nativeWindow_ 0x112e0f0 w: 320, h: 240 请教如何解决,谢谢!

现在还有问题没,这几天都没看评论

回复
2022-6-11 18:54:10
AlgoIdeas
AlgoIdeas

注意需要用到:libnative_window_wrapper.z.so

回复
2022-6-11 18:56:34
大愚AA
大愚AA 回复了 AlgoIdeas
现在还有问题没,这几天都没看评论

有的,我也是学习的,最近在看其他的单元测试demo,方便的话加个微信请教下:ampere_ufo,感谢:)

已于2022-6-20 22:59:17修改
回复
2022-6-20 22:58:21
wx6417044c35ef7
wx6417044c35ef7 回复了 大愚AA
有的,我也是学习的,最近在看其他的单元测试demo,方便的话加个微信请教下:ampere_ufo,感谢:)

请问您的问题,搞定了吗?我也遇到了同样的问题

回复
2023-3-19 20:48:03
AlgoIdeas
AlgoIdeas

注意:libnative_window_wrapper.z.so需要重新编译

回复
2023-4-2 19:21:09
回复
    相关推荐