获取世界坐标
posInput的坐标需要再加上相机的坐标,蜜汁小小坑…
float3 ws = _WorldSpaceCameraPos + posInput.positionWS;
利用深度信息获取近/远裁剪平面坐标
用到函数 ComputeWorldSpacePosition()。
float3 nearPositionWS = ComputeWorldSpacePosition(posInput.positionNDC, 1, UNITY_MATRIX_I_VP);
nearPositionWS += _WorldSpaceCameraPos;
这个函数的内部实现当然还是用NDC坐标推世界空间坐标,但此处参数传入的是屏幕空间UV。(虽然他命名成NDC- -,但实际上是屏幕空间UV,左下角(0, 0) , 右上角(1, 1))
另外,此处近平面深度是1,远平面是0。
这篇链接详细推导了如何使用NDC坐标和深度推导世界空间坐标。上善若水_2019-根据深度信息重建屏幕像素在世界中的坐标-简书
在顶点着色器中实现
注释掉引用 CustomPassCommon.hlsl 的那一行,把 CustomPassCommon.hlsl 里面的内容复制到你的着色器中,解锁顶点着色器。
把顶点着色器中的代码更改成这样子
struct Varyings
{
float4 positionCS : SV_POSITION;
// 增加一个寄存器
float3 positionWS : TEXCOORD0;
UNITY_VERTEX_OUTPUT_STEREO
};
Varyings Vert(Attributes input)
{
Varyings output;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
// 注释掉
// output.positionCS = GetFullScreenTriangleVertexPosition(input.vertexID, UNITY_RAW_FAR_CLIP_VALUE);
// 改成
float2 uv = float2((input.vertexID << 1) & 2, input.vertexID & 2);
output.positionCS = float4(uv * 2.0 - 1.0, UNITY_RAW_FAR_CLIP_VALUE, 1.0);
float3 nearPositionWS = ComputeWorldSpacePosition(uv, 1, UNITY_MATRIX_I_VP);
nearPositionWS += _WorldSpaceCameraPos;
output.positionWS = nearPositionWS;
return output;
}
外部传参方式获取远裁剪平面坐标
假如不容易获取深度信息,可以在外部将摄像机的近/远裁剪平面传入Shader。
skyboxMaterial.SetVectorArray("_Cornel", new List<Vector4>
{
cam.ViewportToWorldPoint(new Vector3(0, 0, cam.farClipPlane)),
cam.ViewportToWorldPoint(new Vector3(1, 0, cam.farClipPlane)),
cam.ViewportToWorldPoint(new Vector3(0, 1, cam.farClipPlane)),
cam.ViewportToWorldPoint(new Vector3(1, 1, cam.farClipPlane)),
});
在 Shader 借助预存的UV或其他信息取得对应数据
float4 _Cornel[4];
v2f vert (appdata v)
{
// 省略
o.worldPos = _Cornel[v.uv.x + v.uv.y * 2].xyz;
// 省略
}