我在由三角形组成的现代 OpenGL(即着色器)中苦苦挣扎,但后来我发现了一个非常简单的解决方案!我会说它比当前接受的答案中建议的要好得多,也更简单。
我有一组三角形(显然每个都有 3 个顶点),它们形成了锥面。我不关心底面(圆形底座),因为这真的很简单。在我所有的工作中,我使用以下简单的顶点结构:
在我的顶点着色器中,我只变换顶点位置(乘以投影和模型视图矩阵)并变换法线向量(乘以模型视图矩阵的变换逆矩阵)。然后将转换后的位置、法线向量和未转换的颜色传递给片段着色器,在其中我计算光方向和法线向量的点积,并将这个数字与颜色相乘。
让我从我所做的并发现不令人满意的事情开始:
尝试#1:每个圆锥面(三角形)都使用恒定的法向量,即一个三角形的所有顶点具有相同的法向量。这很简单,但没有实现平滑的光照,每个面都有一个恒定的颜色,因为三角形的所有片段都具有相同的法向量。错误的。
尝试#2:我分别计算了每个顶点的法线向量。这对于圆锥体圆形底部的顶点来说很容易,但是圆锥体的尖端应该使用什么?我使用了整个三角形的法线向量(即与尝试#相同的值)。好吧,这更好,因为我在靠近锥体底部的部分有平滑的照明,但在尖端附近不平滑。错误的。
但后来我找到了解决方案:
尝试#3:除了我在锥尖顶点中分配的法向量等于零向量 vec3(0.0f, 0.0f, 0.0f) 之外,我按照尝试#2 进行了所有操作。这是诀窍的关键!然后这个零法线向量被传递给片段着色器,(即在顶点和片段着色器之间,它会自动插入其他两个顶点的法线向量)。当然,您需要对片段(!)着色器中的向量进行归一化,因为它没有恒定的大小 1(我需要点积)。所以我对其进行归一化——当然这对于法向量的大小为零的锥体尖端是不可能的。但它适用于所有其他点。就是这样。
有一件重要的事情要记住,要么你只能在片段着色器中对法线向量进行归一化。如果您尝试在 C++ 中对零大小的向量进行归一化,肯定会出错。因此,如果由于某种原因在进入片段着色器之前需要归一化,请确保排除大小为零的法线向量(即圆锥的尖端,否则会出错)。
这会在除锥尖点之外的所有点产生平滑的锥体阴影。但这一点并不重要(谁在乎一个像素......)或者你可以用一种特殊的方式来处理它。另一个优点是您甚至可以使用非常简单的着色器。唯一的变化是在片段着色器中而不是在顶点着色器甚至之前对法线向量进行归一化。