后处理逻辑

原理:根据屏幕的大小设置一个正方形的面片,重新渲染这个面片,调用shader,之后对帧缓冲区图像进行自定义的处理。

OnRenderImage()的on一定要大写!一定要大写!查错查了可久

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// ExecuteInEditMode函数让脚本在编辑阶段可运行
[ExecuteInEditMode()]
public class ImageEffectTest: MonoBehaviour{
public Material material;

void Start(){
// 是否使用当前后处理的判断逻辑
if(material == null || SystemInfo.supportsImageEffects == false
|| material.shader == null || material.shader.isSupported == false){
enabled = false;
return;
}
}
// 只能挂在摄像机上
void OnRenderImage(RenderTexture source, RenderTexture destination){
// 第四个参数表示是哪个pass
Graphics.Blit(source, destination, material, 0);
}
}

计算屏幕空间坐标

起因和原理

起因:因为希望图片在屏幕尺寸变换的时候,跟着一起变换

原理:NDC坐标归一化的时候将图片展到和设备坐标相同的位置

注意:写后处理逻辑的shader的时候一定设置,Cull Off, ZWrite Off,ZTest Always

shader
1
2
3
4
// 透视除法,这一步操作不能在顶点阶段做,提前做了screen_uv就不是线性的值了
half2 screen_uv = i.screen_pos.xy / (i.screen_pos.w + 0.000001);
// 映射到0-1
screen_uv = (screen_uv + 1.0) * 0.5;

基本方法

直接使用上述方法可能会遇到屏幕上下反转的问题,原因是平台坐标不同起始点导致

使用_ProjectionParams.x处理屏幕翻转问题 — 主要是不同平台的处理

1
o.screen_pos.y = o.screen_pos.y * _ProjectionParams.x;

内置方法

  1. ComputeScreenPos(clip_pos) 处理屏幕翻转以及缩放 _ProjectionParams.x
  2. ComputeGrabScreenPos(clip_pos) 处理Grab图像的翻转UNITY_UV_STARTS_AT_TOP
  3. 可以使用vert_img顶点函数和v2f_img结构体来方便编写

ColorAdjustment

色相/饱和度/对比度/亮度

亮度

shader
1
2
half4 col = tex2D(_MainTex, i.uv);
half3 finalcol = col.rgb * _Brightness;

饱和度

饱和度分为两种,线性空间和伽马空间,计算后再通过lerp插值得到饱和度

shader
1
2
3
4
// 伽马空间
dot(col, float3(0.22,0.707,0.071));
// 线性空间
dot(col, float3(0.0396, 0.458, 0.0061));

对比度

和计算饱和度很相似,和(0.5,0.5,0.5)作插值

色相

调整色相先rgb->hsv,对r分量进行操作之后再hsv->rgb, 函数取自unity

晕影/暗角

原理:中心距离周围点的距离,越远越暗

思路:计算中心点到周围距离长度的绝对值

碎屏效果及对应处理

  1. 在unity材质面板中设置wrap mode

  2. 根据屏幕宽高比例进行设置,shader对应使用_ScreenParams.x和_ScreenParams.y

法线图Noise修正

出现原因:法线贴图中有噪点

修改方法:ps中修改贴图

UV边缘扭曲限制

shader
1
2
3
4
half2 d = 1.0 - smoothstep(0.9,1,abs(i.uv * 2.0 -1.0));
half vfactor = d.x * d.y;

half2 uv_distort = i.uv + glass_normal.xy * _Distort * vfactor;