一、屏幕后处理的实现原理
该屏幕后处理的原理是将渲染完成后的屏幕纹理通过脚本和Shader完成一些操作,然后实现各种屏幕效果
而实现屏幕后处理效果的主要操作就是获得当下渲染完成后的屏幕图像,其中unity提供了一个函数用于获取此图像——OnRenderImage ( )
private void OnRenderImage(RenderTexture src,RenderTexture dest) { ... }
当在脚本中声明此函数后,Unity会把当前渲染完成后的图像存储在src源渲染纹理中,通过函数中的一系列操作后(该函数内操作是我们自定义的),再把目标渲染纹理存储在dest渲染纹理中,dest最终会被显示到屏幕上
然后在OnRenderImage函数中可以利用Graphics.Blit函数来完成对渲染后的屏幕图像的操作
Graphics.Blit(Texture src , RenderTexture dest , Material Mat);
Graphics.Blit(Texture src , RenderTexture dest , Material Mat , int pass=-1);
Graphics.Blit(src, dest, Mat);
src为源纹理,dest为处理后的纹理最后会显示到屏幕上
mat是所使用的材质,该材质会利用相应的Sahder代码对源纹理进行屏幕后处理效果的实现
其中src源纹理会被传入到Shader的_MainTex属性中,所以我们就可以利用Sahder对_MainTex属性进行修改来实现屏幕后处理的效果
同时参数Pass的默认值为-1,表示将会依次调用Shader内的所有Pass
二、关于黑白阈值的屏幕后处理效果实现
首先创建一个脚本,挂载到摄像机上
在脚本中添加OnRenderImage函数
private void OnRenderImage(RenderTexture src , RenderTexture dest)
{
Graphics.Blit(src,dest,Mat)
}
其中Mat代表的材质还未指定,可以在脚本中添加一个公共的Sahder用于存放所需要的Shader,然后通过该Shader创建对应的材质球(Material _newmat = new Material( PostProcessingShader );)
public Shader PostProcessingShader;
Material _newmat = new Material( PostProcessingShader ); //通过Shader创建材质球
然后添加一个关于Mat的Get/Set属性访问器(public Material Mat),然后在public Material Mat中设置 private Material mat 的值
当在OnRenderImage函数中使用到Mat时,此时就会进入到public Material Mat的Get方法中读取到mat的值,从而返回给OnRenderImage函数该值。具体如下方所示:
private Material mat;
public Material Mat
{
get
{
//当mat为空时
if(mat==null)
{
//为指定的Shader新建一个材质球
Material _newmat = new Material( PostProcessingShader );//材质球隐藏并且不保存
_newmat.hideFlags = HideFlags.HideAndDontSave;
mat = _newmat;
//将新建的材质球返回给Mat
return mat; //读取并返回值
}
//mat不为空
else
{
return mat; //读取并返回值
}
}
}
关于Get/Set属性访问器的具体用法和原理参考:
Unity-Get/Set属性访问器详解_unity get set-CSDN博客
C#的属性:get、set_c# get =>-CSDN博客
Get/Set属性访问器中使用get方法中读取值,set方法设置值。在set方法中可以对值的设置进行限制,起到一定的安全作用
接着在 "PostProcessingShader" 的Shader代码中来完成屏幕后处理效果,例如黑白阈值
接上文,src源纹理会通过Graphics.Blit函数传入到Shader的_MainTex属性中,所以在Shader可以通过uv坐标采样_MainTex得到源纹理的fixed4 类型的颜色值
对于黑白阈值的实现,可以根据采样后的 col.r 与一个滑杆值进行step函数的计算
step(a,b) 当a<=b时 , return 1; else return 0
滑杆值:_BW("BW",Range(0,1))=0
采样贴图后的值:fixed4 col = tex2D(_MainTex, i.uv);
step(col.r , _BW); //step(a,b) 当a<=b时,return 1; else return 0
但是此时Shader创建的材质球不会被暴漏出来,所以材质球中_BW的滑杆属性不能被调节
所以此时只能在脚本代码中对_BW参数进行调节
- 先在脚本中设置一个变量,用于控制黑白阈值 [Range(0,1)] public float bw;
- 然后通过SetFloat在脚本中更改Shader中_BW属性的值
- Mat.SetFloat("_BW",bw);
C#脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using static System.Net.WebRequestMethods;
using UnityEngine.UI;
//在编辑器模式下也可运行此脚本
[ExecuteInEditMode]
public class Postprocessing : MonoBehaviour
{
public Shader PostProcessingShader;
//在脚本中设置一个变量,用于控制黑白阈值
[Range(0,1)]public float bw;
private Material mat;
public Material Mat
{
get
{
//当mat为空时
if(mat==null)
{
//为指定的Shader新建一个材质球
Material _newmat = new Material(PostProcessingShader);
//材质球隐藏并且不保存
_newmat.hideFlags = HideFlags.HideAndDontSave;
mat = _newmat;
//将新建的材质球返回给Mat
return mat;
}
//mat不为空
else
{
return mat;
}
}
}
//Unity在图像渲染完成后对图像进行修改,需要对OnRenderImage() 进行重写
//OnRenderImage在图像渲染完成之后被调用,因此可以用来添加屏幕的后处理效果。
//输入的图片 RenderTextrure source, 输出的图片是 RenderTexture destination.
//如果要对这个方法进行重写,必须要要加上 Graphic.Blit方法。
//src为原始图像,dest为修改后的图像
private void OnRenderImage(RenderTexture src,RenderTexture dest)
{
Mat.SetFloat("_BW",bw);
//src图像须传入到Mat材质Shader中,然后Shader使用MainTex来获取此图像
Graphics.Blit(src, dest, mat);
}
}
Shader代码:
Shader "Hidden/PostProcessing"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_BW("BW",Range(0,1))=0
}
SubShader
{
// No culling or depth
Cull Off
//深度写入关闭,因为深度写入会导致画面出错
ZWrite Off
//使每一帧的图像都被渲染到屏幕上
ZTest Always
Pass
{
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#include "UnityCG.cginc"
float _BW;
sampler2D _MainTex;
fixed4 frag (v2f_img i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
// just invert the colors 反向颜色值然后输出
// return step(col,0.5); fixed4(step(col.r,0.5),step(col.g,0.5),step(col.b,0.5),step(col.a,0.5))
// step(a,b) 当a<=b时,return 1; else return 0;
//在脚本代码中修改_BW值
return step(col.r,_BW);
}
ENDCG
}
}
}
【太妃糖耶】新作上线,快来看看!