unity shader视口空间到屏幕空间各坐标的取值范围

 

模型空间->世界空间->视口空间:

比较容易理解,就是用矩阵进行缩放旋转和平移

 

视口空间(右手坐标系)-> 齐次裁剪空间(左手坐标系):

下文中的n代表near,f代表far,fov代表FOV

设视口空间坐标点为Vp = {Vx, Vy, Vz},摄像机前方的Vz为负数,裁剪空间坐标点为Cp = {Cx, Cy, Cz, Cw}

则裁剪空间的未映射坐标是:Cp = {Cx, Cy, Cz, Cw} = {Vx * n, Vy * n,  a*Vz + b, -Vz},

此时对Cp进行透视除法,得到的x,y范围是[left, right]和[bottom, top]

若对x,y从[left, right]和[bottom, top]映射到[-1, 1],则可以得到正确的裁剪坐标如下:

对此时的裁剪坐标进行透视除法,得到的x,y的范围是[-1, 1]

推导请查看:https://blog.csdn.net/zengjunjie59/article/details/109572857

变换到齐次裁剪空间后不同类型API得到的取值范围不同:

类 OpenGL 齐次裁剪空间得到的数值范围

Cx∈[-Cw, Cw],  Cy∈[-Cw, Cw],  Cz∈[-Cw, Cw],从视锥体角度看Cz∈[-near, far],  Cw = -Vz,明显,随着|Vz|的增大,|Cx|和|Cy|会随着增大(即视锥体的形状)

类 DX 齐次裁剪空间得到的数值范围

Cx∈[-Cw, Cw],  Cy∈[-Cw, Cw],  Cz∈[0, Cw], 从视锥体角度看Cz ∈ [ 0, far], Cw = -Vz某些平台下,会定义UNITY_REVERSED_Z,此时则Cz∈[Cw, 0],越靠近屏幕,Cz越接近Cw,这是为了提高近距离的深度精度,, 从视锥体角度看, Cz ∈ [near, 0](越靠近近裁切面,Cz越接近near)

详细解析:https://blog.csdn.net/acmhonor/article/details/106167261

官方文档:https://docs.unity3d.com/cn/current/Manual/SL-PlatformDifferences.html

需要注意的是:

在顶点着色器中使用UnityObjectToClipPos函数把模型空间顶点变换到裁剪空间的时候,在顶点着色器中该坐标是裁剪空间下的坐标,但是如果通过SV_POSITION语义传到片元着色器之后,该坐标会被自动变换到了屏幕坐标下,此时的x和y已经是屏幕坐标了。

 

 

Cp是齐次坐标,并且是线性相关的,即均匀更变X的时候Z也是均匀更变的(导数为常数的一次函数),可以进行线性插值,:

设Vy不变,       Vz与Vx是线性相关的(图像是一条直线)。

Cx = Vx * n,    Cx与Vx是线性相关。

Cz = a*Vz + b,  Cz与Vz是线性相关。

由于线性相关的传递性,所以Cx与Cz线性相关,y同理,所以Cp = {Cx, Cy, Cz, Cw} 裁剪坐标间可进行线性插值

例子(忽略y值,y与x同理):

需得到Cz和Cx的关系,即:Cz = f(Cx)

由于Vz和Vx是线性关系,

所以有(1)Vz = cVx + d 

(2)Cx = nVx  

(3)Cz = aVz + b   

三式联立:

Cz = f(Cx) = aVz+ b = a(cVx + d) + b = acVx + ad + b = ac(Cx/n) + ad + b = ac/nCx + ad + b

即:Cz = ac/nCx + ad + b, 所以Cz和Cx是线性关系

 

齐次裁剪空间 -> 屏幕空间:

裁剪后需要进行真正的投影,需要把视椎体投影到屏幕空间(screen space),最后会得到像素位置。

将顶点从裁剪空间投影到屏幕空间,来生成2D坐标。

第一步,需要进行齐次除法(homogeneous diision),就是把齐次坐标系中的x,y,z分量除以w,在OpenGL中这一步得到的坐标也被称为归一化的设备坐标(Normalized Device Coordinated, NDC).

在OpenGL里,x,y,z的取值范围是[-1, 1]

在DirectX里,x,y的取值范围是[-1, 1],z的取值范围是[0, 1],如果定义了UNITY_REVERSED_Z,则z的范围在[1, 0],  越靠近屏幕,z越大

深度:

即depth buffer里面的值,则是用此时的z通过线性映射到[0, 1]中,如果定义了UNITY_REVERSED_Z,则是映射到[1, 0],越靠近屏幕深度越大。

第二步,映射过程,在Unity中,屏幕左下角是(0,0),右上角是(pixelWidth,pixelHeight),由于立方体内的坐标都是[-1,1],因此映射过程就是一个缩放的过程。

齐次除法和屏幕映射可以总结为下面的公式:

 

 

此外,从裁剪空间到屏幕空间的转换是由底层帮我们完成的,如在屏幕后处理等情况中需要用到,则可调用ComputeScreenPos函数:https://blog.csdn.net/zengjunjie59/article/details/111144851

需要注意的是ComputeScreenPos如果在顶点着色器中调用,那么结果只能在顶点着色器中使用,因为经过插值之后传到片元着色器中的时候数值有错误,之所以错误是因为转换第一步中的透视除法导致NDC下的坐标Np = {Nx, Ny, Nz}中的Nx与裁剪坐标Vp中的Vz不是线性关系了。因为Nz = -b / Vz - a,设Ny不变,当投影后的Nx均匀改变的时候Vz并不是均匀改变的(导数不是常数),如果把Nx与Vz当作线性相关的进行插值求得深度Vz会是错误的,应该是一条曲线。

 

 

 

 

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值