前言:本篇是开始撰写的第一篇关于屏幕后处理效果的图片,所有的屏幕后处理效果其实就是对相机渲染得到的纹理再次处理,本质上都属于Image Processing(IP),即图像处理,包括之后会提到的各种滤波操作,运动模糊等效果。而图像处理是针对所有像素的,所以它基本上都是在片元着色器中进行计算,而不用关心几何着色器(主要用于坐标的转换)。
首先讲碎屏效果,碎屏的效果的产生就是在碎裂的地方的痕迹,它会出现一道明显的“线”,这条线让图片的像素的连贯性出现了间断,而如果在shader着色器内正常根据uv坐标进行采样,会得到一幅连贯的图片,但是如果uv坐标有偏差呢,很多图像处理的效果都借助了修改uv坐标来实现,比如大家最常见到的美颜相机里的瘦脸功能,就是通过对uv坐标的拉伸。所以这里碎屏的思路就是要修改uv坐标,但是怎么修改内,这个当然不能靠几何计算来决定哪些线条上的uv坐标需要被修改,所以就出现了发现贴图,注意这里用的是切线空间的法线贴图,如果不了解法线贴图,请到最后的参考资料中先了解一下切线空间的法线贴图。
我们只需要对法线贴图得到的发现的xy坐标进行处理,通过借助xy来进行处理,因为如果法线是(0,0,1),那么就以为着该像素的uv坐标不需要偏移,否则就要进行相应的偏移,同时还可以设置碎屏程度,只需要将xy乘以一个scale即可。
fixed4 packedNormal = tex2D(_BrokenNormalMap,i.uv.zw);
fixed3 tangentNormal;
tangentNormal=UnpackNormal(packedNormal);
tangentNormal.xy*=_BrokenScale;
float2 offset = tangentNormal.xy;
最后,碎屏之后通常情况下屏幕要变暗,这里通过调整饱和度来解决。这是一个惯用的手法,它是通过从灰度图到原图的插值计算来得到不同饱和程度的图像的。
fixed luminance = (col.r + col.g + col.b) / 3;
fixed3 finalCol = lerp(fixed3(luminance,luminance,luminance),col,0.25);
Shader "Custom/RenderImage/ScreenBroken" {
Properties {
_MainTex ("Main Tex", 2D) = "white" {}
_BrokenNormalMap("BrokenNormal Map",2D)="bump"{}
_BrokenScale("BrokenScale",Range(0,1))=1.0
}
SubShader {
Pass{
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#include "UnityCG.cginc"
#include "Lighting.cginc"
#pragma vertex vert
#pragma fragment frag
//这一部分参数的定义要根据Properties
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BrokenNormalMap;
float4 _BrokenNormalMap_ST;
float _BrokenScale;
struct a2v{
float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
};
//输出部分要和输入部分对应起来,而输出部分又要由片元着色器里的计算模型来确定
struct v2f{
float4 pos : SV_POSITION;
float4 uv : TEXCOORD0;
};
v2f vert(a2v v){
v2f o;
o.pos=UnityObjectToClipPos(v.vertex);
o.uv.xy=TRANSFORM_TEX(v.texcoord,_MainTex);
o.uv.zw=TRANSFORM_TEX(v.texcoord, _BrokenNormalMap);
return o;
}
fixed4 frag(v2f i) : SV_Target{
fixed4 packedNormal = tex2D(_BrokenNormalMap,i.uv.zw);
fixed3 tangentNormal;
tangentNormal=UnpackNormal(packedNormal);
tangentNormal.xy*=_BrokenScale;
float2 offset = tangentNormal.xy;
fixed3 lightColor = fixed3(1,1,1);
fixed3 col=tex2D(_MainTex,i.uv.xy+offset).rgb;
fixed luminance = (col.r + col.g + col.b) / 3;
fixed3 finalCol = lerp(fixed3(luminance,luminance,luminance),col,0.25);
return fixed4(finalCol,1.0f);
}
ENDCG
}
}
FallBack "Diffuse"
}
这部分如果之前不会的话,可以参考:UnityShader屏幕后处理效果之OnRenderImage()
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class ScreenBroken : MonoBehaviour {
public Material mat;
private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
RenderTexture src0=RenderTexture.GetTemporary(source.width,source.height);
mat.SetTexture("_MainTex", source);
Graphics.Blit(source,src0,mat,0);
Graphics.Blit(src0,destination);
RenderTexture.ReleaseTemporary(src0);
}
}
1.切线空间的法线贴图:https://blog.csdn.net/qq_36383623/article/details/85961748
2.本文主要借鉴:https://blog.csdn.net/u011047171/article/details/48807105
3.工程代码:https://github.com/zhimo1997/ShaderDemo/tree/master/Assets/7-RenderImage