【Fr2D开发笔记】一、FR2DCMD

GitHub传送:https://github.com/salty-Frankenstein/Fr2D

这大概算是我的第一个有规模的项目了。

之前就一直想试试计算机图形学以至于图像渲染引擎之类的原理,但受限于对Windows编程的不了解,一直没有进行。这次的更新其实是一个基于控制台程序的图形渲染测试(试水吧)。因此,起名为FR2DCMD,但内部的架构还是借鉴了Windows以及directx的实现(我自己的理解)。这也是我对项目开发和图形学的初次尝试,不论是从计算机图形学的理解上还是对整个较大的项目的架构的设计上都不免有不成熟之处。

因为只是开发笔记,不确定以后会不会有大的重构。

以下是Fr2D的架构:

由于是基于控制台,这里并没有处理硬件相关的优化,因此,效率也是可想而知的慢……

一、FRWIN

基本的想法是用一个二维的字符阵列模拟像素。在此层,主要解决的问题是:

  • FRWIN对象的创建、屏幕分辨率
  • 建立像素坐标系
  • 显示,帧率控制、重绘
  • 消息循环系统、回调函数
  • 颜色支持、单像素绘制

FRWIN内部是一个二维字符型数组,作为显示的缓冲区(尽管只有一帧的缓存……)。

所以,在FRWIN层的坐标仍然是i,j遍历的二维矩阵坐标。

对于显示,其实一直是此类引擎的要点,尤其是对于这个基于控制台的程序,输出速度是极慢的(基本上一帧的时间大部分是用在了显示上)。这里使用的是每一行puts的方法,这已经是我所能想到的最快的方法了……然而测试下来,在不做任何绘图,仅仅刷新的情况下也只能达到50fps。因此我设定缺省的fps为30,但也只给了渲染很大的压力,所以在之后的绘图中,经常有把帧率调到20甚至10的情况。

对于帧率控制,我没有使用中断,而是最蠢的sleep到下一帧,这也意味着在这段时间里,也将不能做任何绘图缓冲操作。

消息循环系统和类中的回调函数是我借鉴了winapi的实现,但这个实现方法十分科学,这样留出了两个接口对主程序的检查键盘事件和绘图操作的实现都十分方便、简洁。

对于颜色,我还是留了一定的可扩展性,定义了const char[8],对应八种由浅到深的字符作为颜色(8bit颜色?),便于进一步的调色、着色器操作。

二、FR2DCMD

首先,为了方便调用,这个类库的2D和3D部分采用了同名的对象和函数,以namespace FR2DCMD和FR3DCMD做区分。
FR2DCMD是我对二维图形绘制所做的类。作为绘制层,主要解决的问题是:

  • 与win类对接
  • 像素坐标与坐标系坐标的转换
  • 调节显示比例
  • 定义原点坐标
  • 顶点类vertex(其实是结构体)
  • 绘制各种图形、光栅化
  • 着色器
  • 二维坐标矩阵变换

为了在win类上绘制,FR2D通过传递一个FRWIN的句柄实现对该对象的控制。

为了方便进一步的开发实现,我将i,j坐标与x,y坐标建立函数关系,既让使用时的坐标操作(x,y)变得方便、贴近数学定义,又可以在缓冲区中绘制像素。

由于字符界面的字符长款比并不是1:1的像素,按照1:1的绘制就会使图像变形。因此,需要根据当前显示的字符的长宽比进行调整。

借鉴directx,使用了结构体vertex储存顶点信息,包括(x,y)坐标和顶点颜色。

到了图形绘制阶段,一开始是拍脑袋使用的采样法,即对于每一个像素,与函数解析式比对,并描出像素。
我用这个方法成功地画出了圆。然而,它的复杂度太高,这样如果一帧有1000条线,仅有6.75fps。因此对于描绘直线,采用了“步进法”,将复杂度降到了O(n)级,经测试,20fps下至多可以画5000条线。

画线时,还需要根据顶点的颜色计算需要绘制的颜色,进行着色,这里是简单的取两个颜色的加权平均值。

对于二维坐标的变换,目前实现了旋转,这里调用了我另一个库FRSL中的矩阵类,自认为它的功能还是比较完整的。

三、FR3DCMD

到了3D阶段,由我仅有的一点directx印象,造出了这个轮子:

  • 与FR2D对接
  • 顶点类(其实还是结构体)
  • 创建顶点、索引缓存buffer
  • 3D到2D的投影
  • 三维坐标矩阵变换

一开始我也想过是不是与FR2D是继承关系,发现不行,基本上要重新实现,因此开了3D的namespace,还是通过一个FR2D的句柄进行对接。

顶点类也只是加了个z坐标。

顶点缓存也是我借鉴DirectX的实现。通过建立索引缓存实现对于顶点的重用,并做到集中绘制。

3D中最大的不同就是要投影到2D,平行投影是首先舍弃的,因为没有立体感。这里我使用了点投影,需要在FR3D类注册时加上一个视点的距离。这样,对于每一个3D::vertex都可以通过矩阵变换得到2D::vertex的坐标,这样,再通过FR2D句柄调用2D绘图函数即可。

同样,3D图形也可以通过矩阵变换实现旋转。
旋转

四、导入模型

在网上找到了经典的计算机图形学测试模型——犹他茶壶的stl文件。可喜的是,stl文件的储存方式是与FR3D的逻辑一致的,在我花了些工夫把文件读出来后,真的成功绘制出了一个旋转的茶壶——9438个顶点!而这、竟然是基于控制台程序实现的!效果还是很好的。