什么是屏幕后处理
其原理原于建立一个屏幕后处理脚本系统,即在在场景渲染完屏幕后,再对这个图像进行处理。想在实现这样的处理,我们需要用到OnRenderImage函数。
MonoBehaviour.OnRenderImage(RenderTexture scr,RenderTexture dest)
参数一src:会得到当前屏幕渲染的图像纹理。
参数二dest:得到参数一处理后的纹理,再显示到屏幕上。
通用使用OnRenderImage中的Graphics.Blit函数来完成。
有3个重载:
public Static void Blit(Texture src,RenderTexture dest);
public Static void Blit(Texture src,RenderTexture dest,Material mat,int pass = -1):
public Static void Blit(Texture src,Material mat,int pass = -1):
src对应了源纹理,通常就是屏幕上的纹理或是上一步处理后的纹理,dest是目标纹理如果是null就是直接显示在屏幕上。参数mat是我们使用的材质,这个材质将会使用Unity Shader进行各种幕后处理。而src装会被传给Shader中名为_MainTex的纹理属性。pass默认-1表示全部pass都会执行,否则只会执行索引下的pass。
具体做法
通常情况下我们需要在摄像机中添加一个屏幕后处理脚本。
下面是屏幕后处理脚本的基本结构:
using UnityEngine;
using System.Collections;
[ExecuteInEditMode]
[RequireComponent (typeof(Camera))]
public class PostEffectsBase : MonoBehaviour {
//检查各种资源是否满足,我们调用
protected void CheckResources()
{
bool isSupported = CheckSupport();
if (isSupported == false) {
NotSupported();
}
}
//检查是否支持
protected bool CheckSupport() {
if (SystemInfo.supportsImageEffects == false || SystemInfo.supportsRenderTextures == false) {
Debug.LogWarning("This platform does not support image effects or render textures.");
return false;
}
return true;
}
//不支持的处理
protected void NotSupported() {
enabled = false;
}
protected void Start() {
CheckResources();
}
/// <summary>
/// 后期处理
/// </summary>
/// <param name="shader">该特效使用的Shader</param>
/// <param name="material">用于处理的材质</param>
/// <returns></returns>
protected Material CheckShaderAndCreateMaterial(Shader shader, Material material) {
if (shader == null) {
return null;
}
if (shader.isSupported && material && material.shader == shader)
return material;
if (!shader.isSupported) {
return null;
}
else {
material = new Material(shader);
material.hideFlags = HideFlags.DontSave;
if (material)
return material;
else
return null;
}
}
}
调整亮度、饱和度和对比度
下面是一个具体例子,用于说明功能的用处,是摄像机上的脚本,继承于上面的基本的脚本:
using UnityEngine;
using System.Collections;
public class BrightnessSaturationAndContrast : PostEffectsBase {
public Shader briSatConShader;
private Material briSatConMaterial;
public Material material {
get {
briSatConMaterial = CheckShaderAndCreateMaterial(briSatConShader, briSatConMaterial);
return briSatConMaterial;
}
}
[Range(0.0f, 3.0f)]
public float brightness = 1.0f;
[Range(0.0f, 3.0f)]
public float saturation = 1.0f;
[Range(0.0f, 3.0f)]
public float contrast = 1.0f;
void OnRenderImage(RenderTexture src, RenderTexture dest) {
if (material != null) {
material.SetFloat("_Brightness", brightness);
material.SetFloat("_Saturation", saturation);
material.SetFloat("_Contrast", contrast);
Graphics.Blit(src, dest, material);
} else {
Graphics.Blit(src, dest);
}
}
}
下面是用于做调整的Shader:
Shader "Custom/TestShader29" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_Brightness ("Brightness", Float) = 1
_Saturation("Saturation", Float) = 1
_Contrast("Contrast", Float) = 1
}
SubShader {
Pass {
//关闭深度写入
ZTest Always Cull Off ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
half _Brightness;
half _Saturation;
half _Contrast;
struct v2f {
float4 pos : SV_POSITION;
half2 uv: TEXCOORD0;
};
v2f vert(appdata_img v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed4 renderTex = tex2D(_MainTex, i.uv);
// Apply brightness
fixed3 finalColor = renderTex.rgb * _Brightness;
// Apply saturation
fixed luminance = 0.2125 * renderTex.r + 0.7154 * renderTex.g + 0.0721 * renderTex.b;
fixed3 luminanceColor = fixed3(luminance, luminance, luminance);
finalColor = lerp(luminanceColor, finalColor, _Saturation);
// Apply contrast
fixed3 avgColor = fixed3(0.5, 0.5, 0.5);
finalColor = lerp(avgColor, finalColor, _Contrast);
return fixed4(finalColor, renderTex.a);
}
ENDCG
}
}
Fallback Off
}
最后把这个Shader拖到脚本上就大功告成了。