Unity渲染(4):Shader着色器基础入门获取当前屏幕贴图
通过这里,你将学习如何获得当前屏幕的地图和地图UV,通常用于屏幕的后处理。
上一章:Shader着色器基础入门模糊(Blur)
开发环境:Unity5.0或者更高
概述
1. 获取屏幕贴图 2. 获取屏幕UV 3. 完整代码
1.1 获取屏幕贴图
Unity 内置GrabPass
获取屏幕贴图,语法如下,代码写在SubShader
里面。
Unity-GrabPass官方说明
GrabPass{ "
定制地图名称"}
此Pass其功能是获取当前帧缓冲的内容。我们可以打开它FrameDebugger看到
1.2 获取屏幕UV
Unity为屏幕上获取物体提供了一个接口uv坐标 ComputeGrabScreenPos (float4 clipPos)
ComputeGrabScreenPos 官方说明
他需要输入一个float4.这个坐标是切割空间的坐标shader可以通过UnityObjectToClipPos()
接口获取 函数定义为UnityCG.cginc, 使用前需要介绍此文件
#include "UnityCG.cginc"
这是他的函数原型
inline float4 ComputeGrabScreenPos (float4 pos) {
#if UNITY_UV_STARTS_AT_TOP float scale = -1.0; #else float scale = 1.0; #endif float4 o = pos * 0.5f; o.xy = float2(o.x, o.y*scale) o.w; #ifdef UNITY_SINGLE_PASS_STEREO o.xy = TransformStereoScreenSpaceTex(o.xy, pos.w); #endif o.zw = pos.zw; return o; }
代码说明
- #if UNITY_UV_STARTS_AT_TOP 因为在DX平台和OpenGl平台,坐标轴不同,DX左上角定义(0,0),OpenGl定义(0,0)在左下角。防止图像翻转并添加此代码
- 首先,我们需要知道如何将坐标从切割空间转换为屏幕空间。中间过程有两个步骤。首先,透视除法将进行xyz除以w重量,让值x,y,z的范围都处于[-1,1]之间,
所谓透视除法,主要针对透视投影,正交投影w=1
。然后将此坐标转换为屏幕空间,x坐标[-1,1]->[0,屏宽] ,y坐标[-1,1]->[0,屏幕高度]因于以下公式
但我们需要的是UV只需转换坐标[,1]即可,相当于上述公式的pixelWidth = 1,pixelHeight = 1, 当相机为正交投影时:上述公式clipw = 1 则带入可得到Unity源码中的float4 o = pos * 0.5f; o.xy = float2(o.x, o.y*scale) + o.w;
, 当相机为透视投影时 源码使用TransformStereoScreenSpaceTex(o.xy, pos.w);
进行处理,里面的代码是
float2 TransformStereoScreenSpaceTex(float2 uv, float w)
{
float4 scaleOffset = unity_StereoScaleOffset[unity_StereoEyeIndex];
return uv.xy * scaleOffset.xy + scaleOffset.zw * w;
}
原理是将透视投影的相机平头截体向中间缩放挤压,变成和正交投影一样,在和正交投影一样处理。
最后我们需要再采样的时候将uv.xy / uv.w 因为我们再上面假设clipw =1了
1.3 完整代码
Shader "Toturial/ScreenGrab"
{
Properties
{
_MainTex("MainTex",2D) = "white"{
}
}
SubShader
{
GrabPass{
"_bTexture"}
Tags{
"Queue"="Transparent"}
Pass
{
CGPROGRAM
#include "UnityCG.cginc"
#pragma vertex vert
#pragma fragment frag
sampler2D _MainTex;
sampler2D _bTexture;
struct a2v
{
float4 vertex : POSITION;
};
struct v2f
{
float4 pos : SV_POSITION;
float4 grabPos : TEXCOORD1;
};
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.grabPos = ComputeScreenPos(o.pos);
return o;
}
fixed4 frag(v2f i) : SV_TARGET
{
fixed4 screenColor = tex2D(_bTexture, i.grabPos.xy / i.grabPos.w);
return screenColor * 2;
}
ENDCG
}
}
}
最终得到效果