一个模型的MVP变换,即从模型空间(M)转换到观察空间(V)再转换到裁剪空间(P),然后将投影完成的标准立方体通过屏幕映射(视口变换)转换成屏幕坐标进行显示。
下图即标准立方体到屏幕空间的映射过程。
v2f vert (appdata v)
{
o.screen_pos = o.pos;
//_ProjectionParams.x用于校正不同平台下的uv坐标系
o.screen_pos.y = o.screen_pos.y * _ProjectionParams.x;
}
half4 frag (v2f i) : SV_Target
{
//透视除法,将裁剪空间坐标从[-w,w]变换到屏幕空间坐标[-1,1]
half2 screen_uv = i.screen_pos.xy / (i.screen_pos.w + 0.000001);
//重构屏幕uv坐标到[0,1]
screen_uv = (screen_uv + 1.0) * 0.5;
half4 col = tex2D(_MainTex, screen_uv);
return col;
}
v2f vert (appdata v)
{
//处理跨平台所引起的坐标差异问题(包括屏幕翻转与缩放)
o.screen_pos = ComputeScreenPos(o.pos);
}
half4 frag (v2f i) : SV_Target
{
//透视除法,将裁剪空间坐标从[-w,w]变换到屏幕空间坐标[0,1]
half2 screen_uv = i.screen_pos.xy / (i.screen_pos.w + 0.000001);
half4 col = tex2D(_MainTex, screen_uv);
return col;
}
//纠正贴图uv与屏幕uv的不匹配
//_ScreenParams.x = width, _ScreenParams.y = height, _ScreenParams.z = 1 + 1.0 / width, _ScreenParams.z = 1 + 1.0 / height
half aspect = _ScreenParams.x / _ScreenParams.y;
half2 mask_uv = i.uv * _Mask_ST.xy + _Mask_ST.zw;
mask_uv.x = mask_uv.x * aspect;
half mask = tex2D(_Mask, glass_uv).r;
final_RGB = lerp(final_RGB, _MaskIntensity, mask);
half3 normal = UnpackNormal(tex2D(_GlassNormal, glass_uv));
half2 distance = 1.0 - smoothstep(0.98, 1, abs(i.uv * 2.0 - 1.0));
half vfactor = distance.x * distance.y;
half2 uv_distort = i.uv + normal.xy * _Distort * vfactor;
half4 col = tex2D(_MainTex, uv_distort);
//抠出法线mask
half2 distance_mask = step(0.005, abs(glass_normal.xy));
half mask_data = distance_mask.x * distance_mask.y;
half2 uv_distort = i.uv + normal.xy * _Distort * vfactor * mask_data;
half4 col = tex2D(_MainTex, uv_distort);
首先需要给Camera挂上后处理的C#脚本:
public class EasyImageEffect : MonoBehaviour
{
public Material mat;
public float _Brightness = 1;
public float _Saturation = 1;
public float _Contrast = 1;
[Range(0.05f,3.0f)]
public float _VignetteIntensity = 1.5f;
[Range(0.05f,5.0f)]
public float _VignetteRoundness = 5.0f;
[Range(0.05f,5.0f)]
public float _VignetteSmoothness = 5.0f;
void Start()
{
//初始化操作
if (mat == null || mat.shader == false || mat.shader.isSupported == false)
{
enabled = false;
return;
}
}
private void OnRenderImage(RenderTexture src, RenderTexture dest)
{
mat.SetFloat("_Brightness", _Brightness);
mat.SetFloat("_Saturation", _Saturation);
mat.SetFloat("_Contrast", _Contrast);
mat.SetFloat("_VignetteIntensity", _VignetteIntensity);
mat.SetFloat("_VignetteRoundness", _VignetteRoundness);
mat.SetFloat("_VignetteSmoothness", _VignetteSmoothness);
Graphics.Blit(src, dest, mat, 0);
}
}
然后创建后处理Shader:
SubShader
{
// No culling or depth
Cull Off ZWrite Off ZTest Always
Pass
{
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
float _Brightness;
float _Saturation;
float _Contrast;
float _VignetteIntensity;
float _VignetteRoundness;
float _VignetteSmoothness;
half4 frag (v2f_img i) : SV_Target
{
half4 col = tex2D(_MainTex, i.uv);
//亮度
half3 final_RGB = col * _Brightness;
//饱和度
//gamma空间求明度
half lumin = dot(final_RGB, half3(0.22, 0.707, 0.071));
//linear空间求明度
// half lumin = dot(final_RGB, half3(0.0396, 0.458, 0.0061));
final_RGB = lerp(lumin, final_RGB, _Saturation);
//对比度
half3 mid_point = half3(0.5, 0.5, 0.5);
final_RGB = lerp(mid_point, final_RGB, _Contrast);
//暗角
half2 distance = abs(i.uv - half2(0.5, 0.5)) * _VignetteIntensity;
distance = pow(saturate(distance), _VignetteRoundness);
half dis_length = length(distance);
half vfactor = pow(saturate(1.0 - dis_length * dis_length), _VignetteSmoothness);
final_RGB = lerp(0.02, final_RGB, vfactor);
return half4(final_RGB, col.a);
}
ENDCG
}
}