鴻蒙簡單移植安卓下圖形程序方法

一、創(chuàng)建工程

??File>New>Create Project,創(chuàng)建個新native c++工程出來。
??頁面布局在entry\src\main\ets\pages\Index.ets中,這是Ark-TS寫的頁面布局,顯示“Hello World”字符串,且點擊字符時,會用Node-api調(diào)用C++計算加法,并將結(jié)果返回到Ark-JS進行l(wèi)og輸出。

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實現(xiàn)跨語言交互開發(fā)流程。

二、添加XComponent

??寫安卓圖形應(yīng)用,使用C api方式,一般是先從NDK獲取windows(可能是設(shè)置回調(diào),也可能是直接調(diào)用接口獲取),用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);

設(shè)置布局

??安卓和鴻蒙的差異會主要體現(xiàn)在上述nwh的獲取,對于鴻蒙應(yīng)用來說,會用XComponent組件在上述Index.ets中設(shè)置布局,并設(shè)置庫名稱:

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"就是我設(shè)置的庫名稱,C++代碼需要編譯出libtestgles.so供其加載,因此需要在CMakeLists.txt中,進行相應(yīng)改寫,add_library需要將其內(nèi)容改成新的:

#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的實現(xiàn)

??napi_init.cpp里注冊模塊的地方也需要調(diào)整:

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調(diào)用的函數(shù),但是得注冊XComponent回調(diào)來獲取窗口。

添加log

??我們得先加點log用于查看是否調(diào)用,引入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的實現(xiàn)
find_library(
    libnapi-lib
    ace_napi.z
)

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



??有了LOG,我們先簡單定義四個事件回調(diào)函數(shù):

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函數(shù)
    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;
    }

    //調(diào)用函數(shù)獲取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中設(shè)置回調(diào):

#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,其生命周期在出函數(shù)后就會結(jié)束,結(jié)束后里面注冊的回調(diào)也會找不到(想不到華為這個函數(shù)存的竟然是結(jié)構(gòu)體的地址而不是轉(zhuǎn)存回調(diào)地址)實際編寫中,renderCallback未必是static的,但一定是持久持有在某處內(nèi)存中,肯定不能是函數(shù)中聲明的暫態(tài)對象。
??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})

??此時可以先編譯并部署到設(shè)備中查看,是一個黑屏的app,且在開啟時會輸出OnSurfaceCreated回調(diào)中的logOnSurfaceCrateCB,關(guān)閉app會輸出OnSurfaceDestroyed中的logOnSurfaceDestroyedCB

三、EGL初始化

??EGL用于管理窗口和上下文,獲取渲染的backbuffer,我們在OnSurfaceCreatedCB回調(diào)中可以獲取到窗口,在這里初始化EGL,在這里啟動渲染線程并初始化EGL。
??我定義了一個新類:

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

??在OnSurfaceCreatedCB中設(shè)置窗口和組件,并啟動渲染線程:

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));
}

??渲染線程循環(huán)執(zhí)行渲染,不過我們先提供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初始化沒有區(qū)別:

#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;
}

??現(xiàn)階段就是一個循環(huán)輸出LOG黑屏的應(yīng)用。

四、GLES渲染編寫

??GLES的編寫也沒有任何區(qū)別,我直接從一個安卓實例拷貝出一個簡單的GLES工程,不需要仔細看其中的邏輯,只要保證初始化、更新、銷毀邏輯對應(yīng)上即可:

#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";

??一些簡單的代碼設(shè)施:

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;
}

??初始化資產(chǎn):

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);
}

??銷毀資產(chǎn)

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;
}

??將上述邏輯與現(xiàn)有代碼結(jié)合,首先是初始化和銷毀:

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

??這樣就簡單將一個安卓下的圖形示例移植到鴻蒙下:


總結(jié)

??在渲染功能這一塊,安卓和鴻蒙的寫法差異主要體現(xiàn)在window的獲取上,egl和gles上沒有什么區(qū)別。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容