💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
在[**PPAPI中使用Chromium的3D图形接口**](http://blog.csdn.net/foruok/article/details/50563676)一文中我们介绍了怎么使用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/](https://www.khronos.org/opengles/)。 # PPAPI中使用OpenGL ES2 在[**PPAPI中使用Chromium的3D图形接口**](http://blog.csdn.net/foruok/article/details/50563676)一文中我们提到了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图形接口**](http://blog.csdn.net/foruok/article/details/50563676)一文做了一些补充说明,了解了在PPAPI中可以使用OpenGL ES 2.0的接口来进行3D绘图,这种方式比基于软件和共享内存的Graphics 2D接口效率要高,它利用了GPU加速。接下来看看怎么使用gl接口来绘制一个简单的三角形。 # 绘制三角形 修改了ppapi_hello_gles实例,添加了glTriangle.h和glTriangle.c两个文件,它们实现了三角形绘制。大部分代码是我从《[**OpenGL ES 2.0 Programming Guide**](http://opengles-book.com/)》一书中摘出来的,针对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**](http://opengles-book.com/)》一书,现在最新的书应该是《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。 其他参考文章: - [**CEF Windows开发环境搭建**](http://blog.csdn.net/foruok/article/details/50468642) - [**CEF加载PPAPI插件**](http://blog.csdn.net/foruok/article/details/50485448) - [**VS2013编译最简单的PPAPI插件**](http://blog.csdn.net/foruok/article/details/50485461) - [**理解PPAPI的设计**](http://blog.csdn.net/foruok/article/details/50486788) - [**PPAPI插件与浏览器的交互过程**](http://blog.csdn.net/foruok/article/details/50494061) - [**Windows下从源码编译CEF**](http://blog.csdn.net/foruok/article/details/50498740) - [**编译PPAPI的media_stream_video示例**](http://blog.csdn.net/foruok/article/details/50498873) - [**PPAPI插件的绘图与输入事件处理**](http://blog.csdn.net/foruok/article/details/50499813) - [**在PPAPI插件中创建本地窗口**](http://blog.csdn.net/foruok/article/details/50513228) - [**PPAPI插件与浏览器的通信**](http://blog.csdn.net/foruok/article/details/50513315) - [**Windows下从源码编译Skia**](http://blog.csdn.net/foruok/article/details/50524726) - [**在PPAPI插件中使用Skia绘图**](http://blog.csdn.net/foruok/article/details/50526110) - [**加载DLL中的图片资源生成Skia中的SkBitmap对象**](http://blog.csdn.net/foruok/article/details/50543762) - [**PPAPI+Skia实现的涂鸦板**](http://blog.csdn.net/foruok/article/details/50547737) - [**PPAPI中使用Chromium的3D图形接口**](http://blog.csdn.net/foruok/article/details/50563676)