0、概述
色彩是图像不变的主题,要让软件渲染器支持色彩,只需要给面加上色彩的数据属性即可。但是要让软件渲染器支持真实世界的色彩,还需要更新绘图的函数,当实现了让物体随参数显示渐变色彩时,光照效果也就很容易实现了。
1、绘图函数
在线框时代,只需要实现逐点绘线即可,所以在 drawPixel 外面做一层 wrapper 就可以应对所有情况。但是当开始绘制色彩时,往往需要一个面一个面地去绘制,如果仍调用 drawPixel 将产生大量点计算和颜色计算,所以迫切需要一个批量操作的绘图函数。
在这里,可以根据最初创建显示缓存的方式来进行设计:创建显示缓存时,使用了逐行分配的方式进行内存索引,所以可以实现一个绘制单行的绘图函数避免重复寻址。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
inline void Painter::drawHorizonLine(int x1, int x2, int y, Color& color)
{
if (y < 0 || y > _height) return;
// the judge put in _drawFlatBottomTriangle and _drawFlatTopTriangle
if (x2 < x1)
std::swap(x1, x2);
if (x1 < 0) x1 = 0;
if (x2 > _width) x2 = _width;
int tmpA = color.A;
_pixel = _scanLine + _bytesPerLine * (y);
_pixel += x1 * 3;
for (int x = x1; x < x2; x++)
{
_setColor(_pixel, color);
_pixel += 3;
}
}
依托于 drawHorizonLine,可以实现针对三角形 Gouraud 着色函数的 drawTriangle。
在这里需要考虑的情况很多:1)越界;2)坐标顺序;3)插值计算;4)平顶三角形、平底三角形分割。详见代码。
2、光照
实现光照的处理方法与求物体背面的方法很类似,都需要两个向量:面的法向量和从光照/相机出发的方向向量。
在这个部分的实现中,可以在两个不同的结构中处理:1)物体对象中;2)渲染列表中。
这两种处理方法都是可行的,但是各有优劣。
$$ \begin{array} {} 结构 & 优势 & 劣势 \\ \hline 物体对象中 & 各个面共用顶点,Gouraud 着色平滑 & 当物体面的数量很大时,计算量大 \\ 渲染列表中 & 各个面均为必须要渲染的面,冗余少,计算速度快 & 着色失真 \end{array} $$
在《大师技巧》中,光照处理是在渲染列表中进行的。在我的实现中,则是在物体对象中进行,实现这一处理的函数为 Object4D::Compute_Vertex 和 LightList::rayOn()。
实现了光照后,线框世界就变成了有色世界。
3、排序
当存在多个物体时,渲染列表中的面未必是从远到进的,如果出现先绘制近的面,再绘制远的面,会出现图像撕裂。
此时需要对面进行深度排序,深度排序有多种方法,常用的有 zbuff 和 zsort,我的实现中使用了 zsort。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
inline int Compare_avgz(const void* arg1, const void* arg2)
{
float z1, z2;
PPolyList4D poly1 = *((PPolyList4D*)arg1);
PPolyList4D poly2 = *((PPolyList4D*)arg2);
z1 = (float)0.333 * (poly1->tvlist[0].z + poly1->tvlist[1].z + poly1->tvlist[2].z);
z2 = (float)0.333 * (poly2->tvlist[0].z + poly2->tvlist[1].z + poly2->tvlist[2].z);
if (z1 > z2)
return -1;
else if (z1 < z2)
return 1;
else
return 0;
}
inline void RenderList4D::zsort()
{
qsort(poly_ptrs, this->num_polys, sizeof(PPolyList4D), Compare_avgz);
}
经过了这些操作,3D 流水线的形态变更为:
这个软件渲染器还很初级,很多高级的特效也都没有做,这几篇文章算是一边回顾一边整理整理思路,为了下一个目标努力。
待续。
如果你发现我文章中的问题或希望与我交流,可以给我发邮件:plinux@qq.com。
如果你读后有些许收获,也可以到我的 Github 点赞支持。
© 2015 plinx