填充三角形
线性插值
来复习一下画线,我们知道画线的时候我们做的实际上是这样的事情:对于要画的线AB,在满足我们设定的条件(斜率 ≤ 1, A < B)之后,因为我们要画的是整数的像素点,对于x每增加1,我们算出对应的y,然后来画点(x,y),这样重复直到x增加到B点:
对于 AB 上的任意一点 P 满足:
也可以写成:
这个公式是著名的线性插值,实际上也是我们画线的基础。因为在画线部分核心代码长这样:
for
对于P点,我们根据增加后的x算出t值,然后算出y,得到应该画的点。
其实在画框架的时候我们已经画过三角形了,就画三条线就OK。现在我们要做的是来填充三角形。
扫描法
若要填充一个三角形,最简单的能想到的办法是对于三角形的每一个y,我们找到对应的左侧和右侧,x_left和x_right,我们画上x_left到x_right的线,那么从三角形最上面的点按y增加扫到最下面的点既可。
为了简单起见,我们先把三角形拆成上下两部分:
那么对于一个特定的y,我们想要找到它的左边和右边 A B 两点,思路是这样:
- 首先排序,保证 t0 ≤ t1 ≤ t2
- 整个三角形的高度必为 t2.y - t0.y
- 那么对于上半部分,y每增加1(注意有可能t0 == t1),我们用插值法算出对应的两点A和B
这样就能算出对应的 A 和 B
void
那么有了 A 和 B 之后,我们在AB之间调用我们的画线函数,再用同样的方法给下半部分填满,问题既解决。
void
这样三角形填充就解决。代码里有很多重复的部分,然后这里决定让代码短一点,代价是读起来没那么清楚了:
void
wavefront obj
上一章我们画了框架,这下我们来填上三角形:
好吧,并不是很动人=。=之所以不动人是因为光影光影,我们只有颜色,没有考虑光,
KrisYu/tinyrendergithub.comcompile:
g++ -std=c++11 main.cpp tgaimage.cpp model.cpp -o main
重心坐标法
除了上面提到的扫描法之外,另外一个可以想到的办法是,因为我们终究是画到二维平面上的像素,一个一个的点,那么对于我们要画的区域内的每一个点,我们是否可以检测看它是否在三角形之内,如果是的话,画它,否则不理之。这样的思路是可行的,对于三角形内及其边上的任意一点,我们都可以用重心坐标系来表示:
这个长得也很像线性插值。
运算:
PA是AB和AC的线性组合。
拆一拆:
实际上我们都可以看做是我们在寻找向量 (u, v, 1) 同时垂直于向量
关于重心坐标系以及代码的更多推理可以读这里:
二圈妹:三角形重心坐标zhuanlan.zhihu.com代码我们这样写:
Vec3f
我们当然也不用把平面区域的每个点代入P去做检查,我们只需要找到三角形的 bounding_box,然后看其中的每一个整数点,如果在其中,那就画之。
用同样的方法来给模型填色,效果一样。
KrisYu/tinyrendergithub.com效果跟之前依旧一致,我们给每个三角形随机填上色:
随机填色这个我们看起来倒是有点cool.