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。

其他参考文章:

时间: 2024-10-07 06:39:00

PPAPI中使用OpenGL ES绘图的相关文章

Android OpenGL ES绘图教程之一 : 构建OpenGL ES 环境

为了在Android应用中使用OpenGL ES绘图,首先必须要创建一个view容器.一个最简单的方法是实现GLSurfaceView和GLSurfaceView.Renderer.GLSurfaceView一个view容器,用来显示OpenGL绘制的图形,GLSurfaceView.Renderer用来控制GLSurfaceView里面图形的绘制.更多的信息,请参考 OpenGL ES 开发向导. GLSurfaceView是将OpenGL ES图形用到应用的一种方式,对于全屏或者接近全屏的图

Android OpenGL ES绘图教程之三 : 绘制图形

在定义了将要被OpenGL绘制的形状之后,你当然想要绘制它们.使用OpenGL ES 2.0绘制图形需要的代码可能比你想象的要多,因为API提供了大量的图形渲染管道控制接口. 这一章将介绍如何使用OpenGL ES 2.0 API绘制上一章中定义的形状 1. 初始化形状 在你做任何的绘制操作之前,你都必须进行初始化和加载计划绘制的形状.除非在执行的过程中形状所在的结构(原坐标)发生变化,你应该在render中的onSurfaceCreated()方法中初始化它们以提高内存和执行效率. publi

在Android中使用OpenGL ES进行开发第(一)节:概念先行

一.前期基础是知识储备笔者计划写三篇文章来详细分析OpenGL ES基础的同时也是入门关键的三个点: ①OpenGL ES是什么?与OpenGL的关系是什么?——概念部分 ②使用OpenGL ES绘制2D/3D图形的第一步:定义图形:——运用部分 ③使用OpenGL ES绘制出②步骤中定义好的图形:——运用部分,难点所在 通过这三篇文章的分析,就像给万丈高楼垫定了基石,万丈高楼平地起,后面利用OpenGLES做各种效果,各种变换都是建立在这三步的图形编程理解之上的. 话不多说正文开始 (1)什么

Android OpenGL ES绘图教程之六 :响应触摸事件

使对象根据预设的程序进行运动,比如旋转三角形,可以吸引人的注意力.但是如果你想让用户同你的OpenGL ES图形进行交互会怎么样呢?使你的OpenGL ES应用程序触摸互动的关键是要扩展GLSurfaceView,复写onTouchEvent()方法,来监听touch事件.本教程展示了,如何监听透出事件,让用户旋转一个OpenGL ES对象. 1.   设置一个Touch Listener 为了使你的OpenGL ES应用响应touch事件,你必须在GLSurfaceView类中实现OnTouc

如何使用Android中的OpenGL ES媒体效果

Android的媒体效果框架允许开发者可以很容易的应用多种令人印象深刻的视觉效果到照片或视频之上.作为这个媒体效果的框架,它使用GPU来处理图片处理的过程,它仅仅接收OpenGL的纹理(texture)作为输入.在本次教程中,你将会学习到如何使用OpenGL ES2.0将图片资源转化为纹理,以及如何使用框架为图片应用不同的处理效果. 准备 为了开始本次的教程,你必须具备: 1.一款支持Android开发的IDE,如果你没有的话,可以在Android Developer website下载最新版本

Android OpenGL ES绘图教程之二 : 定义形状

在OpenGL ES view中可以定义要绘制图形的形状,是你创建高端图形杰作的第一步.在不知道一些基础的情况下来绘制会有点棘手,比如OpenGL ES是如何定义图形对象的. 本教程解释了OpenGL ES坐标系统与Android设备屏幕的关系,如果定义基础的形状,比如三角形和四边形. 1. 定义一个三角形 OpenGL ES允许你在三维坐标系统中定义绘制对象,所以在绘制三角形之前,必须定义它的坐标.在OpenGL里面,定义坐标的典型方式是定义一个浮点型的顶点坐标数组,为了最大化效率,将这些坐标

OpenGL ES总结(六)OpenGL ES中EGL

Agenda: EGL是什么? EGL数据类型 EGL在Android中应用 EGL的工作流程 GLSurfaceView与EGL区别 简单Demo EGL是什么? EGL? is an interface between Khronos rendering APIs such as OpenGL ES or OpenVG and the underlying native platform window system. It handles graphics context managemen

Develop -- Training(十六) -- 显示绘图和OpenGL ES

Android framework提供了许多标准的工具,来创建有吸引力的.功能丰富的用户图形界面.但是,如果你想要更多的控制权,比如在应用程序的屏幕上绘图,或者冒险进入三维图形,你需要使用不同的工具.通过Android framework提供的OpenGL ES的API提供了一套显示高端的工具,动画图像超出你的想象,许多Android设备的图像处理单元得到了加速(GPUs). 这节课主要开发一个OpenGL应用程序.包括设置.画对象.移动对象元素.响应触摸输入事件. 这节课的示例代码使用的是Op

android graphic(14)—EGL和OpenGL ES之间的关系

OpenGL ES EGL 例子 EGL加载OpenGL ES库 涉及的库 库的加载 小结 OpenGL ES 什么是OpenGL? Open Graphics Library (OpenGL) is a cross-language, cross-platform application programming interface (API) for rendering 2D and 3D vector graphics. The API is typically used to intera