Skip to content

cgeffect/Web-Electron-Wasm-OpenGL

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PixelForge

PixelForge 是一个跨端渲染工程模板(是web端图文编辑器的一种实践方案, 不是一个完整的项目),重点是要讲清楚三件事:

  • 如何用同一套 C++ 渲染代码同时支持 Native 和 Web。
  • 如何通过 Emscripten 把 OpenGL/SDL 代码编译成浏览器可运行的 WASM。
  • 如何接入第三方静态库(.a),并在 Web/Native 分别完成链接。
Native Web
Native Demo Web Demo

架构说明

  • src/core/engine.cpp
    • 渲染核心,实现分层场景(图片层 + 文本层)并在底层合成。
  • src/platform/sdl_runtime.cpp
    • SDL 窗口生命周期、GL Context、主循环、启动时场景下发。
  • src/api/pixelforge_c_api.cpp + src/include/pf/pixelforge_c_api.h
    • 对外 C ABI(pf_scene_begin / pf_scene_add_image_rgba / pf_scene_add_text_utf8)。
  • web/app.js
    • Web 调用方:解析协议、加载资源、调用 WASM 导出函数。
  • scripts/build_web.sh
    • Web 构建入口:em++ 编译、链接第三方 .a、预加载资源到虚拟文件系统。
  • src/third-party/thorvg/build.sh / src/third-party/thorvg/build_web.sh
    • 第三方库 ThorVG 的 Native / WASM 静态库构建脚本。

为什么着色器要区分 Web / Native

src/core/engine.cpp 里的着色器源码使用了 #ifdef __EMSCRIPTEN__ 条件编译,这是有必要的。
原因不是渲染逻辑不同,而是目标平台使用的 GLSL 方言不同:

  • Web(Emscripten + WebGL / GLES2)

    • 使用 GLSL ES 风格
    • 片元着色器通常需要精度声明(例如 precision mediump float;
    • 不使用 desktop GLSL 的 #version 120 写法
  • Native(SDL + OpenGL 2.1)

    • 使用 desktop GLSL 1.20 风格
    • 通常会写 #version 120
    • 不使用 GLES 的精度声明语法

因此当前实现采用“同一套渲染逻辑 + 两套着色器字符串头部语法”来兼容两端。
如果后续要进一步统一,可以改为“共享主体代码 + 按平台注入 shader header”的方式,但底层仍需要区分目标 GLSL 语法。

如何编译浏览器可用 WASM

  1. 准备 Emscripten 环境(确保 em++ 可用)。
  2. 执行 Web 构建脚本:
cd PixelForge
source ../emsdk/emsdk_env.sh
bash scripts/build_web.sh
  1. 启动静态服务预览:
cd dist/web
python3 -m http.server 8000

浏览器打开 http://localhost:8000

build_web.sh 的关键职责:

  • em++ 编译 engine.cppsdl_runtime.cpppixelforge_c_api.cpp 等源码。
  • 通过 -s EXPORTED_FUNCTIONS 暴露 C API 给 JS 调用。
  • 通过 --preload-file 预加载字体/协议资源到 WASM 文件系统。
  • 链接 WASM 版本第三方静态库(例如 deploy-wasm/lib/libthorvg-1.a)。

Web 渲染数据链路(app.js -> WASM -> OpenGL -> Canvas)

这部分描述的是“如何接入”而不是 OpenGL 理论。

1) app.js 如何接入 WASM

web/app.js 通过 createPixelForgeModule(...) 初始化运行时,并把页面上的 canvas 传给 Emscripten。
之后 SDL/WebGL 会直接渲染到这个 canvas。

关键点:

  • locateFile 用来定位 .wasm 文件
  • canvas 指定渲染目标
  • ccall 用于调用 C 导出函数(pf_scene_*

2) JS 的 RGBA 如何传到 C++

图片层流程:

  1. JS fetch 图片并绘制到临时 2D canvas
  2. getImageData() 得到 RGBA 字节数组
  3. 在 WASM 堆里 _malloc 一段内存
  4. module.HEAPU8.set(...) 把 JS 字节拷贝到 WASM 内存
  5. ccall("pf_scene_add_image_rgba", ...) 把指针和尺寸传给 C++
  6. 调用后 _free 释放 WASM 内存

文本层流程:

  • 先走 pf_scene_add_text_utf8
  • 如果底层文字渲染失败,web 会 fallback:在 JS 2D canvas 里栅格化文本,再按图片层方式走 pf_scene_add_image_rgba

3) OpenGL 纹理如何显示到网页 canvas

底层 C++ 渲染是两步:

  1. 将图层绘制到离屏 FBO(layer FBO)
  2. 将 FBO 颜色纹理全屏绘制到默认 framebuffer

最后 SDL 调用 SDL_GL_SwapWindow 提交帧。
在 Emscripten 环境下,这个默认 framebuffer 对应的就是你传入的 DOM canvas,所以浏览器页面能看到最终画面。

简化理解:

  • JS 负责“喂数据”(协议、RGBA、交互)
  • WASM/C++ 负责“渲染合成”(图层、FBO、着色器)
  • Canvas 只是最终显示目标

如何使用第三方 .a 静态库

项目中第三方库分为两类:

  • Native 静态库:如 src/third-party/thorvg/deploy/lib/libthorvg-1.a
  • WASM 静态库:如 src/third-party/thorvg/deploy-wasm/lib/libthorvg-1.a

接入原则:

  • Native 构建(CMake)链接 Native .a
  • Web 构建(Emscripten)只能链接 WASM 目标 .a,不能复用 arm64/x86 的 Native .a

推荐流程:

  1. 先构建第三方库
    • Native:bash src/third-party/thorvg/build.sh
    • Web:bash src/third-party/thorvg/build_web.sh
  2. 在主工程构建脚本里加 include + 链接
    • Web:在 scripts/build_web.sh 中添加 -I... 和对应 .a
    • Native:在 CMakeLists.txttarget_link_libraries(...)
  3. 统一 API 调用层,不在上层分叉渲染逻辑。

Native 构建

cd PixelForge
bash scripts/build_native.sh
./build/pixelforge

依赖:SDL2、OpenGL,以及已构建好的 Native 第三方静态库。

About

该项目是一个跨端渲染工程模板, 如何在web端使用OpenGL

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors