前几天尝试写一个传送门的shader,发现本身对坐标之间的变换掌握的不够熟练,趁着这阵子想整理shader相关的知识点,先把各类空间及之间转换整理一下。编程
建模时在模型空间进行,模型自带的坐标均为模型空间下的表示。
当模型被放到世界坐标系中时,表达某个模型的位置使用的是世界空间下的坐标,因此模型上对应的某一个点,必须相应的转化为世界空间下的坐标。
从模型空间到世界空间的变换 叫作 模型变换。
Unity的Shader中,模型空间的坐标由Renderer直接提供,做为顶点着色器的输入,语义为POSITION。如:app
struct appdata { float4 vertex : POSITION; }
于咱们能看到的渲染图像均是经过摄像机获得的,为了方便后续裁剪、投影等操做,因此在将模型从模型空间变换到世界空间以后,还须要将其转换到观察空间。所谓的观察空间,就是以摄像机位置为原点,摄像机局部坐标轴为坐标轴的坐标系。
从世界空间到观察空间的变换叫作观察变换(视图变换)。this
坐标转换到观察空间后,因为直接使用摄像机的平截头体进行裁剪比较复杂(平截头体的边界方程求交困难),因此须要将其转化到裁剪空间。裁剪空间变换的思路是,对平截头体进行缩放,使近裁剪面和远裁剪面变成正方形,使坐标的w份量表示裁剪范围,此时,只须要简单的比较x,y,z和w份量的大小便可。
从观察空间到裁剪空间的变换叫作投影变换。
注意,虽然叫作投影变换,可是投影变换并无进行真正的投影。spa
在顶点着色器中,模型、观察、裁剪空间的相关变换矩阵通常为如下几个:.net
别名 | 定义 | 含义 |
---|---|---|
UNITY_MATRIX_M | unity_ObjectToWorld | 模型变换矩阵 |
UNITY_MATRIX_V | unity_MatrixV | 视图变换矩阵 |
UNITY_MATRIX_P | glstate_matrix_projection | 投影变换矩阵 |
UNITY_MATRIX_VP | unity_MatrixVP | 视图投影变换矩阵 |
UNITY_MATRIX_MV | mul(unity_MatrixV, unity_ObjectToWorld) | 模型视图变换 |
UNITY_MATRIX_MVP | mul(unity_MatrixVP, unity_ObjectToWorld) | 模型视图投影变换 |
注意,最新版本中(笔者用的是Unity5.6.3p4),官方建议将坐标点从模型空间转换到裁剪空间时,应使用UnityObjectToClipPos方法,该方法内部定义为:code
mul(UNITY_MATRIX_VP, mul(unity_ObjectToWorld, float4(pos, 1.0));
上述方法比起下面的顺序更有效率。blog
mul(UNITY_MATRIX_MVP, appdata.vertex);
在定点着色器中,输出即为裁剪空间下的坐标。接口
通过裁剪操做以后,咱们须要将裁剪空间的坐标投影到屏幕空间。
这里主要分红两个步骤:
1. 齐次除法
使用xyz分别处以w份量,获得NDC(归一化设备坐标),通过这一步,可以看到的坐标点变成了一个xy边长为1,z为-1到1的立方体。
2. 屏幕映射
使用NDC坐标和屏幕长宽像素直接映射,获得屏幕空间下的xy坐标。注意,虽然屏幕空间没有深度,但屏幕空间下的坐标仍然保留了z的深度值,能够进行深度检测或其余处理。图片
从裁剪空间到屏幕空间由unity直接进行。这里还要记住,从裁剪空间到屏幕空间,插值是硬件直接进行的。ip
以后咱们从像素着色器中得到的语义为SV_POSITION的输入,其坐标基本没有太多用处了。
若是但愿得到此时的屏幕坐标,可使用VPOS或者WPOS,在Unity中,这二者同义。固然,使用上述语义须要
#pragma target 3.0
VPOS中的xy表明屏幕空间中的像素坐标,注意,这里的像素坐标是中心点坐标,不是整数,如屏幕分辨率为400*300,则x的范围为[0.5, 400.5],y的范围为[0.5, 300.5]。z的范围为[0, 1],0是近裁剪面,1是远裁剪面。w的范围须要根据摄像机投影类型划分,若是是透视投影,则其范围为[1/near,1/far],若是是正交投影,则恒为1。
这里还须要注意的是,若是使用VPOS或者WPOS做为fs的输入,就没法同时使用SV_POSITION做为输入,因此vs和fs须要写成以下方式:
Shader "Unlit/Screen Position" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma target 3.0 // note: no SV_POSITION in this struct struct v2f { float2 uv : TEXCOORD0; }; v2f vert ( float4 vertex : POSITION, // vertex position input float2 uv : TEXCOORD0, // texture coordinate input out float4 outpos : SV_POSITION // clip space position output ) { v2f o; o.uv = uv; outpos = UnityObjectToClipPos(vertex); return o; } sampler2D _MainTex; fixed4 frag (v2f i, UNITY_VPOS_TYPE screenPos : VPOS) : SV_Target { // screenPos.xy will contain pixel integer coordinates. // use them to implement a checkerboard pattern that skips rendering // 4x4 blocks of pixels // checker value will be negative for 4x4 blocks of pixels // in a checkerboard pattern screenPos.xy = floor(screenPos.xy * 0.25) * 0.5; float checker = -frac(screenPos.r + screenPos.g); // clip HLSL instruction stops rendering a pixel if value is negative clip(checker); // for pixels that were kept, read the texture and output it fixed4 c = tex2D (_MainTex, i.uv); return c; } ENDCG } } }
摄像机和屏幕相关经常使用的内置变量以下表所示。
别名 | 类型 | 含义 |
---|---|---|
_WorldSpaceCameraPos | float3 | |
_ProjectionParams | float4 | |
_ScreenParams | float4 | |
_ZBufferParams | float4 | |
unity_OrthoParams | float4 | |
unity_CameraProjection | float4x4 | |
unity_CameraInvProjection | float4x4 | |
unity_CameraWroldClipPlanes[6] | float4 |
熟悉OpenGL编程的同窗都知道gl接口有一个glViewport,该接口其实就是肯定视口空间。视口空间是一个将屏幕坐标对应到(0, 0)到(1, 1)范围内的空间。
若是想获得视口空间坐标,则能够经过下面两种方法。
fixed4 frag(float4 sp : VPOS) : SV_Target { return fixed(sp.xy/_ScreenParams.xy, 0.0, 1.0); }
这里的_ScreenParams中保存了屏幕分辨率。
另外一种方法以下:
struct vertOut { float4 pos : SV_POSITION; float4 srcPos : TEXCOORD0; }; vertOut vert(appdata_base v) { vertOut o; o.pos = UnityObjectToClipPos(v.vertex); o.srcPos = ComputeScreenPos(o.pos); return o; } fixed4 frag(vertOut i) : SV_Target { float2 wcoord = (i.srcPos.xy/i.scrPos.w); return fixed4(wcoord, 0.0, 1.0); }
这里,ComputeScreenPos并无直接进行透视除法,缘由是插值是线性的,必须在透视除法以后进行,因此,咱们必须在fs中手动进行。
几篇不错的参考资料以下:
http://blog.csdn.net/wangdingqiaoit/article/details/51594408
http://blog.csdn.net/wangdingqiaoit/article/details/51589825