CG6 - 光照与排序

08 Jul 2015

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)平顶三角形、平底三角形分割。详见代码

drawTriangle

2、光照

实现光照的处理方法与求物体背面的方法很类似,都需要两个向量:面的法向量和从光照/相机出发的方向向量。

在这个部分的实现中,可以在两个不同的结构中处理:1)物体对象中;2)渲染列表中。

这两种处理方法都是可行的,但是各有优劣。

$$ \begin{array} {} 结构 & 优势 & 劣势 \\ \hline 物体对象中 & 各个面共用顶点,Gouraud 着色平滑 & 当物体面的数量很大时,计算量大 \\ 渲染列表中 & 各个面均为必须要渲染的面,冗余少,计算速度快 & 着色失真 \end{array} $$

在《大师技巧》中,光照处理是在渲染列表中进行的。在我的实现中,则是在物体对象中进行,实现这一处理的函数为 Object4D::Compute_VertexLightList::rayOn()

实现了光照后,线框世界就变成了有色世界。

cubeDemo3

3、排序

当存在多个物体时,渲染列表中的面未必是从远到进的,如果出现先绘制近的面,再绘制远的面,会出现图像撕裂。

cubeDemo4

此时需要对面进行深度排序,深度排序有多种方法,常用的有 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);
}

cubeDemo5

经过了这些操作,3D 流水线的形态变更为:

pipeline4

这个软件渲染器还很初级,很多高级的特效也都没有做,这几篇文章算是一边回顾一边整理整理思路,为了下一个目标努力。

待续。


如果你发现我文章中的问题或希望与我交流,可以给我发邮件:plinux@qq.com

如果你读后有些许收获,也可以到我的 Github 点赞支持

© 2015 plinx