emrun 启动 Canvas 空白的根本原因是 Emscripten 默认不自动创建 WebGL 上下文,需手动调用 emscripten_webgl_init 或显式声明 canvas 并配置上下文属性,禁用原生 GLES 链接,同步 canvas 尺寸与 glViewport,并采用零拷贝传递顶点数据。

emrun 启动时 Canvas 为空白,glCreateContext 失败
根本原因不是 WebGL 初始化失败,而是 Emscripten 默认不自动创建 WebGL 上下文。它只提供底层 glue code,上下文需手动申请或由 emscripten_set_webgl_context_lost_callback 等显式触发。
实操建议:
- 必须在 C++ 主函数中调用
emscripten_set_main_loop前,先调用emscripten_webgl_init(Emscripten 3.1.54+)或更稳妥地:在 HTML 模板中显式声明,并在编译时加-s GL_ENABLE_GET_CONTEXT_ATTRIBUTES=1 - 避免依赖
glfwInit或SDL_Init—— 它们在 Emscripten 下默认禁用 OpenGL ES 后端;改用emscripten_webgl_create_context手动创建上下文,并传入EmscriptenWebGLContextAttributes结构体指定alpha: 0、depth: 1等关键字段 - 检查浏览器控制台是否报
WebGL: INVALID_OPERATION: makeCurrent: context is lost—— 这通常意味着 canvas 尚未 attach 到 DOM,或EMSCRIPTEN_WEBGL_CONTEXT_HANDLE未正确传递给glMakeCurrent
链接 libGLESv2 报错:undefined symbol: glClear
Emscripten 不提供原生 GLES 库,所有 OpenGL ES 调用都必须通过其内置的 WebGL 绑定层翻译。直接链接系统 libGLESv2.so 或使用 -lGLESv2 会彻底失败。
实操建议:
- 编译时**禁止**添加任何
-lGLESv2、-lEGL或-lglfw(除非用emrun --proxy-port配合本地 GLFW 测试) - 头文件仍可包含
,但必须确保使用 Emscripten 自带的版本(路径类似$EMSDK/upstream/emscripten/system/include/GLES2/gl2.h),而非安卓 NDK 或树莓派 SDK 的副本 - 启用兼容模式:加编译参数
-s LEGACY_GL_EMULATION=1可让部分旧 GLES 1.1 调用(如glOrthof)被模拟,但代价是额外 JS 层开销,仅用于迁移遗留代码
Canvas 尺寸变化后渲染错位或拉伸
WebGL 渲染缓冲区尺寸(glViewport)和 canvas 元素的 CSS 尺寸、DOM 尺寸三者不一致,是常见视觉错误根源。Emscripten 不自动同步这三者。
把任意文章或文档内容转换成精美的全屏翻页 HTML 演示文件(幻灯片风格)。当用户发来一篇文章、文档链接、Markdown 内容、或粘贴的文本,并且希望生成演示用的 HTML 时触发。支持任意内容来源:iWiki 链接、KM 链接、本地文件路径、直接粘贴的文本。生成的 HTML 文件保存到本地工作区,并通过企微直接发送给用户。触发词示例:帮我把这篇文章做成PPT、转成演示文档、生成HTML幻灯片、做个演示、转成可以演示的格式、article to deck、make a slide。
实操建议:
- 监听
resize事件时,必须同时调用:emscripten_get_canvas_element_size获取真实像素尺寸 →glViewport(0, 0, width, height)→glScissor(如启用剪裁) - 避免仅靠 CSS 设置
canvas { width: 100%; height: 100vh; }—— 这会改变显示尺寸但不改变帧缓冲分辨率,导致像素拉伸;应在 JS 中用canvas.width = canvas.clientWidth * window.devicePixelRatio显式重设缓冲区 - 若使用
-s FULLSCREEN=1,需配合-s RESIZABLE=1并在 C++ 中注册emscripten_set_resize_callback,否则 resize 后glGetError()可能返回GL_INVALID_FRAMEBUFFER_OPERATION
从 C++ 向 JS 传递顶点数据太慢,glBufferData 卡顿
频繁跨语言拷贝大块内存(如每帧传 MB 级顶点数组)会触发 JS 引擎的 ArrayBuffer 复制和 GC 压力,比原生慢 5–10 倍。
实操建议:
- 优先使用 Emscripten 的「零拷贝」机制:用
malloc分配内存 →emscripten_run_script将指针转为 JS 的HEAPF32.subarray(ptr >> 2, ptr >> 2 + count)→ 直接传给gl.bufferData - 避免在循环中反复调用
emscripten_run_script;把 JS 逻辑封装成一个函数(如window.updateVBO),C++ 侧只传指针和长度整数 - 对静态几何体,用
-s EXPORTED_FUNCTIONS='["_init_geometry"]'导出初始化函数,在页面加载后一次性上传,运行时只调用glDrawArrays
最易被忽略的是:WebGL 上下文丢失后,所有 VBO/IBO/Texture ID 都失效,但 C++ 侧指针仍可读写——此时继续调用 glDraw* 不报错,却渲染黑屏。务必在 main_loop 开头插入 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) return; 做兜底检测。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










