一. 前言
有时候我们需要在MCU上进行一些图像处理,比如变换,3D显示等,希望有类似opengl的工具,恰好Tinygl就是这样一个不错的选择。其官方网页为:https://bellard.org/TinyGL/。它的作者就是开发出了ffmpeg等项目的大牛FabriceBellard,可以见其网站https://bellard.org/, 可以看到该网站上还有很多的项目,包括QEMU,TCC等,每一个都是核弹级别。我们这一篇就来介绍其中一个项目tinygl, 分享tinygl在MCU上的移植使用。
二. Tinygl简介
网址:https://bellard.org/TinyGL/,最新版本为0.4.1下载地址https://bellard.org/TinyGL/TinyGL-0.4.1.tar.gz
TinyGL 是OpenGL 的一个小型实现,适用于嵌入式系统。纯软实现,实现了OpenGL 的主要调用。其优势在于速度快、简单易用,因为它无需完全兼容OpenGL。特别是,它的纹理映射和几何变换速度非常快。例如,与Mesa 或Solaris OpenWin OpenGL 实现相比,TinyGL 的速度要快得多,后者是VReng 虚拟现实引擎的OpenGL 实现。
TinyGL的主要特点如下:
与OpenGL兼容的头文件。
类似于Zlib的许可协议,便于在商业设计中进行集成。
GLX的子集,便于在X Window上进行测试。
类似于GLX的API(NGLX),用于与NanoX一起使用。
BeOS下的BGLView子集。
类似于OpenGL的光照效果。
有限的OpenGL 1.1数组支持。
完整的OpenGL选择模式处理,用于对象选择。
16位Z缓冲区。16位RGB显示。可以进行高速调色板8位处理。可以进行高速24位或32位转换。
优化的16位RGB Gouraud着色,以实现快速阴影绘制。
具有透视校正和纹理对象的快速纹理映射功能。
仅使用32位浮点数进行算术运算。
非常小巧:在x86上编译后的代码大小约为40 KB。
支持32位和64位架构的GCC C源代码。在x86-Linux和sparc-Solaris上成功测试。
架构:
由以下四个主要模块组成,
数学函数库(zmath)。
OpenGL类似的仿真(zgl)。
Z缓冲区和光栅化(zbuffer)。
GLX接口(zglx)。
三. 移植到MCU中
3.1添加源码
我们使用github上的一个基于原版改造的版本,更适合移植。
下载代码
git clone https://github.com/jserv/tinygl
文件如下
tinygl/
|-- INTEGRATION
|-- LICENSE
|-- Makefile
|-- README.md
|-- assets
| |-- blend.gif
| |-- capture.gif
| |-- capture2.gif
| |-- helloworld.gif
| |-- model.gif
| |-- model2.gif
| |-- model2_lit.gif
| |-- model_hole.gif
| |-- model_lit.gif
| |-- specular.gif
| |-- texture_test.png
| `-- tgl_minimal.png
|-- config.mk
|-- examples
| |-- 3dmath.h
| |-- raw
| | |-- Makefile
| | `-- gears.c
| |-- sdl
| | |-- Makefile
| | |-- extrude.obj
| | |-- game.c
| | |-- gears.c
| | |-- hello.c
| | |-- model.c
| | |-- monkey3.obj
| | |-- tex.jpg
| | |-- tex_hole.png
| | |-- tex_old.jpg
| | |-- texture.c
| | `-- texture.png
| `-- tobjparse.h
|-- include
| |-- TGL
| | `-- gl.h
| |-- zbuffer.h
| `-- zfeatures.h
|-- lib
`-- src
|-- Makefile
|-- api.c
|-- arrays.c
|-- clear.c
|-- clip.c
|-- error_check.h
|-- error_check_no_context.h
|-- font8x8_basic.h
|-- get.c
|-- image_util.c
|-- init.c
|-- light.c
|-- list.c
|-- matrix.c
|-- memory.c
|-- misc.c
|-- msghandling.c
|-- msghandling.h
|-- opinfo.h
|-- select.c
|-- specbuf.c
|-- texture.c
|-- vertex.c
|-- zbuffer.c
|-- zgl.h
|-- zline.c
|-- zline.h
|-- zmath.c
|-- zmath.h
|-- zpostprocess.c
|-- zraster.c
|-- ztext.c
|-- ztriangle.c
`-- ztriangle.h
8 directories, 70 files
添加tinygl/src下所有c文件到自己的工程
设置头文件包含路径tinygl/include/
3.2依赖
math.h
sin
cos
Fabs
sqrt
按需替换
src/memory.c中
malloc,calloc和free接口按需替换
宏M_PI
3.3修改编译告警
include/TGL/gl.h中
GLenum glGetError();
改为
GLenum glGetError(void);
否则报错
acore/tinygl/include/TGL/gl.h:1111:1: warning: function declaration isn't a prototype [-Wstrict-prototypes]
1111 | GLenum glGetError();
还有其他很多类似的参数为空, 按照编译器要求添加(void).
tinygl/src/init.c中
glInitTextures(c);
改为
glInitTextures();
该函数无需参数。
3.4配置
tinygl/include/zfeatures.h中
显示如果采用16位则配置为
#define TGL_FEATURE_16_BITS 1
#define TGL_FEATURE_32_BITS 0
32位则配置为
#define TGL_FEATURE_16_BITS 0
#define TGL_FEATURE_32_BITS 1
3.5添加测试代码
添加tinygl/examples/raw/gears.c到工程
int main(int argc, char **argv)
改为
int gears_main(int argc, char **argv)
在自己合适的地方调用
删除
#define STBIW_ASSERT(x)
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
添加自己的显示接口头文件,比如我这里是
#include “lcd.h”
gears_main中
int winSizeX = 640, winSizeY = 480;
改为自己显示设备的实际大小
uint8_t *pbuf = malloc(3 * winSizeX * winSizeY);
for (int i = 0; i < winSizeX * winSizeY; i++) {
pbuf[3 * i + 0] = GET_RED(imbuf[i]);
pbuf[3 * i + 1] = GET_GREEN(imbuf[i]);
pbuf[3 * i + 2] = GET_BLUE(imbuf[i]);
}
stbi_write_png("render.png", winSizeX, winSizeY, 3, pbuf, 0);
free(imbuf);
free(pbuf);
改为自己的显示操作, 即将imbuf的内容进行显示,和前面设置BITS对应,32位即对应RGB888,16位为RGB565。
比如lcd_fill((uint8_t*)imbuf);
或者按点操作
for (int i = 0; i < winSizeX * winSizeY; i++) {
dis_itf_set_pixel_0(i, GET_RED(imbuf[i]), GET_GREEN(imbuf[i]), GET_BLUE(imbuf[i]));
}
或者直接将frameBuffer的内容写入显存显示无需再调用
ZB_copyFrameBuffer
减少一次内存拷贝。
按需替换以下接口
printf
free
malloc
calloc
sin
cos
sqrt
注释掉
//fflush(stdout);
最开始添加宏
#ifndef M_PI
#define M_PI 3.14159265
#endif
static void draw()
改为
static void draw(void)
还有其他类似的根据编译器告警修改即可。
3.6注意
ZB_open
Imbuf
开辟帧缓存需要堆比较大。
Gears.c中
#include "zfeatures.h"
否则宏不同步
#if TGL_FEATURE_RENDER_BITS == 32
if (frames > 0)
break;
改为以下,这样可以不断更新帧,动态显示动起来
if (frames > 10000)
break;
for (int i = 0; i < winSizeX * winSizeY; i++) {
dis_itf_set_pixel_0(i, GET_RED(imbuf[i]), GET_GREEN(imbuf[i]), GET_BLUE(imbuf[i]));
}
3.7测试
我这里是通过UVC显示
四. 总结
Tinygl的移植比较简单,只依赖math的sin,cos,sqrt以及,malloc,free等少数接口,嵌入式平台需要按需移植。
操作流程是
开辟缓存 frameBuffer = ZB_open
初始化gl glInit(frameBuffer);
... gl接口操作
...缓存frameBuffer 显示
清除缓存ZB_close(frameBuffer);
关闭glClose();