UnityShader屏幕后处理效果之碎屏效果

前言:本篇是开始撰写的第一篇关于屏幕后处理效果的图片,所有的屏幕后处理效果其实就是对相机渲染得到的纹理再次处理,本质上都属于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

你可能感兴趣的:(unity,shader编程)