前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >WASM + OpenGL + C++ 入门:绘制三角形

WASM + OpenGL + C++ 入门:绘制三角形

作者头像
前端西瓜哥
发布2023-10-22 19:19:48
6230
发布2023-10-22 19:19:48
举报
文章被收录于专栏:前端西瓜哥的前端文章

大家好,我是前端西瓜哥。

我在尝试用 C++ 写一段 OpenGL 代码,用 Emscripten 编译成 WASM,运行在浏览器。OpenGL 最后会被 WASM 转换为 WebGL 进行渲染。

先装 Emscripten SDK。安装和入门可以看这篇文章:

wasm 初探,写个 Hello World

红色三角形

还是老样子,图形渲染的 helloworld:画一个红色三角形。

创建一个 red_triangle.cpp 文件,输入以下内容。

代码语言:javascript
复制
#include <functional>

#include <SDL.h>
#include <stdio.h>

#define GL_GLEXT_PROTOTYPES 1
#include <SDL_opengles2.h>

const char *vertexShaderSource =
    "attribute vec4 a_position;\n"
    "void main() {\n"
    "    gl_Position = a_position;\n"
    "}\n";

const char *fragmentShaderSource =
    "precision mediump float;\n"
    "void main() {\n"
    "    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
    "}\n";

int main()
{
  printf("前端西瓜哥正在渲染红色三角形~~~\n");

  // 创建一个 400x300 的画布
  SDL_Window *window;
  SDL_CreateWindowAndRenderer(400, 300, 0, &window, nullptr);

  // 针对 OpenGL ES,表示要生成几个 vao,后面顶点属性绑定 vbo 时会保存到这里
  GLuint vao;
  glGenVertexArraysOES(1, &vao);
  glBindVertexArrayOES(vao);

  // 1. 顶点着色器
  GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
  glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
  glCompileShader(vertexShader);

  // 2. 片元着色器
  GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
  glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
  glCompileShader(fragmentShader);

  // 3. 程序对象
  GLuint program = glCreateProgram();
  // 将着色器附加到程序对象上
  glAttachShader(program, vertexShader);
  glAttachShader(program, fragmentShader);
  glLinkProgram(program);
  glUseProgram(program);

  // 顶点数据
  GLfloat vertices[] = {0.0f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f};

  GLuint vbo;
  glGenBuffers(1, &vbo);
  // 绑定缓冲区到上下文
  glBindBuffer(GL_ARRAY_BUFFER, vbo);
  // 将 顶点数据 复制到 缓冲区
  glBufferData(GL_ARRAY_BUFFER, 24, vertices, GL_STATIC_DRAW);

  // 获取顶点着色器中的 position 属性
  GLint position = glGetAttribLocation(program, "a_position");

  // 设置读取方式
  glVertexAttribPointer(position, 2, GL_FLOAT, GL_FALSE, 0, 0);
  // 启用顶点属性数组
  glEnableVertexAttribArray(position);

  // 清空屏幕
  glClearColor(0, 1, 1, 0);   // 背景色为 #00FFFF
  glClear(GL_COLOR_BUFFER_BIT); // 清空颜色缓存

  glDrawArrays(GL_TRIANGLES, 0, 3); // 绘制三角形

  glDeleteBuffers(1, &vbo);
}

WebGL 代码对照:

一起学 WebGL:绘制三角形

执行下面命令进行编译

代码语言:javascript
复制
emcc red_triangle.cpp -std=c++11 -s WASM=1 -s USE_SDL=2 -O3 -o index.html

效果

更新三角形顶点位置

再尝试通过 JavaScript 给 wasm 通信,更新三角形的顶部的顶点信息然后重新渲染。

我们要暴露方法给 JavaScript 调用,对此需引入 emscripten.h 头文件。

代码语言:javascript
复制
#include <emscripten.h>

然后声明要暴露的方法:

代码语言:javascript
复制
// 定义一个 updateColor 方法给 js 用。全局会出现一个 _updateColor 方法。
// EMSCRIPTEN_KEEPALIVE 宏防止方法编译时被优化掉。
extern "C" void EMSCRIPTEN_KEEPALIVE updateColor(float n1, float n2)
{
  printf("n1: %f, n2: %f\n", n1, n2);
  vertices[0] = n1;
  vertices[1] = n2;
  render();
}

完整 C++ 代码:

代码语言:javascript
复制
#include <functional>

#include <SDL.h>
#include <stdio.h>

#define GL_GLEXT_PROTOTYPES 1
#include <SDL_opengles2.h>
// wasm 需要暴露方法给 js,引入这个头文件
#include <emscripten.h>

const char *vertexShaderSource =
    "attribute vec4 a_position;\n"
    "void main() {\n"
    "    gl_Position = a_position;\n"
    "}\n";

const char *fragmentShaderSource =
    "precision mediump float;\n"
    "void main() {\n"
    "    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
    "}\n";

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

void render()
{
  printf("前端西瓜哥正在渲染红色三角形~~~\n");

  // 创建一个 400x300 的画布
  SDL_Window *window;
  SDL_CreateWindowAndRenderer(400, 300, 0, &window, nullptr);

  GLuint vao;
  glGenVertexArraysOES(1, &vao);
  glBindVertexArrayOES(vao);

  GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
  glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
  glCompileShader(vertexShader);

  GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
  glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
  glCompileShader(fragmentShader);

  GLuint program = glCreateProgram();
  glAttachShader(program, vertexShader);
  glAttachShader(program, fragmentShader);
  glLinkProgram(program);
  glUseProgram(program);

  GLuint vbo;
  glGenBuffers(1, &vbo);
  glBindBuffer(GL_ARRAY_BUFFER, vbo);
  glBufferData(GL_ARRAY_BUFFER, 24, vertices, GL_STATIC_DRAW);

  GLint position = glGetAttribLocation(program, "a_position");
  glVertexAttribPointer(position, 2, GL_FLOAT, GL_FALSE, 0, 0);
  glEnableVertexAttribArray(position);

  glClearColor(0, 1, 1, 0);    
  glClear(GL_COLOR_BUFFER_BIT); 

  glDrawArrays(GL_TRIANGLES, 0, 3);

  glDeleteBuffers(1, &vbo);
}

int main()
{
  render();
}

// 定义一个 updateColor 方法给 js 用。全局会出现一个 _updateColor 方法。
// EMSCRIPTEN_KEEPALIVE 宏防止方法编译时被优化掉
extern "C" void EMSCRIPTEN_KEEPALIVE updateColor(float n1, float n2)
{
  printf("n1: %f, n2: %f\n", n1, n2);
  vertices[0] = n1;
  vertices[1] = n2;
  render();
}

编译。这次不要导出 html 文件了,这个我们自己写。

代码语言:javascript
复制
emcc update_triangle.cpp -std=c++11 -s WASM=1 -s USE_SDL=2 -O3 -o index.js

编译的 wasm 默认会暴露到全局的 Module 对象上。

我们可以 通过这个 Module 预设置一些属性,比较重要的是指定好要渲染的画布元素。

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>前端西瓜哥在更新三角形</title>
  </head>
  <body>
    <canvas></canvas>
    <script>
      const canvas = document.querySelector('canvas');
      var Module = {
        // 指定要渲染的画布元素
        canvas: canvas,
      };
    </script>
    <script src="./index.js"></script>
  </body>
</html>

效果

结尾

简单体验了一下用 C++ 写 OpenGL,编译成 WASM 在浏览器上运行,基于 WebGL 渲染出三角形,并用 JavaScript 将数据传给 WASM 更新画布。

我是前端西瓜哥,关注我,学习更多前端图形知识。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-10-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端西瓜哥 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 红色三角形
  • 更新三角形顶点位置
  • 结尾
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档