做作业三这里有一些比较模糊的问题需要理解的。这里也 mark 一下吧,不然学了个寂寞。
一开始不太理解作业框架所以搜了一些问题避免踩坑:
-
作业3 关于深度值问题自己踩的坑和一些想法 – 计算机图形学与混合现实研讨会 (games-cn.org)
-
graphics - How exactly does OpenGL do perspectively correct linear interpolation? - Stack Overflow
-
论文:Microsoft Word - lowk_persp_interp_06.doc (nus.edu.sg)
图形管线复习
首先是其次坐标到 perspective 的世界坐标的过程,由于我们根据三角形相似把远处的 x y 都以 z 为大的三角形对世界 near 进行了一个比例压缩,但是 z 的压缩是推出来的。
所以结果就是 x y 都带了一个 /z 的项,为了方便计算,最终把这个 z 放到齐次坐标的 w 里。
所以 m(模型本身在他的位置(输入模型在3D中定义的点集合)进行旋转平移等变换)v(模型和照相机一起保留相对位置移动使相机回到坐标原点,用逆矩阵推的)p 投完之后 w 的值实际是 z.
然后是整个管线流程,首先是输出的 vertex,然后输入一堆指定哪些 vertex 构成 triangle,然后对 triangle 进行 rasterization 然后是 fragment 阶段,fragment 可能是对像素也可能是真的 fragment(vertex),然后 fragment 阶段里要进行着色,每一块 fragment (pixel)上什么颜色。然后内容到 frame buffer 里最后输出。
tiger book 的说法是这样的:背向面剔除,即深度测试,mvp ,窗口裁剪,除以齐次坐标,光栅化和光照。
然后现在我们做 shader 的部分,要进行 uv 纹理贴图,最简单的是对一个三角形内的重心坐标来求得相对 3 个 vertex 的比例,线性 interpolate 之后和 uv 坐标对应点进行映射,而三角形的顶点对应的 uv 坐标是艺术家或者生成程序写好的了。
作业二里面主要是做遮挡测试,由于我们只有 vertex 的坐标,所以对整个三角形面里这么多个点 z 需要进行插值,这里用的也是重心坐标插值。这个部分实际就是光栅化,由于mvp后不改变遮挡关系,遮挡测试可以在 perspective 坐标系里面做,所以之后马上就可以 shader 获取点直接输出到 frame buffer。但是这里的问题就在于,这个重心坐标不能用 2D 的 barycentric coordinate 来 interpolate。
实际流水线里还要做裁剪,不在立方体六个平面内的部分都裁剪掉从而不用过光栅化和渲染。(tiger book 也分析了裁剪实际放在 mvp 之前也可以,除以齐次坐标后也可以,在mvp之后也可以,但是除法之后(世界坐标)对于眼睛附近的地方是不连续的,所以这方面的运动就很难,所以一般在除法之前做,而超平面也不难表示)。
管线中求的是 2D 重心坐标
如果我们的 shader 放在 fragment 的阶段那就有一个问题,我们一开始说求 barycentric coordinate 的时候必须要在 3D 坐标系里面计算,而不是在 viewport 里面,但是给 frame buffer 赋值又是在 fragment 的时候在世界坐标里面算的,所以很麻烦。然而由于我们在齐次坐标里保留了 z 的信息,这就够了,我们只需要找到一个从 2d 的 barycentric coordinate 映射回去的闭合表达式即可,而且只需要变换 x y。
这个纹理插值是绝对不能近似的,因为这样贴出来的肯定是错的,这里附上 stackoverflow 上的一个图片:
至于为什么是这种结果是很容易想到的。
所以必须要做这个 perspective correct 的操作,至于为什么我们不在 3D 即 MVP 之前做这个 interpolate 是因为这样没必要,如果我们能快速地反推,那么在有了上面一段讲裁剪的过程(他也是在做 homogeneous division 之前),然后 shading 和 z-buffer 一起做(即 fragment 阶段每个 fragment 做一次测试,通过则进入 shader)能节约一些计算。不过就需要 pass 这个 w 坐标过来(即 z)。所以框架里面传给三角形光栅化的参数还有一个 view_pos (做了 MV 变换后的正常 3D 坐标)。
然而文章里面提到的问题是,w 这个值本身在过程中丢掉了(而且传的三角形坐标是 vector3f),但是我看实际他既然都传了只进行 MV 变换的坐标过去,这么就有了原来的坐标,所以实际上是可以计算出 view space 的重心坐标的啊。。作业3 interpolated_shadingcoords – 计算机图形学与混合现实研讨会 (games-cn.org)这里的汤包喵喵喵也是这样说的。
额,链接没有显示出来都是因为 csdn 富文本编辑器剪贴板图片上传有问题,所以用了 md,实际 google 一下文字都能搜到。
而现在来推导一下这么做这个逆变换吧。
先看论文里的图片,z 是怎么投过来到 viewport 显示平面的:
根据相似三角形的原理(额,不是图的三角形,是从 ACB点往下做一条垂线之后的三角形)用重心坐标的公式(ppt有)和这个相似三角形的坐标,经过一些计算(很容易进行的笔算),就能知道实际3D插值的坐标和 2D插值的坐标是和对应的三角形各顶点的 1/z 成正比的。我们乘上这个就能得到 3D 下的重心坐标。
所以插值的时候用 z 值做这个矫正就行了:
a * f_a / w_a + b * f_b / w_b + c * f_c / w_c
f= -----------------------------------------------------
a / w_a + b / w_b + c / w_c
这里 a b c 是相对三个顶点的值,f 是要插的属性。w 就是上面说的实际他是 z。
f 就是插出来的值 while a b c 是 2D barycentric coordinate。然后就发现了框架的 interpolate_z 的提示代码就算这么写的,我现在终于明白了问题在哪了,哈哈,作业 2 的 interpolate_z 可没有传什么 view_pos 。那我做作业三的时候修正一下吧。