Yee
1
请问为为什么我如果对射线与面片的交点进行法线插值计算以后渲染出的结果会是这样,为啥会有那么多颗粒?还有周围为什么有一圈过渡的光,正常的镜子应该是跟中间那一圈的颜色一样。另外,为什么牛脸上会有一些奇怪的锯齿?
牛的下面我是放了一个样例中的那个 cube 作为镜子进行反射。
没有法线插值的结果就是下面这张图那样,看起来好像是正常的:
插值是在与面片求交点的时候进行,在有交点的时候返回面片的法线或者交点插值后的法线:
result.normal = Norm;
//插值时用 (b0 * N0 + b1 * N1 + b2 * N2).normalized();
//b0、b1、b2 为重心坐标,N0、N1、N2 为顶点法线值
1 Like
Super_DT
(Super_DT)
2
这也是一个花了我很久时间的问题,我一开始怀疑是用插值的法线会产生一些精度问题,这可能会导致不应该出现的多次迭代,我当时认为可能是法线的精度问题导致对交点进行加上 eps 矫正的时候使得交点反而在内表面而产生错误的迭代(比如单纯渲染一个任何参数都保持默认的 cube,交点被判断在了 cube 内,光线可能就会在 cube 内反复迭代)。不过这只能解释出现过暗的情况,我在使用插值的法线时也会出现奇怪的高光部分,我用 cube 实验的时候有些 kr 极度趋近于 1,这说明相机发出的光线与交点处法线接近垂直,而我设置的单光源和相机的位置是 222,理论上不应该出现这种接近垂直的情况。后来我输出 cube 的顶点法线(由 mesh.normal 调用得到,世界坐标系),很多法线并不是 001 并且有一定差距;而面片的法线是由顶点坐标计算而来,所以是正确的 001。
使用 cube 正确法线进行渲染的结果
使用 cube 插值得到的法线进行渲染的结果(也即 2.2 的结果)
cube 的 obj 文件
我并没有在 obj 文件中找到法线的数据,至于为什么 obj 文件没有法线数据却能直接得到顶点法线,我也不清楚。
两位同学提了一个非常好的问题 白天有些忙,容我先 mark 一下,晚上来详细回答。
晚上来答了。
关于法线插值
Yee 同学对射线求交的结果进行了法线插值,但出现了问题:
周围为什么有一圈过渡的光,正常的镜子应该是跟中间那一圈的颜色一样。
如果你在射线求交时对返回结果作了法线插值,那么“有一圈过渡的光”才是正确的现象。
“用顶点法线的插值表示面片上任意一点的法线”建立在一个前提假设的基础上:这个物体是光滑的,因而其表面上的法线变化应该也是连续的。在局部光照实验中,Phong 着色模型之所以进行法线插值,就是因为它假设要渲染的物体表面是光滑的曲面;而在带有平面镜的场景中,平面的镜子根本就不是一个光滑的曲面,它表面的法线是一个常值。用顶点法线插值计算,得到的并不是竖直向上的法线(正确的法线)。
虽然插值的结果基本正确,但在前提假设不成立的情况下,你计算出的反射光路并不符合平面镜的实际物理意义,渲染出的结果自然也是不正确的。
Super_DT 同学所说的:
我在使用插值的法线时也会出现奇怪的高光部分
也是类似的原因导致的。在方块的八个角点处,法线方向应该是什么呢?显然不应该是任何一个邻接面片的法向,而是从方块中心向外放射的方向。
因此,如果你用定点的法线插值得到面片上某一点的法线,所得的结果与该点的中心坐标有关。越是靠近顶点的位置,插值出的法线就越“平”(倾向于和表面成 45^\circ 角);越是远离顶点的位置,插值出的法线就越“竖”(更接近表面的法线)。
这意味着:如果你想要准确地渲染一个方块,那么你就不应该插值法线(与光栅化渲染时的 Flat Shading 思想相似);而如果你想要渲染一个比方块更“鼓”一点的鼓包,你才应该插值法线(与光栅化渲染时的 Phong Shading 思想相似)。由于鼓包表面会更光滑一点,正对光源的区域也更大一点,插值法线后渲染的结果也会有更大的高光区域。
至于这个问题:
我并没有在 obj 文件中找到法线的数据,至于为什么 obj 文件没有法线数据却能直接得到顶点法线,我也不清楚。
那是因为 Dandelion 不会从模型文件里加载顶点法线,而是通过几何计算估计顶点法线(由 Assimp 库完成),顶点法线是算出来的。又因为用不到模型文件里的顶点法线,所以我导出文件的时候就把它们去掉了。
如果你对估算顶点法线的方法有兴趣,可以看一下 src/geometry/vertex.cpp 中实现的 Vertex::normal
方法,这是最简单的估算方式(以面积为权重,求邻接面片法线的加权平均)。
关于颜色不正确的像素点
Yee 同学问
为啥会有那么多颗粒?
我也不敢确定无疑地给出原因,但首先总结一下现象:
- 在镜面区域上各处都有颜色不正确的像素点
- 这些像素点的分布比较均匀
- 所有颜色不正确的地方似乎都是同一个颜色
从这些现象来看,我也比较倾向于 Super_DT 的猜测,即插值出的法线在进行 \epsilon 矫正时出了错误,导致返回结果是求交成功,但交点在物体内部。然后由于底下的方块是镜面,这条求交错误的光线反复反射,直至达到最大限度后返回了一个基础颜色。而“牛脸上的锯齿”可能也是类似的原因,但由于牛不是镜面材质,程序直接认为这里没有相交,进而直接在此处填上了黑色。
222333
(Hadeon)
4
所以我渲染出的右边那一条黑色竖直边缘,也是这个原因吗