Triangle Meshes
三角形可以在空间中组成不同的面,也可以在二维中表现成不同的图形。
Triangles - Fundamental Shape Primitives
三角形能在图形学中得到广泛利用的原因就是,三角形有很多性质:
- 是最基础的多边形
- 任何其他的多边形都能将其拆成三角形(多边形的基础)
- 三角形独特的性质
- 内部一定是平面的(例如四边形可以折成两个平面)
- 内部外部定义得很清楚,可以通过向量的叉积来定义一个点在不在三角形内。
- 定义三角形里面三个顶点的不同属性,我们可以做插值。(例如重心坐标插值)
What Pixel Values Approximate a Triangle?
之前我们知道空间中三角形顶点 变换到屏幕坐标上,能知道其屏幕空间的位置 。
要将三角形变成一堆像素,就要考虑一些情况。例如某个像素上三角形只覆盖了一定的区域,那么像素应该显示什么颜色?
因此我们就需要考虑像素中心点与三角形的位置关系。
A Simple Approach: Sampling
最简单的做法:采样。
Sampling a Function
采样就是给一个连续的函数,然后跟其问值是多少。
采样,就是给一个函数离散化的过程。
for (int x = 0; x < xmax; ++x) output[x] = f(x);
采样是指用像素中心来对屏幕空间进行采样。
我们可以采样很多东西:We sample time (1D), area (2D), direction (2D), volume (3D) …
Rasterization As 2D Sampling
我们可以定义一个函数
inside(tri, x, y)
(x,y 不需要是整数),给我们一个三角形,给屏幕空间中任何一个点坐标,要知道这个点在不在三角形内。然后我们就可以用这个函数来判断所有像素。
for (int x = 0; x < xmax; ++x) for (int y = 0; y < ymax; ++y) image[x][y] = inside(tri, x + 0.5, // 像素中心 y + 0.5); // 要么等于 0 要么等于 1
如何判断一个点在不在三角形内?
叉乘 ,就能知道 点是在 左侧还是右侧。右手定则发现朝屏幕外,就能知道 在左侧。
再判断 叉乘 ,知道 在左侧。
判断 叉乘 ,知道 在右侧。因此不在三角形内。
逆时针看点, 叉乘结果应该都在左侧。顺时针的结果则应该都在右侧。
考虑一个三角形,然后找到一个方向不断重复,例如 AB、BC、AC,叉乘结果要么全在左侧,要么全在右侧,这样 才会在三角形里面。
还可以参考:
Edge Cases (Literally)
如果点在三角形边界上,怎么算?
对于这种情况,要么不做处理,要么特殊处理。
在一些图形 API 上,例如 OpenGL、DirectX,它们有非常严格的规定,例如点落在上边和左边,则在三角形内,落在下边和右边则落在三角形外。(没必要掌握)
Checking All Pixels on the Screen?
前面三角形光栅化,我们要考虑所有像素,有无必要?
没必要,三角形只会覆盖一些区域,例如下图,三角形不可能填充到左边第一列上。我们就可以只考虑下图的蓝色区域,也就是包围盒(轴向的,水平竖直)。
如果像素不在这个区域里面,我们就不需要做光栅化。
Incremental Triangle Traversal (Faster?)
我们还可以优化一下,每一行只考虑最左和最右,做起来不容易。这个方法适用于光栅化窄长的、或者旋转一定角度的,包围盒较大但实际覆盖很少的三角形。
针对不同情况,有不同处理方法。
Rasterization on Real Displays
实际屏幕的光栅化是怎么样的?
Real LCD Screen Pixels (Closeup)
iPhone 一个像素是红绿蓝三种条组成。
右边是 Bayer pattern 分布,让红绿蓝均匀分布在像素上。很难找一个方块,绿色点比其他颜色多,这种屏幕上,用了更多绿点。是因为人眼对绿色最敏感。
相机也是如此,绿色的感光元件会多一点。绿色得到更好的还原,人眼会更舒服。
Aside: What About Other Display Methods?
彩色打印机上会有更复杂的图案,因为打印会考虑如何最省墨水。
Assume Display Pixels Emit Square of Light
我们这里仍然认为一个像素是颜色均匀的小方块。
So, If We Send the Display the Sampled Signal
对于每个像素都能判断是否在三角形内,那么我们就能填成三角形对应的颜色。
但是填成的像素跟三角形相去甚远,有锯齿。
锯齿也是光栅化一直致力解决的一个重要问题。因为像素本身有一定大小,而且我们的采样率对像素来说不够高,信号的走样问题。
因此我们需要反走样。