PPAPI中使用OpenGL ES绘图

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PPAPI中使用OpenGL ES绘图相关的知识,希望对你有一定的参考价值。

PPAPI中使用Chromium的3D图形接口一文中我们介绍了怎么使用PPB_Graphics3D接口,提供了一个简单示例,单机鼠标可以变换插件颜色。

foruok原创,如需转载请关注foruok的微信订阅号“程序视界”联系foruok。

PPB_Graphics3D是Chromium暴露给PPAPI的3D图形接口,类似衔接Open GL和本地窗口系统的EGL。我们使用PPB_Graphics3D的Create方法来创建context,然后使用PPB_Instance的BindGraphics将得到的OpenGL Context和插件实例绑定,再接下来,就可以使用OpenGL来绘图了。在这个过程中,PPB_Graphics3D替代了EGL。

Chromium使用的应该是WebGL,也就是OpenGL ES 2.0 for the Web,它暴露给PPAPI的gl接口是C结构体加函数指针的形式,有PPB_OpenGLES2、PPB_OpenGLES2FramebufferBlit、PPB_OpenGLES2ChromiumEnableFeature等,搜索ppb_opengles2*.h即可查看对应的接口定义。

关于OpenGL ES,可以看这里:https://www.khronos.org/opengles/

PPAPI中使用OpenGL ES2

PPAPI中使用Chromium的3D图形接口一文中我们提到了glInitializePPAPI和glSetCurrentContextPPAPI两个方法,解释一下。

  • glInitializePPAPI

glInitializePPAPI做的事情就是把浏览器暴露给PPAPI的各种gl相关的接口都拿到(通过PPB_GetInterface),保存在全局变量中(参看gl2ext_ppapi.c),后续我们使用glXXX(参看gles2.c)函数时,实际上是通过保存下来的接口(结构体+函数指针),调用函数指针来实现的。我们用到的glXXX是宏定义,看起来和OpenGL的API一致,用起来方便。

我在ppapi_hello_gles.c的PPP_InitializeModule方法中添加了下面的代码:

if (GL_TRUE != glInitializePPAPI(get_browser_interface))
    return -1;

上面的代码初始化PPAPI相关的gl接口。

  • glSetCurrentContextPPAPI

PPB_Graphics3D的Create方法创建一个图形上下文,OpenGL就用它绘图。glSetCurrentContextPPAPI方法(参看gl2ext_ppapi.c)需要的参数就是PPB_Graphics3D接口Create出来的那个context。

ppapi_hello_gles.c中的MakeAndBindGraphics3D函数创建了Graphics3D context,并调用glSetCurrentContextPPAPI来传递给封装gl C接口的模块:

PP_Resource MakeAndBindGraphics3D(PP_Instance instance,
    const struct PP_Size* size) {
    PP_Resource graphics;
    int32_t attribs[] = { PP_GRAPHICS3DATTRIB_WIDTH, 800,
        PP_GRAPHICS3DATTRIB_HEIGHT, 800,
        PP_GRAPHICS3DATTRIB_NONE };
    graphics = g_graphics_3d_interface->Create(instance, 0, attribs);
    if (!graphics)
        return 0;

    if (!g_instance_interface->BindGraphics(instance, graphics)) {
        g_core_interface->ReleaseResource(graphics);
        return 0;
    }

    glSetCurrentContextPPAPI(graphics);

    return graphics;
}

好啦,我们对PPAPI中使用Chromium的3D图形接口一文做了一些补充说明,了解了在PPAPI中可以使用OpenGL ES 2.0的接口来进行3D绘图,这种方式比基于软件和共享内存的Graphics 2D接口效率要高,它利用了GPU加速。接下来看看怎么使用gl接口来绘制一个简单的三角形。

绘制三角形

修改了ppapi_hello_gles实例,添加了glTriangle.h和glTriangle.c两个文件,它们实现了三角形绘制。大部分代码是我从《OpenGL ES 2.0 Programming Guide》一书中摘出来的,针对PPAPI做了修改。

glTriangle.h如下:

#ifndef GLTRIANGLE_DRAW_H
#define GLTRIANGLE_DRAW_H
#include "ppapi/lib/gl/include/GLES2/gl2.h"

GLboolean InitTriangle(void **userData);
void DrawTriangle(void *userData);

#endif

声明了两个函数,ppapi_hello_gles.c中会用到。

glTriangle.c如下:

/* Copyright (c) 2016 foruok. All rights reserved.
* 欢迎关注我的微信订阅号程序视界
* see: 《OpenGL ES 2.0 Programming Guide》
*/
#include "glTriangle.h"
#include <Windows.h>
#include "ppapi/lib/gl/include/GLES2/gl2.h"
#include "ppapi/lib/gl/gles2/gl2ext_ppapi.h"
#include "ppapi/lib/gl/include/GLES2/gl2ext.h"
#include <tchar.h>

typedef struct
{
    GLuint programObject;
} UserData;

/*
 * create a shader object, load the shader source, and compile the shader
 */
GLuint LoadShader(GLenum type, const char *shaderSource)
{
    GLuint shader;
    GLint compiled;

    // create the shader object
    shader = glCreateShader(type);
    if (shader == 0)
        return 0;

    // load the shader source
    glShaderSource(shader, 1, &shaderSource, NULL);

    // compile the shader
    glCompileShader(shader);

    // check the compile status
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
    if (!compiled)
    {
        GLint infoLen = 0;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
        if (infoLen > 1)
        {
            char *infoLog = malloc(infoLen+1);
            glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
            infoLog[infoLen] = 0;
            OutputDebugStringA(infoLog);
            free(infoLog);
        }

        glDeleteShader(shader);
        return 0;
    }

    return shader;
}

/*
 * Initialize the shader and program object
 */
GLboolean InitTriangle(void **userData)
{
    GLbyte vShaderStr[] =
        "attribute vec4 vPosition; \n"
        "void main() \n"
        "{ \n"
        "    gl_Position = vPosition; \n"
        "} \n";

    GLbyte fShaderStr[] =
        "precision mediump float; \n"
        "void main() \n"
        "{ \n"
        "    gl_FragColor = vec4(1.0, 0.5, 0.0, 1.0); \n"
        "} \n";

    // load shaders
    GLuint vertexShader = LoadShader(GL_VERTEX_SHADER, vShaderStr);
    GLuint fragmentShader = LoadShader(GL_FRAGMENT_SHADER, fShaderStr);

    // create the program object
    GLuint programObject = glCreateProgram();
    if (programObject == 0) return GL_FALSE;

    glAttachShader(programObject, vertexShader);
    glAttachShader(programObject, fragmentShader);

    // blind vPosition to attribute 0
    glBindAttribLocation(programObject, 0, "vPosition");

    // link the program
    glLinkProgram(programObject);

    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    // check link status
    GLint linked;
    glGetProgramiv(programObject, GL_LINK_STATUS, &linked);
    if (!linked)
    {
        GLint infoLen = 0;
        glGetProgramiv(programObject, GL_INFO_LOG_LENGTH, &infoLen);
        if (infoLen > 1)
        {
            char *infoLog = malloc(infoLen + 1);
            glGetProgramInfoLog(programObject, infoLen, NULL, infoLog);
            infoLog[infoLen] = 0;
            OutputDebugStringA(infoLog);
            free(infoLog);
        }

        glDeleteProgram(programObject);
        return GL_FALSE;
    }
    UserData *data = *userData;
    if (data == 0)
    {
        data = (UserData*)malloc(sizeof(UserData));
        *userData = data;
    }
    data->programObject = programObject;

    return GL_TRUE;
}

void DrawTriangle(void *userData)
{
    OutputDebugString(_T("DrawTriagle\r\n"));
    UserData *data = userData;

    GLfloat vVertices[] = {
        0.0f, 0.5f, 0.0f,
        -0.5f, -0.5f, 0.0f,
        0.5f, -0.5f, 0.0f
    };

    // use the program object
    glUseProgram(data->programObject);

    // load the vertex data
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vVertices);
    glEnableVertexAttribArray(0);

    glDrawArrays(GL_TRIANGLES, 0, 3);
}

glTriangle.c的三个函数都来自《OpenGL ES 2.0 Programming Guide》一书,现在最新的书应该是《OpenGL ES 3.0 Programming Guide》。详细的代码解说请参考该书,非常详尽、有条理,超级棒!

最后,我修改了ppapi_hello_gles.c,以便使用glTriangle的接口。新的ppapi_hello_gles.c内容如下:

/* Copyright (c) 2016 foruok. All rights reserved.
* 欢迎关注我的微信订阅号程序视界
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <Windows.h>
#include <tchar.h>

#include "ppapi/c/pp_completion_callback.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/c/pp_instance.h"
#include "ppapi/c/pp_module.h"
#include "ppapi/c/pp_rect.h"
#include "ppapi/c/pp_var.h"
#include "ppapi/c/ppb.h"
#include "ppapi/c/ppb_core.h"
#include "ppapi/c/ppb_instance.h"
#include "ppapi/c/ppb_view.h"
#include "ppapi/c/ppp.h"
#include "ppapi/c/ppp_instance.h"
#include "ppapi/c/ppb_input_event.h"
#include "ppapi/c/ppp_input_event.h"
#include "ppapi/c/ppb_graphics_3d.h"
#include "ppapi/c/ppb_opengles2.h"
#include "ppapi/lib/gl/include/GLES2/gl2.h"
#include "ppapi/lib/gl/gles2/gl2ext_ppapi.h"
// foruok[1]
#include "glTriangle.h"

PPB_GetInterface g_get_browser_interface = NULL;

const PPB_Core* g_core_interface;
const PPB_Graphics3D* g_graphics_3d_interface;
const PPB_Instance* g_instance_interface;
const PPB_View* g_view_interface;
const PPB_InputEvent *g_input_interface;
const PPB_MouseInputEvent *g_mouse_interface;

/* PPP_Instance implementation -----------------------------------------------*/

typedef struct InstanceInfo {
    PP_Instance pp_instance;
    struct PP_Size last_size;
    PP_Resource graphics;
    // foruok[2]
    void *user_data;

    struct InstanceInfo* next;
} InstanceInfo;

/** Linked list of all live instances. */
struct InstanceInfo* all_instances = NULL;

/** Returns a refed resource corresponding to the created graphics 3d. */
PP_Resource MakeAndBindGraphics3D(PP_Instance instance,
    const struct PP_Size* size) {
    PP_Resource graphics;
    int32_t attribs[] = { PP_GRAPHICS3DATTRIB_WIDTH, 800,
        PP_GRAPHICS3DATTRIB_HEIGHT, 800,
        PP_GRAPHICS3DATTRIB_NONE };
    graphics = g_graphics_3d_interface->Create(instance, 0, attribs);
    if (!graphics)
        return 0;

    if (!g_instance_interface->BindGraphics(instance, graphics)) {
        g_core_interface->ReleaseResource(graphics);
        return 0;
    }

    glSetCurrentContextPPAPI(graphics);

    return graphics;
}


void ReinitializeGraphics3D(void *user_data, int32_t result)
{
    InstanceInfo *inst = (InstanceInfo*)user_data;
    inst->graphics = MakeAndBindGraphics3D(inst->pp_instance, &inst->last_size);
    if (inst->graphics != 0)
    {
        // foruok[5]
        if(inst->user_data)InitTriangle(inst->user_data);

        OutputDebugString(_T("reinitialize graphics 3d context sucess\r\n"));
    }
}

void FlushCompletionCallback(void* user_data, int32_t result) {
    /* Don‘t need to do anything here. */
    if (result == PP_ERROR_CONTEXT_LOST)
    {
        OutputDebugString(_T("PP_ERROR_CONTEXT_LOST"));
        //reinitialize context
        g_core_interface->CallOnMainThread(0, PP_MakeCompletionCallback(ReinitializeGraphics3D, user_data), 0);
    }
}

unsigned int g_colors[4] = { 0xFF0000FF, 0xFFFF00FF, 0xFF00FFFF, 0xFF2AFE00 };
unsigned int g_color_index = 0;
#define GETA(clr) ((clr >> 24) & 0xFF)
#define GETR(clr) ((clr >> 16) & 0xFF)
#define GETG(clr) ((clr >> 8) & 0xFF)
#define GETB(clr) (clr & 0xFF)

void Repaint(struct InstanceInfo* instance, const struct PP_Size* size) {
    /* Ensure the graphics 3d is ready. */
    if (!instance->graphics) {
        instance->graphics = MakeAndBindGraphics3D(instance->pp_instance, size);
        if (!instance->graphics)
            return;

        // foruok[3]
        if (GL_TRUE == InitTriangle(&instance->user_data))
            OutputDebugString(_T("InitTriangle OK\r\n"));
    }

    g_color_index++;
    if (g_color_index >= sizeof(g_colors) / sizeof(g_colors[0])) g_color_index = 0;

    struct PP_CompletionCallback callback = {
        FlushCompletionCallback, instance, PP_COMPLETIONCALLBACK_FLAG_NONE,
    };
    glViewport(0, 0, instance->last_size.width, instance->last_size.height);

    glClearColor(GETR(g_colors[g_color_index]),
        GETG(g_colors[g_color_index]),
        GETB(g_colors[g_color_index]),
        GETA(g_colors[g_color_index]));

    glClear(GL_COLOR_BUFFER_BIT);

    // foruok[4]
    if(instance->user_data)DrawTriangle(instance->user_data);

    g_graphics_3d_interface->SwapBuffers(instance->graphics, callback);
}

/** Returns the info for the given instance, or NULL if it‘s not found. */
struct InstanceInfo* FindInstance(PP_Instance instance) {
    struct InstanceInfo* cur = all_instances;
    while (cur) {
        if (cur->pp_instance == instance)
            return cur;
        cur = cur->next;
    }
    return NULL;
}

PP_Bool Instance_DidCreate(PP_Instance instance,
    uint32_t argc,
    const char* argn[],
    const char* argv[]) {
    struct InstanceInfo* info =
        (struct InstanceInfo*)calloc(1, sizeof(struct InstanceInfo));
    info->pp_instance = instance;

    /* Insert into linked list of live instances. */
    info->next = all_instances;
    all_instances = info;

    g_input_interface->RequestInputEvents(instance, PP_INPUTEVENT_CLASS_MOUSE);
    g_input_interface->RequestFilteringInputEvents(instance, PP_INPUTEVENT_CLASS_MOUSE);

    OutputDebugString(_T("Instance_DidCreate\r\n"));

    return PP_TRUE;
}

void Instance_DidDestroy(PP_Instance instance) {
    /* Find the matching item in the linked list, delete it, and patch the
    * links.
    */
    struct InstanceInfo** prev_ptr = &all_instances;
    struct InstanceInfo* cur = all_instances;
    while (cur) {
        if (instance == cur->pp_instance) {
            *prev_ptr = cur->next;
            g_core_interface->ReleaseResource(cur->graphics);
            free(cur);
            return;
        }
        prev_ptr = &cur->next;
        cur = cur->next;
    }
}

void Instance_DidChangeView(PP_Instance pp_instance,
    PP_Resource view) {
    struct PP_Rect position;
    struct InstanceInfo* info = FindInstance(pp_instance);
    if (!info)
        return;

    if (g_view_interface->GetRect(view, &position) == PP_FALSE)
        return;

    if (info->last_size.width != position.size.width ||
        info->last_size.height != position.size.height) {
        info->last_size.width = position.size.width;
        info->last_size.height = position.size.height;
        /* Got a resize, repaint the plugin. */
        Repaint(info, &position.size);
    }

    OutputDebugString(_T("Instance_DidChangeView\r\n"));
}

void Instance_DidChangeFocus(PP_Instance pp_instance, PP_Bool has_focus) {
}

PP_Bool Instance_HandleDocumentLoad(PP_Instance pp_instance,
    PP_Resource pp_url_loader) {
    return PP_FALSE;
}

static PPP_Instance instance_interface = {
    &Instance_DidCreate,
    &Instance_DidDestroy,
    &Instance_DidChangeView,
    &Instance_DidChangeFocus,
    &Instance_HandleDocumentLoad
};

PP_Bool InputEvent_HandleInputEvent(PP_Instance instance, PP_Resource input_event)
{
    struct PP_Point pt;
    TCHAR szLog[512] = { 0 };
    switch (g_input_interface->GetType(input_event))
    {
    case PP_INPUTEVENT_TYPE_MOUSEDOWN:
        pt = g_mouse_interface->GetPosition(input_event);
        _stprintf_s(szLog, 512, _T("InputEvent_HandleInputEvent, mouse down at [%d, %d]\r\n"), pt.x, pt.y);
        OutputDebugString(szLog);
        break;
    default:
        return PP_FALSE;
    }
    struct InstanceInfo* info = FindInstance(instance);
    if (info && info->last_size.width > 0)
    {
        Repaint(info, &info->last_size);
    }
    return PP_TRUE;
}

static PPP_InputEvent input_interface = {
    &InputEvent_HandleInputEvent
};

/* Global entrypoints --------------------------------------------------------*/

PP_EXPORT int32_t PPP_InitializeModule(PP_Module module,
    PPB_GetInterface get_browser_interface) {
    g_get_browser_interface = get_browser_interface;

    g_core_interface = (const PPB_Core*)
        get_browser_interface(PPB_CORE_INTERFACE);
    g_instance_interface = (const PPB_Instance*)
        get_browser_interface(PPB_INSTANCE_INTERFACE);
    g_graphics_3d_interface = (const PPB_Graphics3D*)
        get_browser_interface(PPB_GRAPHICS_3D_INTERFACE);
    g_view_interface = (const PPB_View*)
        get_browser_interface(PPB_VIEW_INTERFACE);
    g_input_interface = (const PPB_InputEvent*)get_browser_interface(PPB_INPUT_EVENT_INTERFACE);
    g_mouse_interface = (const PPB_MouseInputEvent*)get_browser_interface(PPB_MOUSE_INPUT_EVENT_INTERFACE);

    if (!g_core_interface || !g_instance_interface ||
        !g_graphics_3d_interface || !g_view_interface ||
        !g_input_interface || !g_mouse_interface)
        return -1;

    if (GL_TRUE != glInitializePPAPI(get_browser_interface))
        return -1;

    OutputDebugString(_T("PPP_InitializeModule\r\n"));
    return PP_OK;
}

PP_EXPORT void PPP_ShutdownModule() {
}

PP_EXPORT const void* PPP_GetInterface(const char* interface_name) {
    if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0)
    {
        OutputDebugString(_T("PPP_GetInterface, instance_interface\r\n"));
        return &instance_interface;
    }
    else if (strcmp(interface_name, PPP_INPUT_EVENT_INTERFACE) == 0)
    {
        OutputDebugString(_T("PPP_GetInterface, input_interface\r\n"));
        return &input_interface;
    }

    return NULL;
}

改动部分我用foruok[1]、foruok[2]之类的注释标注出来了,共有5处。可以搜索查看。

InitTriangle方法在调用MakeAndBindGraphics3D之后调用,创建了shader、program object。DrawTriangle方法是在Repaint中被调用的,它内部通过glDrawArrays来绘制三角形。


好啦,这个改造过的示例依然很简单,不过可以演示如何在PPAPI中使用OpenGL ES。

其他参考文章:

以上是关于PPAPI中使用OpenGL ES绘图的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL ES 2.0 (iOS) 中的 2D 绘图

在 OpenGL ES 中,如何将绘图“剪辑”到矩形?

OpenGL ES 1.1 clearColor 绘图

Android OpenGL ES 纹理映射/绘图问题 - 倾斜图像

在 OpenGL ES Android 中使用 glBufferSubData 和 glMultiDrawArrays

OpenGL ES之“深度测试”与“模板测试”的使用流程