Unity Shader中的ComputeScreenPos函数

Unity Shader中的ComputeScreenPos函数

Unity Shader中的ComputeScreenPos函数 EnigmaJJ 关注

2017.02.03 20:10* 字数 465 阅读 4727评论 0喜欢 12

Unity shader提供了内置函数ComputeScreenPos,其定义位于UnityCG.cginc中,大致如下:

 

Unity Shader中的ComputeScreenPos函数

ComputeScreenPos函数定义

 

其中的宏UNITY_SINGLE_PASS_STEREO用于控制在不同平台上计算方式的差异,而宏UNITY_HALF_TEXEL_OFFSET用于支持DirectX 9的像素偏移。
看该函数的命名第一直觉是该函数返回的是屏幕空间下的坐标值,但是该函数返回的是齐次坐标下的屏幕坐标值。让我们剔除不必要的代码,来看核心的计算代码,如下:

inline float4 ComputeScreenPos (float4 pos)
{
    float4 o = pos * 0.5f;
    o.xy = float2(o.x, o.y*_ProjectionParams.x) + o.w;
    o.zw = pos.zw;
    
    return o;
}

首先,该函数传入的参数pos为顶点变换到齐次坐标系下的坐标,也就是说,在shader中,你需要这么使用:

o.pos = UnityObjectToClipPos(v.vertex);
o.screenPos = ComputeScreenPos(o.pos);

接着来回顾下如何从齐次坐标变换到屏幕坐标:我们知道,顶点在变换到齐次坐标后,其x和y分量的范围在[-w, w]。假设目前屏幕的宽度为width,高度为height,那么屏幕坐标的计算方法为:

screenPosX = ((x / w) * 0.5 + 0.5) * width
screenPosY = ((y / w) * 0.5 + 0.5) * height

我们来看ComputeScreenPos是如何做的,它的计算方法为:

o.x = (pos.x * 0.5 + pos.w * 0.5)
o.y = (pos.y * 0.5 * _ProjectionParams.x + pos.w * 0.5)

其中_ProjectionParams.x用于在使用翻转投影矩阵时(此时其值为-1.0)翻转y的坐标值。
现在我们将屏幕坐标的计算方法变换到ComputeScreenPos使用的计算方法,如下:

screenPosX / width = ((x / w) * 0.5 + 0.5)
screenPosY / height = ((y / w) * 0.5 + 0.5)

两边同时乘w:

screenPosX / width * w = ((x * 0.5 + w * 0.5)
screenPosY / height * w = ((y * 0.5 + w * 0.5)

现在一目了然了,ComputeScreenPos返回的值是齐次坐标系下的屏幕坐标值,其范围为[0, w]。那么为什么Unity要这么做呢?Unity的本意是希望你把该坐标值用作tex2Dproj指令的参数值,tex2Dproj会在对纹理采样前除以w分量。当然你也可以像下面代码那样自己除以w分量后进行采样,但是效率不如内置指令tex2Dproj:

pos = UnityObjectToClipPos(v.vertex);
screenPos = ComputeScreenPos(o.pos);
tex2D(_ScreenTex, float2(screenPos.xy / screenPos.w))