Date: 2020-05-12
Post Processing中有时候需要获取屏幕坐标对应的世界坐标,获取方式如下,需要相机开启深度缓冲支持。
原理大概是预先计算出clip空间到世界空间的转换矩阵,在shader中将clip还原为世界坐标,继而计算出世界坐标到相机坐标的矢量以进行插值计算,最后在片段着色器中还原出世界坐标。
PPS中支持代码如下:
using System;
using UnityEngine;
using UnityEngine.Rendering.PostProcessing;
[Serializable]
[PostProcess(typeof(DrawWorldSpacePositionRenderer), PostProcessEvent.AfterStack, "Custom/DrawWorldSpacePosition")]
public sealed class DrawWorldSpacePosition : PostProcessEffectSettings
{
}
public sealed class DrawWorldSpacePositionRenderer : PostProcessEffectRenderer<DrawWorldSpacePosition>
{
public override void Render(PostProcessRenderContext context)
{
var sheet = context.propertySheets.Get(Shader.Find("Hidden/Custom/DrawWorldSpacePosition"));
var camera = context.camera;
var p = GL.GetGPUProjectionMatrix(camera.projectionMatrix, false);// Unity flips its 'Y' vector depending on if its in VR, Editor view or game view etc...
p[2, 3] = p[3, 2] = 0.0f;
p[3, 3] = 1.0f;
var clipToWorld = Matrix4x4.Inverse(p * camera.worldToCameraMatrix) * Matrix4x4.TRS(new Vector3(0, 0, -p[2, 2]), Quaternion.identity, Vector3.one);
sheet.properties.SetMatrix("clipToWorld", clipToWorld);
context.command.BlitFullscreenTriangle(context.source, context.destination, sheet, 0);
}
}
Shader如下:
Shader "Hidden/Custom/DrawWorldSpacePosition"
{
HLSLINCLUDE
#include "../PostProcessing/Shaders/StdLib.hlsl"
TEXTURE2D_SAMPLER2D(_CameraDepthTexture, sampler_CameraDepthTexture);
uniform float4x4 clipToWorld;
struct VaryingsExtended
{
float4 vertex : SV_POSITION;
float2 texcoord : TEXCOORD0;
float2 texcoordStereo : TEXCOORD1;
float3 worldDirection : TEXCOORD2;
};
VaryingsExtended Vert(AttributesDefault v)
{
VaryingsExtended o;
o.vertex = float4(v.vertex.xy, 0.0, 1.0);
o.texcoord = TransformTriangleVertexToUV(v.vertex.xy);
#if UNITY_UV_STARTS_AT_TOP
o.texcoord = o.texcoord * float2(1.0, -1.0) + float2(0.0, 1.0);
#endif
o.texcoordStereo = TransformStereoScreenSpaceTex(o.texcoord, 1.0);
o.worldDirection = mul(clipToWorld, o.vertext) - _WorldSpaceCameraPos;
return o;
}
float4 Frag(VaryingsExtended i) : SV_Target
{
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, sampler_CameraDepthTexture, i.texcoordStereo);
depth = LinearEyeDepth(depth);
float3 worldspace = i.worldDirection * depth + _WorldSpaceCameraPos;
float4 color = float4(worldspace, 1.0);
return color;
}
ENDHLSL
SubShader
{
Cull Off ZWrite Off ZTest Always
Pass
{
HLSLPROGRAM
#pragma vertex Vert
#pragma fragment Frag
ENDHLSL
}
}
}