作用:
把裁剪空间齐次坐标转换到屏幕空间的齐次坐标
原理:
裁剪空间下的齐次坐标,x和y的取值范围是[-w, w], w=-z。
而屏幕空间下的齐次坐标取值范围是[0, w], w=-z。所以核心代码就是 float2 screenPos = (clipPos.xy + clipPos.w) / 2;
最后通过透视除法之后xy的取值范围就是[0, 1]了。一般用来提取屏幕纹理
源码:
其中在DirectX下屏幕坐标的原点在左上角,所以需要翻转Y坐标,此时_ProjectParams.x变量为-1。另外,在使用Direct3D9时,它会注意纹理对齐。在进行单遍立体渲染时,还需要特殊的逻辑。
用法:
在顶点着色器或者片元着色器调用:float4 screenPos = ComputeScreenPos(ClipPos);
在片元着色器用来提取纹理像素:
tex2D(_ScreenTex, float2(screenPos.xy / screenPos.w))
或者
tex2Dproj(_ScreenTex, screenPos); // 该函数会自动把screenPos通过齐次除法(透视除法)转换成普通坐标之后再对纹理进行采样
用ComputeScreenPos的结果,进行透视除法后,再乘以_ScreenParams.xy(xy分别是宽、高)后(即(screenPos.xy / screenPos.w) * _ScreenParams.xy),则可以得到真正的屏幕坐标。
需要注意的是screenPos透视除法这一步不能在顶点着色器中使用,透视除法之后会导致片元函数中插值结果错误。
如下图:
时候先看一下正确的情况:
现有顶点a和b,齐次裁剪坐标(clipPos),在光栅化线性插值之后,在片元着色器得到一簇片元即ab线段上的所有片元,然后进行透视除法,则得到cb线段上的投影点,此时的z值对应的,则是db曲线,。即a点对应c点,a下面的点对应c右边的点。这是正确的
若在顶点着色器进行透视除法
现有顶点a和b,在顶点着色器进行透视除法,则得到c点和b点,然后通过线性插值,在片元着色器中的到cb上的所有片元,此时得到的z值则对应db直线,即a点对应c点,a下面的点对应c右边的点,这是错误的,正确的点应该更靠近c一点。