三角网格中一些描述符
一、问题描述:
当几个面共享了一个顶点时,该如何设置这个顶点的法线呢?
二、下面是参考 Max Wagner 的 文章 《Generating Vertex Normals》,记的笔记。
1. 设顶点法线与设面法线有什么区别?
面的法线是与面垂直的线(数学意义)。但在OpenGL中好像并非这样。严格意义上讲,点是没有法线的。点的法线是在使用Phone或Gouraud模型时计算光照使用。如果一个面上的所有法线都一样,他们的光照也就一样,就会产生 flatness 效果;而如果把每个顶点的法向设置不同,则更平滑。
2. The Intuition behind Vertex Normals
光线会被应用到每个顶点,并且根据面法线和光照方向的点积去调整光线颜色的强度。这个光照颜色与预先设定的顶点颜色(相加?)(这是Diffuse情况下,Specular, Ambient 情况下也是一样)。因此,如果是三角面,三个顶点就是有面法线和光照方向的同样的点积。三个顶点使用相同的法向导致三角面成为flat,这是不对的。But imagine we could actually sample the “true” surface at the vertices themselves; then we would surely get more variation amongst the vertex normals, in turn creating a (smooth) variation in the diffuse lighting terms. But how can we sample the “true” surface?
很不幸,我们得不到"true" face。但是我们可以得到接近的,利用几个三角面共享了一个顶点。“平均”这个顶点所有邻接三解形的面法线(这个是可求的)(要搞清楚这个过程,我们可以利用一个三角形的三个顶点计算其法线,然后利用这个理论上的法线去设置三个顶点的法线,然后再由OpenGL的实现对三角面中每个顶点的法线进行插值,这个过程有点绕!)。
3. The Algorithm
(1) 针对每个顶点,先找到共享此顶点的所有三角形,然后计算这些三角形的面法线并相加,得到顶点的法线。(未考虑法线与各个面的夹角信息)。伪代码:
- struct Vertex { Position p, Normal n }
- VertexList v
- for each vertex i in VertexList v
- n ←Zero Vector
- for each triangle j that shares ith
- vertex
- n ←n + Normalize(Normal(v, j))
- end for
- v[i].n ←Normalize(n)
- end for
此算法的时间复杂度为(O(n^2))
(2) 这个算法有个问题是,有些地方就不是smooth,而是flat,比如四面体网格,若仍按上面的算法,结果就会很奇怪。
一个解决方法是使用一个阈值判断一个面法线是否应该被加入到平均化过程。遍历顶点的过程中,存储一个面法向,这个面是这个顶点技术性包含于(technically belongs to)的面(as opposed to the triangles which share a copy of the vertex eleswhere in the triangle list)(什么叫技术性包含于?)。然后,当遍历共享此顶点的三角形时,先计算存储的面法线与当前三角形的面法线的点积,然后判断此点积是否大于某一阈值,如果小于此阈值,则不参与平均化过程。这个原理是什么呢?两个面法线的点积与其夹角的cos值相对应,如果这个cos值过小,说明法线的夹角较大(cos在0-180度单调递减)。(但论文中是这样说的:We are effectively saying, if the angle between two surfaces is greater than throhold, don't average them。与计算的不统一啊)。
伪代码:
- struct Vertex { Position p, Normal n }
- VertexList v
- epsilon e
- for each vertex i in VertexList v
- n ←Zero Vector
- m ←Normalize(Normal(v, i%3))
- for each triangle j that shares ith
- vertex
- q ←Normalize(Normal(v, j))
- if DotProduct(q, m) > e
- n ←n + q
- end if
- end for
- v[i].n ←Normalize(n)
- end for