[原创] tinygl 1000 行代码,就可以实现 3D 的软件渲染算法
+----------------+tinygl 3d 渲染器
+----------------+
tinygl 是一个软件光栅化 3D 图形渲染器。
在学习了 TinyRenderer 后,自己使用 C 语言写了这个 tinygl.
代码使用纯 C 编写,渲染部分的代码不依赖于任何第三方库和平台 API,只需要有标准 C 库的支持即可编译通过
渲染结果可保存到 .bmp 和 .tga 图片,也可以直接显示在 windows 的窗口上。
目前 windows 平台支持 wingdi 用于实现渲染结果输出到窗口,方便查看结果。
1000 行代码,基本就实现了 TinyRenderer 的完整功能,支持的特性如下:
1. TGA 图片和 BMP 图片的加载和保存
2. 基础的像素点绘制、直线绘制、bitblt 图块绘制、填充矩形绘制
3. vector 向量和 matrix 矩阵的基础运算功能
4. obj 格式的模型文件的加载和保存以及数据的读取
5. 基于包围盒和重心坐标的三角形光栅化算法的实现
6. 渲染管线的实现:背面剔除、z-buffer、纹理映射等
7. 顶点着色器:mvpp, wire, rand, flat, gouraud
8. 片元着色器:color*, phong, normal*, texture*
9. 实现 mvp 变换、相机变换、透视投影、透视插值
10.简洁易用的 API 设计、优化的架构设计、良好的编码规范
11.在 win10 上实测,人头模型可跑到 100fps 的帧率(cpu 占用 20%)
+--------+
API 说明
+--------+
model.h
-------
用于模型加载,并从中获取模型数据,包括:三角形数据和纹理数据
void* model_load(char *object, char *texture);
指定模型的 .obj 和 .tga 文件,加载模型并得到对象指针
void model_save(void *ctx, char *object, char *texture);
保存模型到 .obj 和 .tga 文件
void model_free(void *ctx);
释放模型对象
void* model_get_texture(void *ctx);
得到模型的 texture 纹理数据
int model_get_face(void *ctx, int idx, vertex_t face[3]);
根据 idx 编号,获取模型的三角形数据
tinygl.h
--------
用于渲染模型,创建后内部会创建一个 texture 对象作为 render target
也可以通过 tinygl_set(gl, "target", texture); 来指定一个新的 texture 作为 target
通过 tinygl_set(gl, "save", “out.bmp”); 可以把渲染结果保存到文件进行查看
void* tinygl_init(int w, int h);
初始化 tinygl 并得到 tinygl 的对象指针
void tinygl_free(void *ctx);
反初始化
void tinygl_begin(void *ctx, int clear);
开始渲染,clear 如果为 1 会清除 framebuffer 和 zbuffer
void tinygl_end(void *ctx);
完成渲染
void tinygl_draw(void *ctx, void *model);
放到 tinygl_begin 和 tinygl_end 中间,用于绘制模型
void tinygl_clear(void *ctx, char *type);
用于清除 frame buffer 或者 z-buffer
tinygl_clear(gl, "framebuf"); // 清除 frame buffer
tinygl_clear(gl, "zbuffer" ); // 清除 z-buffer
tinygl_clear(gl, "framebuf+zbuffer"); // 同时清除 frame buffer 和 z-buffer
void tinygl_set(void *ctx, char *name, void *data);
void* tinygl_get(void *ctx, char *name);
用于获取和设置渲染参数
wingdi.h
--------
wingdi 实现了 windows 下的 gdi 窗口,创建时内部自动创建了一个 texture 对象,
在这个 texture 对象上绘制图形就等于是在窗口上绘制。
通过 wingdi 的 get 函数调用:
TEXTURE *t = wingdi_get(win, "texture");
获取到这个 texture 对象,然后使用 texture 的 API 就可以在窗口上绘图了
void* wingdi_init(int w, int h);
初始化并得到 wingdi 对象,w h 指定了窗口大小
void wingdi_free(void *ctx, int close);
反初始化
void wingdi_set(void *ctx, char *name, void *data);
void* wingdi_get(void *ctx, char *name);
用于获取和设置参数
shader.h
--------
shader 模块定义并实现了 shader 的接口和数据类型,可以在此基础上扩展实现更多的顶点着色器和片元着色器
目前已经实现的着色器有:
顶点着色器:wire, rand, flat, gouraud
片元着色器:color0, color1, phong, normal0, normal1, texture0, texture1, texture2
用户也可以通过 tinygl_set(gl, "shader.xxx", data); 来设置 tinygl 内部的 shader 参数
通过设置不同的 vertext 顶点着色器和 fragmt 片元着色器,可以得到不同的渲染效果
+--------+
编译说明
+--------+
代码是在 msys2 + gcc + gdb 环境下调试开发,安装 msys2 开发环境,执行 ./build.sh 即可完成编译
+-----------+
tinygl_demo
+-----------+
tinygl_demo 目前实现了在窗口上旋转显示模型,并支持空格按键切换 shader 类型
空格按键可以切换 shader 类型,edsf 作为方向按键可以前进后退左右移动
+------------+
hello tinygl
+------------+
下面是最简单的 tinygl 示例,把人头模型渲染并保存到 out.bmp:
#include "tinygl.h"
#include "model.h"
int main(void)
{
void *gl = tinygl_init(800, 800);
void *m = model_load("model/head.obj", NULL);
tinygl_begin(gl, 1);
tinygl_draw(gl, m);
tinygl_end(gl);
tinygl_set(gl, "save", "out.bmp");
model_free(m);
tinygl_free(gl);
return 0;
}
rockcarry
2022/10/4