Home Forums GAMES在线课程(现代计算机图形学入门)讨论区 作业3 关于深度值问题自己踩的坑和一些想法 Reply To: 作业3 关于深度值问题自己踩的坑和一些想法

#8872 Score: 1
happyfire
Participant
4 pts

很高兴你也注意到了w==1这个问题,其实作业2,作业3的框架都有问题,他们公共的问题是光栅化取三角形顶点坐标时使用了 auto v = t.toVector4(); 这样w肯定是1了。然后作业2错误更多些,在透视除法里面使用了vec /= vec.w();造成w为1。然后作业3修复了这一点,只对x,y,z除了w。但是没有用啊,因为toVector4。

然后项目框架里面的w应该是什么含义呢?按照课程的约定,NDC使用了和ViewSpace一样的右手系,所以投影矩阵的最后一行是(0,0,1,0),这样w=Z_view。所以课程3框架的注释里面说:v[i].w() is the vertex view space depth value z. 并没有问题。而在网上找的关于OpenGL的文章里面,推导出来w=-Z_view,那是因为OpenGL的约定中,NDC为左手坐标系(near为-1,far为+1)。

然后我们看下z插值的问题,作业2框架中这么写(作业3其实也一样):


float w_reciprocal = 1.0/(alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
float z_interpolated = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();
z_interpolated *= w_reciprocal;

由于w为1,因此上面的w_reciprocal就是1.0/(alpha+beta+gamma),而alpha+beta+gamma==1,因此w_reciprocal为1。
因为w为1,z_interpolated 其实就是直接使用重心坐标进行插值了(没有透视校正的效果),最后一步z_interpolated *= w_reciprocal也没有作用。最终z_interpolated 就是三个顶点的z值的插值。
那么这个地方三个顶点的z值是什么呢?在draw中:


float f1 = (50 - 0.1) / 2.0;
float f2 = (50 + 0.1) / 2.0;
vert.z() = vert.z() * f1 + f2;

f1,f2是什么意思?因为框架中的near是0.1,far是50.(这儿near,far都是距离值,是正值。我只能这么理解吧),所以f1,f2的作用就是把clip space的z值变换到[near,far]的范围中。我LOG了一下,第一个三角形的clip space z为 -0.975379,变换后为0.714285;第二个三角形从-0.983968变换到0.50002。因为框架中一直使用的是右手坐标系,且view space的view方向为-z。因为第一个三角形的z值-0.97xx大于第二个三角形的-0.98xxx,离camera更近,所以第一个三角形应该挡住第二个三角形。但是真正做深度测试的时候,使用的是变换到[0.1,50]范围的z值。这样第一个三角形的0.7xxx由于大于第二个三角形的0.5xxx,因此如果直接使用z_interpolated 并且按照小值离camera近的逻辑去比较就会得到第二个三角形挡住第一个三角形的错误结果。为了得到正确的结果,简单的取个负修正一下确实可以,但这里面的逻辑确实搞错了。根源就在于作业2的pdf里面说的:为了方便同学们写代码,我们将 z 进行了反转,保证都是正数,并且越大表示离视点越远。
按照我上面的分析,反转后的正数z并不是越大离视点越远,而是正好相反。所以作业3牛画反的同学应该明白了吧,并不是你的问题。框架本身就有问题。。这个地方转成正数后反而不能用<去测试了,有点画蛇添足的感觉。

This post has received 1 vote up.