Unity 屏幕后处理 总述

文章目录

  • 后处理
    • 流程
      • 脚本
        • 1)可行性检查
        • 2)使用 OnRenderImage函数 来获取当前屏幕的渲染纹理
        • 3)通过调用 Graphics.Blit 执行后处理Shader
      • 后处理Shader
  • 简单实例:调整屏幕亮度、饱和度、对比对
    • 摄像头脚本
    • Shader





后处理


流程

1、向摄像机添加后处理脚本,这个脚本指向一个材质,材质所携带的shader就是后处理shader
(很好,你猜对了。后处理脚本就是给摄像机的)(带上材质是为了材质能够携带贴图,shader本身不带)
2、shader编写


脚本

1)可行性检查

每次后处理之前都要检查一系列条件是否被满足,例如当前平台是否支持使用渲染纹理和屏幕特效,是否支持当前的shader等(就像是subshader一样,要考虑平台兼容性的)

进行检查的各种函数已经编写好,如下所示(实际中将下列代码导入Assets中)
在进行后处理脚本的编写时,可行性检查部分调用该基类即可
(这个脚本暂时没有看太懂,以后多仔细看,多记笔记)

start 函数用来提前做一些检查,当一些效果需要更多的检查和配置的时候,可以重载(同名函数)start、CheckSupport、CheckResources函数等

using UnityEngine;
using System.Collections;

[ExecuteInEditMode] //编辑器状态下也可以执行脚本来查看效果
[RequireComponent (typeof(Camera))] //需要相机组件

//继承自基类 MonoBehaviour
public class PostEffectsBase : MonoBehaviour {	
	//函数
	// CheckResources函数用来检查各种资源和条件是否满足(为了提前检查,放在Start函数中)
	protected void CheckResources() {
		bool isSupported = CheckSupport(); //这个函数调用在下面
		
		if (isSupported == false) {
			NotSupported(); //也在下面
		}
	}
	
	//检查是否支持屏幕特效(image effects)和渲染纹理  
	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;
	}
	
	// Called when start,main entrance
	protected void Start() {
		CheckResources();
	}

	//后处理不能光一个shade,r一定要有一个材质
	//最终效果为:没shader和shader不支持的,返回null
	//shader支持的,有材质的返回材质,没有的创建材质返回材质
	protected Material CheckShader_CreateMat(Shader shader, Material material) //接收参数 需要使用的shader,附带的材质
	{
		if (shader == null) //先检查有没有shader
		{
			return null;
		}
		
		if (shader.isSupported && material && material.shader == shader) //若支持该shader&&材质存在&&材质附带的shader为参数中的shader(就是摄像机面板里的shader)(有时候材质附带的shader带错了)
			return material;
		
		if (!shader.isSupported) {
			return null;
		}
		
		else  //即 shader支持,材质不存在/材质携带错误
		//虽然看不懂这里的语句,但是我知道这里的作用:创建临时材质并返回携带shader的材质
		{
			material = new Material(shader); //不懂
			material.hideFlags = HideFlags.DontSave; //不懂 DontSave很明显就是临时
			if (material)
				return material;
			else 
				return null;
		}
	}
}



2)使用 OnRenderImage函数 来获取当前屏幕的渲染纹理

Unity 提供的后处理接口为 OnRenderImage函数,其声明如下:

MonoBehaviour.OnRenderImage(RenderTexture src, RenderTextre dest)

//参数1表示源渲染纹理source, 参数2表示目标渲染纹理destination
//也就是这个函数接受src纹理,进行后处理后,输出为dest纹理
  • OnRenderImage函数 会在所有对象的shader执行完毕后再进行(毕竟是处理)
  • 但是若需要在中途执行屏幕后处理(如透明pass之前),可在OnRenderImage函数前添加 ImageEffectQpaque 属性。
  • OnRenderImage内部r若存在循环,循环内部创建的渲染纹理在循环外部是访问不到的


3)通过调用 Graphics.Blit 执行后处理Shader

OnRenderImage函数 中我们会使用 Graphics.Blit函数 来完成其对src纹理的处理,其声明为如下之一

注意这里直接创建了两张纹理,并不需要另行创建
注意该函数声明即执行

public static void Blit(RenderTexture src, RenderTextre dest); //缺失材质,会直接传递,不会执行pass
public static void Blit(RenderTexture src, RenderTextre dest, Material mat, int pass = -1); //pass值默认-1吧?
public static void Blit(RenderTexture src,  Material mat, int pass = -1);

//参数Material mat 为材质,并不是所有的后处理都需要材质
//src会被传递给_MainTex,也就是shader中的MainTex就是渲染纹理
//int pass = -1 表示遍历后处理shader的所有Pass,若为其他索引值,则仅执行一个Pass(应该是调试用的)

当后处理比较复杂多样时,可能需要调用多次Graphics.Blit



后处理Shader

需要注意后处理的shader并不需要appdata数据结构
但顶点着色器还是需要数据传入,并且后处理并不需要处理顶点,所以后处理的shader顶点着色器大部分都如下:

v2f vert(appdata_img v) {
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);	
				o.uv = v.texcoord;
				return o;
			}

如果需要进行卷积,那还需要进一步:

 v2f vert (appdata_img v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                half2 uv = v.texcoord;

                o.uv[0] = uv + _MainTex_TexelSize.xy * half2(-1,-1);
                o.uv[1] = uv + _MainTex_TexelSize.xy * half2( 0,-1);
                o.uv[2] = uv + _MainTex_TexelSize.xy * half2( 1,-1);
                o.uv[3] = uv + _MainTex_TexelSize.xy * half2(-1, 0);
                o.uv[4] = uv + _MainTex_TexelSize.xy * half2( 0, 0);
                o.uv[5] = uv + _MainTex_TexelSize.xy * half2( 1, 0);
                o.uv[6] = uv + _MainTex_TexelSize.xy * half2(-1, 1);
                o.uv[7] = uv + _MainTex_TexelSize.xy * half2( 0, 1);
                o.uv[8] = uv + _MainTex_TexelSize.xy * half2( 1, 1);

                return o;
            }





简单实例:调整屏幕亮度、饱和度、对比对



摄像头脚本

using UnityEngine;
using System.Collections;

//这里冒号前的名称应与脚本名保持一致
//继承了上述检测脚本
public class B_S_C : PostEffectsBase {
	
	//shader和Mat的声明
	public Shader B_S_C_Shader;
	private Material B_S_C_Mat;
	
	public Material material {  
		get {
			//这里用到的函数在 PostEffectsBase 里,进行可行性检验
			B_S_C_Mat = CheckShader_CreateMat(B_S_C_Shader, B_S_C_Mat);
			return B_S_C_Mat;
		}  
	}

	//这里的部分会显示到脚本面板中
	[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) {
			//虽然这里是Mat的函数,但是其会直接作用到shader中的变量,与mat面板上的参数无关
			//所以其实在后处理shader中我们并不需要写properties,但Tex要写
			material.SetFloat("_Brightness", brightness);
			material.SetFloat("_Saturation", saturation);
			material.SetFloat("_Contrast", contrast);
			
			//这个记得吗?上面是改参,这一句是执行
			Graphics.Blit(src, dest, material);
		} 
		
		//没有材质就不进行处理了,说明前面出错了,我觉得debug一下会更好?
		else {
			Graphics.Blit(src, dest);
		}
	}
}



Shader

Shader "Unity Shaders Book/Chapter 12/Brightness Saturation And Contrast" {
	Properties 
	{
		_MainTex ("Base (RGB)", 2D) = "white" {} //MainTex不能省略
		//_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);  
				  
				// 明度
				fixed3 finalColor = renderTex.rgb * _Brightness;
				
				// 饱和度,注意饱和度是怎么算的
				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);
				
				// 对比度
				fixed3 avgColor = fixed3(0.5, 0.5, 0.5);
				finalColor = lerp(avgColor, finalColor, _Contrast);
				
				return fixed4(finalColor, renderTex.a);  
			}  
			  
			ENDCG
		}  
	}
	
	Fallback Off
}


你可能感兴趣的:(Image,Effect后处理,unity,游戏引擎)