鸿蒙简单移植安卓下图形程序方法

一、创建工程

  File>New>Create Project,创建个新native c++工程出来。
  页面布局在entry\src\main\ets\pages\Index.ets中,这是Ark-TS写的页面布局,显示“Hello World”字符串,且点击字符时,会用Node-api调用C++计算加法,并将结果返回到Ark-JS进行log输出。

import testNapi from 'libentry.so'

@Entry
@Component
struct Index {
  @State message: string = 'Hello World'

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
          .onClick(() => {
            hilog.info(0x0000, 'testTag', 'Test NAPI 2 + 3 = %{public}d', testNapi.add(2, 3));
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

  napi的写法具体可以参考这里:使用Node-API实现跨语言交互开发流程

二、添加XComponent

  写安卓图形应用,使用C api方式,一般是先从NDK获取windows(可能是设置回调,也可能是直接调用接口获取),用windows初始化EGL,获取Surface:

EGLNativeDisplayType ndt = NULL;
EGLNativeWindowType  nwh; //Window
EGLDisplay display = eglGetDisplay(NULL == ndt ? EGL_DEFAULT_DISPLAY : ndt);

EGLSurface  surface = eglCreateWindowSurface(display, config, nwh, NULL);

EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, s_contextAttrs);
eglMakeCurrent(display, surface, surface, context);

设置布局

  安卓和鸿蒙的差异会主要体现在上述nwh的获取,对于鸿蒙应用来说,会用XComponent组件在上述Index.ets中设置布局,并设置库名称:

build() {

    Stack({ alignContent: Alignment.TopStart }) {
      XComponent({
        id: "test001",
        type: XComponentType.SURFACE,
        libraryname: "testgles"
      })
        .onLoad(() => {
          hilog.info(0x0000, 'testTag', 'XComponent onLoad');
        })
        .width('100%')
        .height('100%')

      Row() {
        Column() {
          Text(this.message)
            .fontSize(50)
            .fontWeight(FontWeight.Bold)
            .onClick(() => {
              hilog.info(0x0000, 'testTag', 'Test NAPI 2 + 3 = %{public}d', testNapi.add(2, 3));
            })
        }
        .width('100%')
      }
      .height('100%')
    }
  }

  上面代码libraryname: "testgles"就是我设置的库名称,C++代码需要编译出libtestgles.so供其加载,因此需要在CMakeLists.txt中,进行相应改写,add_library需要将其内容改成新的:

#add_library(entry SHARED napi_init.cpp)
#target_link_libraries(entry PUBLIC libace_napi.z.so)

add_library(testgles SHARED napi_init.cpp)
target_link_libraries(testgles PUBLIC libace_napi.z.so)#提供native_api.h的实现

  napi_init.cpp里注册模块的地方也需要调整:

static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    //.nm_modname = "entry",
    .nm_modname = "testgles",
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};

  Init里暂且不需要注册给ets调用的函数,但是得注册XComponent回调来获取窗口。

添加log

  我们得先加点log用于查看是否调用,引入log.h文件来完成:

#include "hilog/log.h"

#define APP_LOG_TAG "TEST GLES Demo"
#define LOGI(...) ((void)OH_LOG_Print(LOG_APP, LOG_INFO, LOG_DOMAIN, APP_LOG_TAG, __VA_ARGS__))
#define LOGE(...) ((void)OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, APP_LOG_TAG, __VA_ARGS__))

  log文件不是自动链接的,还要去CMakeList.txt中添加链接:

find_library(
    hilog-lib
    hilog_ndk.z
)

#提供native_api.h的实现
find_library(
    libnapi-lib
    ace_napi.z
)

target_link_libraries(testgles PUBLIC ${hilog-lib} ${libnapi-lib})



  有了LOG,我们先简单定义四个事件回调函数:

void OnSurfaceCreatedCB(OH_NativeXComponent *component, void *window)
{
    LOGI("OnSurfaceCrateCB");
}

void OnSurfaceChangedCB(OH_NativeXComponent *component, void *window)
{
    LOGI("OnSurfaceChangedCB");
}

void OnSurfaceDestroyedCB(OH_NativeXComponent *component, void *window)
{
    LOGI("OnSurfaceDestroyedCB");
}

void DispatchTouchEventCB(OH_NativeXComponent *component, void *window) {}

  然后在C++用napi获取原生XComponent组件对象:

OH_NativeXComponent* GetNativeXComponent(napi_env env, napi_value exports)
{
    //获取exportInstance函数
    napi_value exportInstance = nullptr;
    if (napi_ok != napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance)) {
        LOGE("export napi_get_named_property fail");
        return nullptr;
    }

    //调用函数获取XComponent
    OH_NativeXComponent *nativeXComponent = nullptr;
    if (napi_ok != napi_unwrap(env, exportInstance, reinterpret_cast<void **>(&nativeXComponent))) {
        LOGE("export napi_unwrap fail");
        return nullptr;
    }
    
    return nativeXComponent;
}

  在Init中设置回调:

#include <ace/xcomponent/native_interface_xcomponent.h>

EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
    OH_NativeXComponent* nativeXComponent = GetNativeXComponent(env, exports);
    
    if (nativeXComponent != nullptr)
    {
        static OH_NativeXComponent_Callback renderCallback;
        renderCallback.OnSurfaceCreated = OnSurfaceCreatedCB;
        renderCallback.OnSurfaceChanged = OnSurfaceChangedCB;
        renderCallback.OnSurfaceDestroyed = OnSurfaceDestroyedCB;
        renderCallback.DispatchTouchEvent = DispatchTouchEventCB;
        
        if (OH_NATIVEXCOMPONENT_RESULT_SUCCESS != OH_NativeXComponent_RegisterCallback(nativeXComponent, &renderCallback))
        {
            LOGE("OH_NativeXComponent_RegisterCallback napi_get_named_property fail");
        }
    }
    
    return exports;
}
EXTERN_C_END

  注意我这里的renderCallback变量是static的,因为如果不定义static,其生命周期在出函数后就会结束,结束后里面注册的回调也会找不到(想不到华为这个函数存的竟然是结构体的地址而不是转存回调地址)实际编写中,renderCallback未必是static的,但一定是持久持有在某处内存中,肯定不能是函数中声明的暂态对象。
  OH_NativeXComponent_RegisterCallback在头文件ace/xcomponent/native_interface_xcomponent.h中声明,并且也要在CMakeList.txt中链接文件:

find_library(
    libace-lib
    ace_ndk.z
)

target_link_libraries(testgles PUBLIC ${hilog-lib} ${libnapi-lib} ${libace-lib})

  此时可以先编译并部署到设备中查看,是一个黑屏的app,且在开启时会输出OnSurfaceCreated回调中的logOnSurfaceCrateCB,关闭app会输出OnSurfaceDestroyed中的logOnSurfaceDestroyedCB

三、EGL初始化

  EGL用于管理窗口和上下文,获取渲染的backbuffer,我们在OnSurfaceCreatedCB回调中可以获取到窗口,在这里初始化EGL,在这里启动渲染线程并初始化EGL。
  我定义了一个新类:

class Renderer {
public:
    void RenderThread();
    void Close();
    
    OH_NativeXComponent *mComponent;
    void *mWindow;
    
private:
    bool mStop = false;
    bool mEglInit = false;
};

  在OnSurfaceCreatedCB中设置窗口和组件,并启动渲染线程:

Renderer renderer;
std::thread renderThread;

void OnSurfaceCreatedCB(OH_NativeXComponent *component, void *window)
{
    LOGI("OnSurfaceCrateCB");
    
    renderer.mWindow = window;
    renderer.mComponent = component;
    renderThread = std::thread(std::bind(&Renderer::RenderThread, renderer));
}

  渲染线程循环执行渲染,不过我们先提供EGL初始化,并每帧输出LOG:

void Renderer::RenderThread()
{
    while (!mStop)
    {
        if (!mEglInit && mWindow != NULL)
        {
            EGLInit(mWindow);
            mEglInit = true;
        }
            
        if (mEglInit)
        {
            DrawLoop();
        }
    }
}

void DrawLoop()
{
    LOGI("Draw……");
    eglSwapBuffers(eglDisplay, eglSurface);
}

  EGL初始化的方法较安卓上EGL初始化没有区别:

#include <EGL/egl.h>

EGLDisplay eglDisplay;
EGLSurface eglSurface;

void EGLInit(void *window)
{
    //EGLNativeWindowType eglWindow = static_cast<EGLNativeWindowType>(window);
    EGLNativeWindowType eglWindow = (EGLNativeWindowType)window;
    eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    
    if (EGL_NO_DISPLAY == eglDisplay) {
        LOGE("EGLCore init failed: unable to get EGL display");
        return;
    }
    
    EGLint majorVersion;
    EGLint minorVersion;
    if (!eglInitialize(eglDisplay, &majorVersion, &minorVersion)) {
        LOGE("EGLCore init failed: unable to get initialize EGL display");
        return;
    }
    
    const EGLint maxConfigSize = 1;
    const EGLint ATTRIB_LIST[] = {
        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
        EGL_RED_SIZE, 8,
        EGL_GREEN_SIZE, 8,
        EGL_BLUE_SIZE, 8,
        EGL_ALPHA_SIZE, 8,
        EGL_DEPTH_SIZE, 24,
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
        EGL_NONE
    };
    EGLint numConfigs;
    EGLConfig eglConfig;
    if (!eglChooseConfig(eglDisplay, ATTRIB_LIST, &eglConfig, maxConfigSize, &numConfigs)) {
        LOGE("EGLCore init failed: eglChooseConfig unable to choose configs");
        return;
    }
    
    eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, eglWindow, NULL);
    
    if (nullptr == eglSurface) {
        LOGE("EGLCore init failed: eglCreateWindowSurface unable to create surface");
        return;
    }
    
    const EGLint CONTEXT_ATTRIBS[] = {
        EGL_CONTEXT_CLIENT_VERSION, 2,
        EGL_NONE
    };
    EGLContext eglContext = eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, CONTEXT_ATTRIBS);
    if (!eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) {
        LOGE("EGLCore init failed: eglMakeCurrent failed");
        return;
    }
}

  CMakeList.txt需要添加egl的链接:

find_library(
    EGL-lib
    EGL
)

target_link_libraries(testgles PUBLIC ${hilog-lib} ${libnapi-lib} ${libace-lib} ${EGL-lib})

  顺带增加下停止逻辑:

void OnSurfaceDestroyedCB(OH_NativeXComponent *component, void *window)
{
    LOGI("OnSurfaceDestroyedCB");
    
    renderer.Close();
}

void Renderer::Close()
{
    mStop = true;
}

  现阶段就是一个循环输出LOG黑屏的应用。

四、GLES渲染编写

  GLES的编写也没有任何区别,我直接从一个安卓实例拷贝出一个简单的GLES工程,不需要仔细看其中的逻辑,只要保证初始化、更新、销毁逻辑对应上即可:

#include <GLES3/gl32.h>

  shader代码中写死:

static const char VERTEX_SHADER[] =
    "#version 300 es\n"
    "layout(location = " STRV(POS_ATTRIB) ") in vec2 pos;\n"
    "layout(location=" STRV(COLOR_ATTRIB) ") in vec4 color;\n"
    "layout(location=" STRV(SCALEROT_ATTRIB) ") in vec4 scaleRot;\n"
    "layout(location=" STRV(OFFSET_ATTRIB) ") in vec2 offset;\n"
    "out vec4 vColor;\n"
    "void main() {\n"
    "    mat2 sr = mat2(scaleRot.xy, scaleRot.zw);\n"
    "    gl_Position = vec4(sr*pos + offset, 0.0, 1.0);\n"
    "    vColor = color;\n"
    "}\n";

static const char FRAGMENT_SHADER[] =
    "#version 300 es\n"
    "precision mediump float;\n"
    "in vec4 vColor;\n"
    "out vec4 outColor;\n"
    "void main() {\n"
    "    outColor = vColor;\n"
    "}\n";

  一些简单的代码设施:

bool checkGlError(const char* funcName) {
  GLint err = glGetError();
  if (err != GL_NO_ERROR) {
    LOGE("GL error after %s(): 0x%08x\n", funcName, err);
    return true;
  }
  return false;
}

GLuint createShader(GLenum shaderType, const char* src) {
  GLuint shader = glCreateShader(shaderType);
  if (!shader) {
    checkGlError("glCreateShader");
    return 0;
  }
  glShaderSource(shader, 1, &src, NULL);

  GLint compiled = GL_FALSE;
  glCompileShader(shader);
  glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
  if (!compiled) {
    GLint infoLogLen = 0;
    glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLen);
    if (infoLogLen > 0) {
      GLchar* infoLog = (GLchar*)malloc(infoLogLen);
      if (infoLog) {
        glGetShaderInfoLog(shader, infoLogLen, NULL, infoLog);
        LOGE("Could not compile %s shader:\n%s\n",
              shaderType == GL_VERTEX_SHADER ? "vertex" : "fragment", infoLog);
        free(infoLog);
      }
    }
    glDeleteShader(shader);
    return 0;
  }

  return shader;
}

GLuint createProgram(const char* vtxSrc, const char* fragSrc) {
  GLuint vtxShader = 0;
  GLuint fragShader = 0;
  GLuint program = 0;
  GLint linked = GL_FALSE;

  vtxShader = createShader(GL_VERTEX_SHADER, vtxSrc);
  if (!vtxShader) goto exit;

  fragShader = createShader(GL_FRAGMENT_SHADER, fragSrc);
  if (!fragShader) goto exit;

  program = glCreateProgram();
  if (!program) {
    checkGlError("glCreateProgram");
    goto exit;
  }
  glAttachShader(program, vtxShader);
  glAttachShader(program, fragShader);

  glLinkProgram(program);
  glGetProgramiv(program, GL_LINK_STATUS, &linked);
  if (!linked) {
    LOGE("Could not link program");
    GLint infoLogLen = 0;
    glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLen);
    if (infoLogLen) {
      GLchar* infoLog = (GLchar*)malloc(infoLogLen);
      if (infoLog) {
        glGetProgramInfoLog(program, infoLogLen, NULL, infoLog);
        LOGE("Could not link program:\n%s\n", infoLog);
        free(infoLog);
      }
    }
    glDeleteProgram(program);
    program = 0;
  }

exit:
  glDeleteShader(vtxShader);
  glDeleteShader(fragShader);
  return program;
}

  初始化资产:

GLuint shaderProgram;
enum { VB_INSTANCE, VB_SCALEROT, VB_OFFSET, VB_COUNT };
#define MAX_INSTANCES_PER_SIDE 16
#define MAX_INSTANCES (MAX_INSTANCES_PER_SIDE * MAX_INSTANCES_PER_SIDE)
#define TWO_PI (2.0 * M_PI)
#define MAX_ROT_SPEED (0.3 * TWO_PI)

struct Vertex {
  GLfloat pos[2];
  GLubyte rgba[4];
};
Vertex QUAD[4] = {
    // Square with diagonal < 2 so that it fits in a [-1 .. 1]^2 square
    // regardless of rotation.
    {{-0.7f, -0.7f}, {0x00, 0xFF, 0x00}},
    {{0.7f, -0.7f}, {0x00, 0x00, 0xFF}},
    {{-0.7f, 0.7f}, {0xFF, 0x00, 0x00}},
    {{0.7f, 0.7f}, {0xFF, 0xFF, 0xFF}},
};

GLuint mVB[VB_COUNT];
GLuint mVBState;

float* mapOffsetBuf() {
  glBindBuffer(GL_ARRAY_BUFFER, mVB[VB_OFFSET]);
  return (float*)glMapBufferRange(
      GL_ARRAY_BUFFER, 0, MAX_INSTANCES * 2 * sizeof(float),
      GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
}

void unmapOffsetBuf() { glUnmapBuffer(GL_ARRAY_BUFFER); }

unsigned int mNumInstances;
float mScale[2];
float mAngularVelocity[MAX_INSTANCES];
uint64_t mLastFrameNs;
float mAngles[MAX_INSTANCES];

void calcSceneParams(unsigned int w, unsigned int h, float* offsets) {
  // number of cells along the larger screen dimension
  const float NCELLS_MAJOR = MAX_INSTANCES_PER_SIDE;
  // cell size in scene space
  const float CELL_SIZE = 2.0f / NCELLS_MAJOR;

  // Calculations are done in "landscape", i.e. assuming dim[0] >= dim[1].
  // Only at the end are values put in the opposite order if h > w.
  const float dim[2] = {fmaxf(w, h), fminf(w, h)};
  const float aspect[2] = {dim[0] / dim[1], dim[1] / dim[0]};
  const float scene2clip[2] = {1.0f, aspect[0]};
  const int ncells[2] = {static_cast<int>(NCELLS_MAJOR),
                         (int)floorf(NCELLS_MAJOR * aspect[1])};

  float centers[2][MAX_INSTANCES_PER_SIDE];
  for (int d = 0; d < 2; d++) {
    auto offset = -ncells[d] / NCELLS_MAJOR;  // -1.0 for d=0
    for (auto i = 0; i < ncells[d]; i++) {
      centers[d][i] = scene2clip[d] * (CELL_SIZE * (i + 0.5f) + offset);
    }
  }

  int major = w >= h ? 0 : 1;
  int minor = w >= h ? 1 : 0;
  // outer product of centers[0] and centers[1]
  for (int i = 0; i < ncells[0]; i++) {
    for (int j = 0; j < ncells[1]; j++) {
      int idx = i * ncells[1] + j;
      offsets[2 * idx + major] = centers[0][i];
      offsets[2 * idx + minor] = centers[1][j];
    }
  }

  mNumInstances = ncells[0] * ncells[1];
  mScale[major] = 0.5f * CELL_SIZE * scene2clip[0];
  mScale[minor] = 0.5f * CELL_SIZE * scene2clip[1];
}

void GLESResize(int w, int h)
{
    auto offsets = mapOffsetBuf();
    calcSceneParams(w, h, offsets);
    unmapOffsetBuf();
    
    // Auto gives a signed int :-(
    for (auto i = (unsigned)0; i < mNumInstances; i++) {
        mAngles[i] = drand48() * TWO_PI;
        mAngularVelocity[i] = MAX_ROT_SPEED * (2.0 * drand48() - 1.0);
    }
    
    mLastFrameNs = 0;
    
    glViewport(0, 0, w, h);
}

void GLESInit(int w, int h)
{
    shaderProgram = createProgram(VERTEX_SHADER, FRAGMENT_SHADER);
    
    glGenBuffers(VB_COUNT, mVB);
    glBindBuffer(GL_ARRAY_BUFFER, mVB[VB_INSTANCE]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(QUAD), &QUAD[0], GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, mVB[VB_SCALEROT]);
    glBufferData(GL_ARRAY_BUFFER, MAX_INSTANCES * 4 * sizeof(float), NULL,
               GL_DYNAMIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, mVB[VB_OFFSET]);
    glBufferData(GL_ARRAY_BUFFER, MAX_INSTANCES * 2 * sizeof(float), NULL,
               GL_STATIC_DRAW);
    
    glGenVertexArrays(1, &mVBState);
    glBindVertexArray(mVBState);
    
    glBindBuffer(GL_ARRAY_BUFFER, mVB[VB_INSTANCE]);
    glVertexAttribPointer(POS_ATTRIB, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
                        (const GLvoid*)offsetof(Vertex, pos));
    glVertexAttribPointer(COLOR_ATTRIB, 4, GL_UNSIGNED_BYTE, GL_TRUE,
                        sizeof(Vertex), (const GLvoid*)offsetof(Vertex, rgba));
    glEnableVertexAttribArray(POS_ATTRIB);
    glEnableVertexAttribArray(COLOR_ATTRIB);
    
    glBindBuffer(GL_ARRAY_BUFFER, mVB[VB_SCALEROT]);
    glVertexAttribPointer(SCALEROT_ATTRIB, 4, GL_FLOAT, GL_FALSE,
                        4 * sizeof(float), 0);
    glEnableVertexAttribArray(SCALEROT_ATTRIB);
    glVertexAttribDivisor(SCALEROT_ATTRIB, 1);
    
    glBindBuffer(GL_ARRAY_BUFFER, mVB[VB_OFFSET]);
    glVertexAttribPointer(OFFSET_ATTRIB, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float),
                        0);
    glEnableVertexAttribArray(OFFSET_ATTRIB);
    glVertexAttribDivisor(OFFSET_ATTRIB, 1);
    
    GLESResize(w, h);
}

  销毁资产

void GLESRelease()
{
    glDeleteVertexArrays(1, &mVBState);
    glDeleteBuffers(VB_COUNT, mVB);
    glDeleteProgram(shaderProgram);
}

  更新:

float* mapTransformBuf() {
  glBindBuffer(GL_ARRAY_BUFFER, mVB[VB_SCALEROT]);
  return (float*)glMapBufferRange(
      GL_ARRAY_BUFFER, 0, MAX_INSTANCES * 4 * sizeof(float),
      GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
}

void unmapTransformBuf() { glUnmapBuffer(GL_ARRAY_BUFFER); }

void step() {
  timespec now;
  clock_gettime(CLOCK_MONOTONIC, &now);
  auto nowNs = now.tv_sec * 1000000000ull + now.tv_nsec;

  if (mLastFrameNs > 0) {
    float dt = float(nowNs - mLastFrameNs) * 0.000000001f;

    for (unsigned int i = 0; i < mNumInstances; i++) {
      mAngles[i] += mAngularVelocity[i] * dt;
      if (mAngles[i] >= TWO_PI) {
        mAngles[i] -= TWO_PI;
      } else if (mAngles[i] <= -TWO_PI) {
        mAngles[i] += TWO_PI;
      }
    }

    float* transforms = mapTransformBuf();
    for (unsigned int i = 0; i < mNumInstances; i++) {
      float s = sinf(mAngles[i]);
      float c = cosf(mAngles[i]);
      transforms[4 * i + 0] = c * mScale[0];
      transforms[4 * i + 1] = s * mScale[1];
      transforms[4 * i + 2] = -s * mScale[0];
      transforms[4 * i + 3] = c * mScale[1];
    }
    unmapTransformBuf();
  }

  mLastFrameNs = nowNs;
}

  将上述逻辑与现有代码结合,首先是初始化和销毁:

void Renderer::RenderThread()
{
    while (!mStop)
    {
        if (!mEglInit && mWindow != NULL)
        {
            EGLInit(mWindow);
            
            uint64_t width;
            uint64_t height;
            int32_t ret = OH_NativeXComponent_GetXComponentSize(mComponent, mWindow, &width, &height);
            GLESInit(width, height);
            mEglInit = true;
        }
            
        if (mEglInit)
        {
            DrawLoop();
        }
    }
    
    GLESRelease();
}

  然后是更新:

void DrawLoop()
{
    step();
    
    glClearColor(0.2f, 0.2f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    LOGI("Draw……");
    glUseProgram(shaderProgram);
    glBindVertexArray(mVBState);
    glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, mNumInstances);
    
    eglSwapBuffers(eglDisplay, eglSurface);
}

  CMakeList.txt别忘链接gles:

find_library(
    GLES-lib
    GLESv3
)
target_link_libraries(testgles PUBLIC ${hilog-lib} ${libnapi-lib} ${libace-lib} ${EGL-lib} ${GLES-lib})

  这样就简单将一个安卓下的图形示例移植到鸿蒙下:


总结

  在渲染功能这一块,安卓和鸿蒙的写法差异主要体现在window的获取上,egl和gles上没有什么区别。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355

推荐阅读更多精彩内容